1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
36 #include <tny-merge-folder.h>
39 #include <modest-header-view.h>
40 #include <modest-header-view-priv.h>
41 #include <modest-dnd.h>
42 #include <modest-tny-folder.h>
43 #include <modest-debug.h>
44 #include <modest-ui-actions.h>
45 #include <modest-marshal.h>
46 #include <modest-text-utils.h>
47 #include <modest-icon-names.h>
48 #include <modest-runtime.h>
49 #include "modest-platform.h"
50 #include <modest-hbox-cell-renderer.h>
51 #include <modest-vbox-cell-renderer.h>
52 #include <modest-datetime-formatter.h>
53 #include <modest-ui-constants.h>
55 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
56 static void modest_header_view_init (ModestHeaderView *obj);
57 static void modest_header_view_finalize (GObject *obj);
58 static void modest_header_view_dispose (GObject *obj);
60 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
61 GtkTreeViewColumn *column, gpointer userdata);
63 static gint cmp_rows (GtkTreeModel *tree_model,
68 static gint cmp_subject_rows (GtkTreeModel *tree_model,
73 static gboolean filter_row (GtkTreeModel *model,
77 static void on_account_removed (TnyAccountStore *self,
81 static void on_selection_changed (GtkTreeSelection *sel,
84 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
87 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
90 static void setup_drag_and_drop (GtkWidget *self);
92 static void enable_drag_and_drop (GtkWidget *self);
94 static void disable_drag_and_drop (GtkWidget *self);
96 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
98 #ifndef MODEST_TOOLKIT_HILDON2
99 static gboolean on_focus_in (GtkWidget *sef,
100 GdkEventFocus *event,
103 static gboolean on_focus_out (GtkWidget *self,
104 GdkEventFocus *event,
108 static void folder_monitor_update (TnyFolderObserver *self,
109 TnyFolderChange *change);
111 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
113 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
115 static void _clear_hidding_filter (ModestHeaderView *header_view);
117 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
119 const gchar *tny_folder_id);
121 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
122 GdkEventExpose *event,
125 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
126 static void update_style (ModestHeaderView *self);
129 HEADER_VIEW_NON_EMPTY,
134 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
135 struct _ModestHeaderViewPrivate {
137 ModestHeaderViewStyle style;
140 TnyFolderMonitor *monitor;
141 GMutex *observers_lock;
143 /*header-view-observer observer*/
144 GMutex *observer_list_lock;
145 GSList *observer_list;
147 /* not unref this object, its a singlenton */
148 ModestEmailClipboard *clipboard;
150 /* Filter tree model */
153 GtkTreeRowReference *autoselect_reference;
154 ModestHeaderViewFilter filter;
156 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
157 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
159 gulong selection_changed_handler;
160 gulong acc_removed_handler;
162 GList *drag_begin_cached_selected_rows;
164 HeaderViewStatus status;
165 guint status_timeout;
166 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
168 ModestDatetimeFormatter *datetime_formatter;
170 GtkCellRenderer *renderer_subject;
171 GtkCellRenderer *renderer_address;
172 GtkCellRenderer *renderer_date_status;
174 GdkColor active_color;
175 GdkColor secondary_color;
177 gchar *filter_string;
178 gchar **filter_string_splitted;
181 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
182 struct _HeadersCountChangedHelper {
183 ModestHeaderView *self;
184 TnyFolderChange *change;
188 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
189 MODEST_TYPE_HEADER_VIEW, \
190 ModestHeaderViewPrivate))
194 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
197 HEADER_SELECTED_SIGNAL,
198 HEADER_ACTIVATED_SIGNAL,
199 ITEM_NOT_FOUND_SIGNAL,
200 MSG_COUNT_CHANGED_SIGNAL,
201 UPDATING_MSG_LIST_SIGNAL,
206 static GObjectClass *parent_class = NULL;
208 /* uncomment the following if you have defined any signals */
209 static guint signals[LAST_SIGNAL] = {0};
212 modest_header_view_get_type (void)
214 static GType my_type = 0;
216 static const GTypeInfo my_info = {
217 sizeof(ModestHeaderViewClass),
218 NULL, /* base init */
219 NULL, /* base finalize */
220 (GClassInitFunc) modest_header_view_class_init,
221 NULL, /* class finalize */
222 NULL, /* class data */
223 sizeof(ModestHeaderView),
225 (GInstanceInitFunc) modest_header_view_init,
229 static const GInterfaceInfo tny_folder_observer_info =
231 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
232 NULL, /* interface_finalize */
233 NULL /* interface_data */
235 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
239 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
240 &tny_folder_observer_info);
248 modest_header_view_class_init (ModestHeaderViewClass *klass)
250 GObjectClass *gobject_class;
251 gobject_class = (GObjectClass*) klass;
253 parent_class = g_type_class_peek_parent (klass);
254 gobject_class->finalize = modest_header_view_finalize;
255 gobject_class->dispose = modest_header_view_dispose;
257 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
259 signals[HEADER_SELECTED_SIGNAL] =
260 g_signal_new ("header_selected",
261 G_TYPE_FROM_CLASS (gobject_class),
263 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
265 g_cclosure_marshal_VOID__POINTER,
266 G_TYPE_NONE, 1, G_TYPE_POINTER);
268 signals[HEADER_ACTIVATED_SIGNAL] =
269 g_signal_new ("header_activated",
270 G_TYPE_FROM_CLASS (gobject_class),
272 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
274 gtk_marshal_VOID__POINTER_POINTER,
275 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
278 signals[ITEM_NOT_FOUND_SIGNAL] =
279 g_signal_new ("item_not_found",
280 G_TYPE_FROM_CLASS (gobject_class),
282 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
284 g_cclosure_marshal_VOID__INT,
285 G_TYPE_NONE, 1, G_TYPE_INT);
287 signals[MSG_COUNT_CHANGED_SIGNAL] =
288 g_signal_new ("msg_count_changed",
289 G_TYPE_FROM_CLASS (gobject_class),
291 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
293 modest_marshal_VOID__POINTER_POINTER,
294 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
296 signals[UPDATING_MSG_LIST_SIGNAL] =
297 g_signal_new ("updating-msg-list",
298 G_TYPE_FROM_CLASS (gobject_class),
300 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
302 g_cclosure_marshal_VOID__BOOLEAN,
303 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
305 #ifdef MODEST_TOOLKIT_HILDON2
306 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
312 tny_folder_observer_init (TnyFolderObserverIface *klass)
314 klass->update = folder_monitor_update;
317 static GtkTreeViewColumn*
318 get_new_column (const gchar *name, GtkCellRenderer *renderer,
319 gboolean resizable, gint sort_col_id, gboolean show_as_text,
320 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
322 GtkTreeViewColumn *column;
324 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
325 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
327 gtk_tree_view_column_set_resizable (column, resizable);
329 gtk_tree_view_column_set_expand (column, TRUE);
332 gtk_tree_view_column_add_attribute (column, renderer, "text",
334 if (sort_col_id >= 0)
335 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
337 gtk_tree_view_column_set_sort_indicator (column, FALSE);
338 gtk_tree_view_column_set_reorderable (column, TRUE);
341 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
348 remove_all_columns (ModestHeaderView *obj)
350 GList *columns, *cursor;
352 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
354 for (cursor = columns; cursor; cursor = cursor->next)
355 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
356 GTK_TREE_VIEW_COLUMN(cursor->data));
357 g_list_free (columns);
361 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
363 GtkTreeModel *sortable;
364 GtkTreeViewColumn *column=NULL;
365 GtkTreeSelection *selection = NULL;
366 GtkCellRenderer *renderer_header,
367 *renderer_attach, *renderer_compact_date_or_status;
368 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
369 *renderer_subject_box, *renderer_recpt,
371 ModestHeaderViewPrivate *priv;
372 GtkTreeViewColumn *compact_column = NULL;
375 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
376 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
378 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
380 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
382 /* TODO: check whether these renderers need to be freed */
383 renderer_attach = gtk_cell_renderer_pixbuf_new ();
384 renderer_priority = gtk_cell_renderer_pixbuf_new ();
385 renderer_header = gtk_cell_renderer_text_new ();
387 renderer_compact_header = modest_vbox_cell_renderer_new ();
388 renderer_recpt_box = modest_hbox_cell_renderer_new ();
389 renderer_subject_box = modest_hbox_cell_renderer_new ();
390 renderer_recpt = gtk_cell_renderer_text_new ();
391 priv->renderer_address = renderer_recpt;
392 priv->renderer_subject = gtk_cell_renderer_text_new ();
393 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
394 priv->renderer_date_status = renderer_compact_date_or_status;
396 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
397 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
398 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
399 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
400 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
401 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
402 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
403 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
404 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
405 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
406 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
407 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
408 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
409 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
411 #ifdef MODEST_TOOLKIT_HILDON2
412 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
414 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
415 #ifndef MODEST_TOOLKIT_GTK
416 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
417 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
419 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
420 g_object_set(G_OBJECT(renderer_header),
421 "ellipsize", PANGO_ELLIPSIZE_END,
423 g_object_set (G_OBJECT (priv->renderer_subject),
424 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
426 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
427 g_object_set (G_OBJECT (renderer_recpt),
428 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
430 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
431 g_object_set(G_OBJECT(renderer_compact_date_or_status),
432 "xalign", 1.0, "yalign", 0.1,
434 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
435 #ifdef MODEST_TOOLKIT_HILDON2
436 g_object_set (G_OBJECT (renderer_priority),
438 "xalign", 0.0, NULL);
439 g_object_set (G_OBJECT (renderer_attach),
441 "xalign", 0.0, NULL);
443 g_object_set (G_OBJECT (renderer_priority),
444 "yalign", 0.5, NULL);
445 g_object_set (G_OBJECT (renderer_attach),
446 "yalign", 0.0, NULL);
449 #ifdef MODEST_TOOLKIT_HILDON1
450 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
451 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
452 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
453 #elif MODEST_TOOLKIT_HILDON2
454 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
455 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
456 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
458 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
459 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
460 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
463 remove_all_columns (self);
465 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
466 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
467 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
469 /* Add new columns */
470 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
471 ModestHeaderViewColumn col =
472 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
474 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
475 g_printerr ("modest: invalid column %d in column list\n", col);
481 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
482 column = get_new_column (_("A"), renderer_attach, FALSE,
483 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
485 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
487 gtk_tree_view_column_set_fixed_width (column, 45);
491 case MODEST_HEADER_VIEW_COLUMN_FROM:
492 column = get_new_column (_("From"), renderer_header, TRUE,
493 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
495 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
496 GINT_TO_POINTER(TRUE));
499 case MODEST_HEADER_VIEW_COLUMN_TO:
500 column = get_new_column (_("To"), renderer_header, TRUE,
501 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
503 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
504 GINT_TO_POINTER(FALSE));
507 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
508 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
509 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
511 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
512 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
513 compact_column = column;
516 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
517 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
518 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
520 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
521 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
522 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
523 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
524 compact_column = column;
528 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
529 column = get_new_column (_("Subject"), renderer_header, TRUE,
530 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
532 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
536 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
537 column = get_new_column (_("Received"), renderer_header, TRUE,
538 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
540 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
541 GINT_TO_POINTER(TRUE));
544 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
545 column = get_new_column (_("Sent"), renderer_header, TRUE,
546 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
548 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
549 GINT_TO_POINTER(FALSE));
552 case MODEST_HEADER_VIEW_COLUMN_SIZE:
553 column = get_new_column (_("Size"), renderer_header, TRUE,
554 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
556 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
559 case MODEST_HEADER_VIEW_COLUMN_STATUS:
560 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
561 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
563 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
568 g_return_val_if_reached(FALSE);
571 /* we keep the column id around */
572 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
573 GINT_TO_POINTER(col));
575 /* we need this ptr when sorting the rows */
576 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
578 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
582 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
583 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
584 (GtkTreeIterCompareFunc) cmp_rows,
585 compact_column, NULL);
586 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
587 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
588 (GtkTreeIterCompareFunc) cmp_subject_rows,
589 compact_column, NULL);
593 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
599 datetime_format_changed (ModestDatetimeFormatter *formatter,
600 ModestHeaderView *self)
602 gtk_widget_queue_draw (GTK_WIDGET (self));
606 modest_header_view_init (ModestHeaderView *obj)
608 ModestHeaderViewPrivate *priv;
611 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
614 priv->is_outbox = FALSE;
616 priv->monitor = NULL;
617 priv->observers_lock = g_mutex_new ();
618 priv->autoselect_reference = NULL;
620 priv->status = HEADER_VIEW_INIT;
621 priv->status_timeout = 0;
622 priv->notify_status = TRUE;
624 priv->observer_list_lock = g_mutex_new();
625 priv->observer_list = NULL;
627 priv->clipboard = modest_runtime_get_email_clipboard ();
628 priv->hidding_ids = NULL;
629 priv->n_selected = 0;
630 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
631 priv->filter_string = NULL;
632 priv->filter_string_splitted = NULL;
633 priv->selection_changed_handler = 0;
634 priv->acc_removed_handler = 0;
636 /* Sort parameters */
637 for (j=0; j < 2; j++) {
638 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
639 priv->sort_colid[j][i] = -1;
640 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
644 priv->datetime_formatter = modest_datetime_formatter_new ();
645 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
646 G_CALLBACK (datetime_format_changed), (gpointer) obj);
648 setup_drag_and_drop (GTK_WIDGET(obj));
652 modest_header_view_dispose (GObject *obj)
654 ModestHeaderView *self;
655 ModestHeaderViewPrivate *priv;
656 GtkTreeSelection *sel;
658 self = MODEST_HEADER_VIEW(obj);
659 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
661 if (priv->datetime_formatter) {
662 g_object_unref (priv->datetime_formatter);
663 priv->datetime_formatter = NULL;
666 /* Free in the dispose to avoid unref cycles */
668 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
669 g_object_unref (G_OBJECT (priv->folder));
673 /* We need to do this here in the dispose because the
674 selection won't exist when finalizing */
675 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
676 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
677 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
678 priv->selection_changed_handler = 0;
681 G_OBJECT_CLASS(parent_class)->dispose (obj);
685 modest_header_view_finalize (GObject *obj)
687 ModestHeaderView *self;
688 ModestHeaderViewPrivate *priv;
690 self = MODEST_HEADER_VIEW(obj);
691 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
693 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
694 priv->acc_removed_handler)) {
695 g_signal_handler_disconnect (modest_runtime_get_account_store (),
696 priv->acc_removed_handler);
699 /* There is no need to lock because there should not be any
700 * reference to self now. */
701 g_mutex_free(priv->observer_list_lock);
702 g_slist_free(priv->observer_list);
704 g_mutex_lock (priv->observers_lock);
706 tny_folder_monitor_stop (priv->monitor);
707 g_object_unref (G_OBJECT (priv->monitor));
709 g_mutex_unlock (priv->observers_lock);
710 g_mutex_free (priv->observers_lock);
712 /* Clear hidding array created by cut operation */
713 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
715 if (priv->autoselect_reference != NULL) {
716 gtk_tree_row_reference_free (priv->autoselect_reference);
717 priv->autoselect_reference = NULL;
720 if (priv->filter_string) {
721 g_free (priv->filter_string);
724 if (priv->filter_string_splitted) {
725 g_strfreev (priv->filter_string_splitted);
728 G_OBJECT_CLASS(parent_class)->finalize (obj);
733 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
736 GtkTreeSelection *sel;
737 ModestHeaderView *self;
738 ModestHeaderViewPrivate *priv;
740 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
743 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
744 self = MODEST_HEADER_VIEW(obj);
745 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
747 modest_header_view_set_style (self, style);
749 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
750 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
751 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
753 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
754 TRUE); /* alternating row colors */
756 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
757 priv->selection_changed_handler =
758 g_signal_connect_after (sel, "changed",
759 G_CALLBACK(on_selection_changed), self);
761 g_signal_connect (self, "row-activated",
762 G_CALLBACK (on_header_row_activated), NULL);
764 #ifndef MODEST_TOOLKIT_HILDON2
765 g_signal_connect (self, "focus-in-event",
766 G_CALLBACK(on_focus_in), NULL);
767 g_signal_connect (self, "focus-out-event",
768 G_CALLBACK(on_focus_out), NULL);
771 g_signal_connect (self, "button-press-event",
772 G_CALLBACK(on_button_press_event), NULL);
773 g_signal_connect (self, "button-release-event",
774 G_CALLBACK(on_button_release_event), NULL);
776 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
778 G_CALLBACK (on_account_removed),
781 g_signal_connect (self, "expose-event",
782 G_CALLBACK(modest_header_view_on_expose_event),
785 return GTK_WIDGET(self);
790 modest_header_view_count_selected_headers (ModestHeaderView *self)
792 GtkTreeSelection *sel;
795 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
797 /* Get selection object and check selected rows count */
798 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
799 selected_rows = gtk_tree_selection_count_selected_rows (sel);
801 return selected_rows;
805 modest_header_view_has_selected_headers (ModestHeaderView *self)
807 GtkTreeSelection *sel;
810 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
812 /* Get selection object and check selected rows count */
813 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
814 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
821 modest_header_view_get_selected_headers (ModestHeaderView *self)
823 GtkTreeSelection *sel;
824 TnyList *header_list = NULL;
826 GList *list, *tmp = NULL;
827 GtkTreeModel *tree_model = NULL;
830 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
833 /* Get selected rows */
834 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
835 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
838 header_list = tny_simple_list_new();
840 list = g_list_reverse (list);
843 /* get header from selection */
844 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
845 gtk_tree_model_get (tree_model, &iter,
846 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
848 /* Prepend to list */
849 tny_list_prepend (header_list, G_OBJECT (header));
850 g_object_unref (G_OBJECT (header));
852 tmp = g_list_next (tmp);
855 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
862 /* scroll our list view so the selected item is visible */
864 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
866 #ifdef MODEST_TOOLKIT_GTK
868 GtkTreePath *selected_path;
869 GtkTreePath *start, *end;
873 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
874 selected_path = gtk_tree_model_get_path (model, iter);
876 start = gtk_tree_path_new ();
877 end = gtk_tree_path_new ();
879 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
881 if (gtk_tree_path_compare (selected_path, start) < 0 ||
882 gtk_tree_path_compare (end, selected_path) < 0)
883 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
884 selected_path, NULL, TRUE,
887 gtk_tree_path_free (selected_path);
888 gtk_tree_path_free (start);
889 gtk_tree_path_free (end);
891 #endif /* MODEST_TOOLKIT_GTK */
896 modest_header_view_select_next (ModestHeaderView *self)
898 GtkTreeSelection *sel;
903 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
905 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
906 path = get_selected_row (GTK_TREE_VIEW(self), &model);
907 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
908 /* Unselect previous path */
909 gtk_tree_selection_unselect_path (sel, path);
911 /* Move path down and selects new one */
912 if (gtk_tree_model_iter_next (model, &iter)) {
913 gtk_tree_selection_select_iter (sel, &iter);
914 scroll_to_selected (self, &iter, FALSE);
916 gtk_tree_path_free(path);
922 modest_header_view_select_prev (ModestHeaderView *self)
924 GtkTreeSelection *sel;
929 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
931 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
932 path = get_selected_row (GTK_TREE_VIEW(self), &model);
933 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
934 /* Unselect previous path */
935 gtk_tree_selection_unselect_path (sel, path);
938 if (gtk_tree_path_prev (path)) {
939 gtk_tree_model_get_iter (model, &iter, path);
941 /* Select the new one */
942 gtk_tree_selection_select_iter (sel, &iter);
943 scroll_to_selected (self, &iter, TRUE);
946 gtk_tree_path_free (path);
951 modest_header_view_get_columns (ModestHeaderView *self)
953 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
955 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
961 modest_header_view_set_style (ModestHeaderView *self,
962 ModestHeaderViewStyle style)
964 ModestHeaderViewPrivate *priv;
965 gboolean show_col_headers = FALSE;
966 ModestHeaderViewStyle old_style;
968 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
969 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
972 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
973 if (priv->style == style)
974 return TRUE; /* nothing to do */
977 case MODEST_HEADER_VIEW_STYLE_DETAILS:
978 show_col_headers = TRUE;
980 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
983 g_return_val_if_reached (FALSE);
985 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
986 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
988 old_style = priv->style;
995 ModestHeaderViewStyle
996 modest_header_view_get_style (ModestHeaderView *self)
998 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1000 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
1003 /* This is used to automatically select the first header if the user
1004 * has not selected any header yet.
1007 modest_header_view_on_expose_event(GtkTreeView *header_view,
1008 GdkEventExpose *event,
1011 GtkTreeSelection *sel;
1012 GtkTreeModel *model;
1013 GtkTreeIter tree_iter;
1014 ModestHeaderViewPrivate *priv;
1016 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1017 model = gtk_tree_view_get_model(header_view);
1022 #ifdef MODEST_TOOLKIT_HILDON2
1025 sel = gtk_tree_view_get_selection(header_view);
1026 if(!gtk_tree_selection_count_selected_rows(sel)) {
1027 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1028 GtkTreePath *tree_iter_path;
1029 /* Prevent the widget from getting the focus
1030 when selecting the first item */
1031 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1032 g_object_set(header_view, "can-focus", FALSE, NULL);
1033 gtk_tree_selection_select_iter(sel, &tree_iter);
1034 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1035 g_object_set(header_view, "can-focus", TRUE, NULL);
1036 if (priv->autoselect_reference) {
1037 gtk_tree_row_reference_free (priv->autoselect_reference);
1039 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1040 gtk_tree_path_free (tree_iter_path);
1043 if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
1044 gboolean moved_selection = FALSE;
1045 GtkTreePath * last_path;
1046 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1047 moved_selection = TRUE;
1051 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1052 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1053 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1054 moved_selection = TRUE;
1055 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1057 gtk_tree_path_free (last_path);
1059 if (moved_selection) {
1060 gtk_tree_row_reference_free (priv->autoselect_reference);
1061 priv->autoselect_reference = NULL;
1064 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1065 GtkTreePath *current_path;
1066 current_path = gtk_tree_model_get_path (model, &tree_iter);
1067 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1068 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1069 g_object_set(header_view, "can-focus", FALSE, NULL);
1070 gtk_tree_selection_unselect_all (sel);
1071 gtk_tree_selection_select_iter(sel, &tree_iter);
1072 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1073 g_object_set(header_view, "can-focus", TRUE, NULL);
1074 gtk_tree_row_reference_free (priv->autoselect_reference);
1075 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1077 gtk_tree_path_free (current_path);
1078 gtk_tree_path_free (last_path);
1088 modest_header_view_get_folder (ModestHeaderView *self)
1090 ModestHeaderViewPrivate *priv;
1092 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1094 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1097 g_object_ref (priv->folder);
1099 return priv->folder;
1103 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1109 ModestHeaderView *self;
1110 ModestHeaderViewPrivate *priv;
1112 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1114 self = MODEST_HEADER_VIEW (user_data);
1115 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1117 if (cancelled || err)
1120 /* Add IDLE observer (monitor) and another folder observer for
1121 new messages (self) */
1122 g_mutex_lock (priv->observers_lock);
1123 if (priv->monitor) {
1124 tny_folder_monitor_stop (priv->monitor);
1125 g_object_unref (G_OBJECT (priv->monitor));
1127 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1128 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1129 tny_folder_monitor_start (priv->monitor);
1130 g_mutex_unlock (priv->observers_lock);
1134 modest_header_view_set_folder_intern (ModestHeaderView *self,
1140 ModestHeaderViewPrivate *priv;
1141 GList *cols, *cursor;
1142 GtkTreeModel *filter_model, *sortable;
1144 GtkSortType sort_type;
1146 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1148 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1150 /* Start the monitor in the callback of the
1151 tny_gtk_header_list_model_set_folder call. It's crucial to
1152 do it there and not just after the call because we want the
1153 monitor to observe only the headers returned by the
1154 tny_folder_get_headers_async call that it's inside the
1155 tny_gtk_header_list_model_set_folder call. This way the
1156 monitor infrastructure could successfully cope with
1157 duplicates. For example if a tny_folder_add_msg_async is
1158 happening while tny_gtk_header_list_model_set_folder is
1159 invoked, then the first call could add a header that will
1160 be added again by tny_gtk_header_list_model_set_folder, so
1161 we'd end up with duplicate headers. sergio */
1162 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1164 set_folder_intern_get_headers_async_cb,
1167 /* Create a tree model filter to hide and show rows for cut operations */
1168 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1169 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1170 filter_row, self, NULL);
1171 g_object_unref (headers);
1173 /* Init filter_row function to examine empty status */
1174 priv->status = HEADER_VIEW_INIT;
1176 /* Create sortable model */
1177 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1178 g_object_unref (filter_model);
1180 /* install our special sorting functions */
1181 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1183 /* Restore sort column id */
1185 type = modest_tny_folder_guess_folder_type (folder);
1186 if (type == TNY_FOLDER_TYPE_INVALID)
1187 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1189 sort_colid = modest_header_view_get_sort_column_id (self, type);
1190 sort_type = modest_header_view_get_sort_type (self, type);
1191 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1194 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1195 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1196 (GtkTreeIterCompareFunc) cmp_rows,
1198 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1199 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1200 (GtkTreeIterCompareFunc) cmp_subject_rows,
1205 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1206 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1207 g_object_unref (sortable);
1214 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1216 GtkSortType sort_type)
1218 ModestHeaderViewPrivate *priv = NULL;
1219 GtkTreeModel *sortable = NULL;
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);
1225 /* Get model and private data */
1226 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1227 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1229 /* Sort tree model */
1230 type = modest_tny_folder_guess_folder_type (priv->folder);
1231 if (type == TNY_FOLDER_TYPE_INVALID)
1232 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1234 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1237 /* Store new sort parameters */
1238 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1243 modest_header_view_set_sort_params (ModestHeaderView *self,
1245 GtkSortType sort_type,
1248 ModestHeaderViewPrivate *priv;
1249 ModestHeaderViewStyle style;
1251 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1252 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1253 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1255 style = modest_header_view_get_style (self);
1256 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1258 priv->sort_colid[style][type] = sort_colid;
1259 priv->sort_type[style][type] = sort_type;
1263 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1266 ModestHeaderViewPrivate *priv;
1267 ModestHeaderViewStyle style;
1269 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1270 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1272 style = modest_header_view_get_style (self);
1273 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1275 return priv->sort_colid[style][type];
1279 modest_header_view_get_sort_type (ModestHeaderView *self,
1282 ModestHeaderViewPrivate *priv;
1283 ModestHeaderViewStyle style;
1285 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1286 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1288 style = modest_header_view_get_style (self);
1289 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1291 return priv->sort_type[style][type];
1295 ModestHeaderView *header_view;
1296 RefreshAsyncUserCallback cb;
1301 folder_refreshed_cb (ModestMailOperation *mail_op,
1305 ModestHeaderViewPrivate *priv;
1306 SetFolderHelper *info;
1308 info = (SetFolderHelper*) user_data;
1310 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1314 info->cb (mail_op, folder, info->user_data);
1316 /* Start the folder count changes observer. We do not need it
1317 before the refresh. Note that the monitor could still be
1318 called for this refresh but now we know that the callback
1319 was previously called */
1320 g_mutex_lock (priv->observers_lock);
1321 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1322 g_mutex_unlock (priv->observers_lock);
1324 /* Notify the observers that the update is over */
1325 g_signal_emit (G_OBJECT (info->header_view),
1326 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1328 /* Allow filtering notifications from now on if the current
1329 folder is still the same (if not then the user has selected
1330 another one to refresh, we should wait until that refresh
1332 if (priv->folder == folder)
1333 priv->notify_status = TRUE;
1336 g_object_unref (info->header_view);
1341 refresh_folder_error_handler (ModestMailOperation *mail_op,
1344 const GError *error = modest_mail_operation_get_error (mail_op);
1346 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1347 error->code == TNY_IO_ERROR_WRITE ||
1348 error->code == TNY_IO_ERROR_READ) {
1349 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1350 /* If the mail op has been cancelled then it's not an error: don't show any message */
1351 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1352 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1353 modest_platform_information_banner (NULL, NULL, msg);
1360 modest_header_view_set_folder (ModestHeaderView *self,
1363 ModestWindow *progress_window,
1364 RefreshAsyncUserCallback callback,
1367 ModestHeaderViewPrivate *priv;
1369 g_return_if_fail (self);
1371 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1374 if (priv->status_timeout) {
1375 g_source_remove (priv->status_timeout);
1376 priv->status_timeout = 0;
1379 g_mutex_lock (priv->observers_lock);
1380 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1381 g_object_unref (priv->folder);
1382 priv->folder = NULL;
1383 g_mutex_unlock (priv->observers_lock);
1387 GtkTreeSelection *selection;
1388 SetFolderHelper *info;
1389 ModestMailOperation *mail_op = NULL;
1391 /* Set folder in the model */
1392 modest_header_view_set_folder_intern (self, folder, refresh);
1394 /* Pick my reference. Nothing to do with the mail operation */
1395 priv->folder = g_object_ref (folder);
1397 /* Do not notify about filterings until the refresh finishes */
1398 priv->notify_status = FALSE;
1400 /* Clear the selection if exists */
1401 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1402 gtk_tree_selection_unselect_all(selection);
1403 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1405 /* Notify the observers that the update begins */
1406 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1409 /* create the helper */
1410 info = g_malloc0 (sizeof (SetFolderHelper));
1411 info->header_view = g_object_ref (self);
1412 info->cb = callback;
1413 info->user_data = user_data;
1415 /* Create the mail operation (source will be the parent widget) */
1416 if (progress_window)
1417 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1418 refresh_folder_error_handler,
1421 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1424 /* Refresh the folder asynchronously */
1425 modest_mail_operation_refresh_folder (mail_op,
1427 folder_refreshed_cb,
1430 folder_refreshed_cb (mail_op, folder, info);
1434 g_object_unref (mail_op);
1436 g_mutex_lock (priv->observers_lock);
1438 if (priv->monitor) {
1439 tny_folder_monitor_stop (priv->monitor);
1440 g_object_unref (G_OBJECT (priv->monitor));
1441 priv->monitor = NULL;
1444 if (priv->autoselect_reference) {
1445 gtk_tree_row_reference_free (priv->autoselect_reference);
1446 priv->autoselect_reference = NULL;
1449 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1451 modest_header_view_notify_observers(self, NULL, NULL);
1453 g_mutex_unlock (priv->observers_lock);
1455 /* Notify the observers that the update is over */
1456 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1462 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1463 GtkTreeViewColumn *column, gpointer userdata)
1465 ModestHeaderView *self = NULL;
1467 GtkTreeModel *model = NULL;
1468 TnyHeader *header = NULL;
1469 TnyHeaderFlags flags;
1471 self = MODEST_HEADER_VIEW (treeview);
1473 model = gtk_tree_view_get_model (treeview);
1474 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1477 /* get the first selected item */
1478 gtk_tree_model_get (model, &iter,
1479 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1480 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1483 /* Dont open DELETED messages */
1484 if (flags & TNY_HEADER_FLAG_DELETED) {
1487 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1488 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1489 modest_platform_information_banner (NULL, NULL, msg);
1495 g_signal_emit (G_OBJECT(self),
1496 signals[HEADER_ACTIVATED_SIGNAL],
1502 g_object_unref (G_OBJECT (header));
1507 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1509 GtkTreeModel *model;
1510 TnyHeader *header = NULL;
1511 GtkTreePath *path = NULL;
1513 ModestHeaderView *self;
1514 GList *selected = NULL;
1516 g_return_if_fail (sel);
1517 g_return_if_fail (user_data);
1519 self = MODEST_HEADER_VIEW (user_data);
1521 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1522 if (selected != NULL)
1523 path = (GtkTreePath *) selected->data;
1524 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1525 return; /* msg was _un_selected */
1527 gtk_tree_model_get (model, &iter,
1528 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1532 g_signal_emit (G_OBJECT(self),
1533 signals[HEADER_SELECTED_SIGNAL],
1536 g_object_unref (G_OBJECT (header));
1538 /* free all items in 'selected' */
1539 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1540 g_list_free (selected);
1544 /* PROTECTED method. It's useful when we want to force a given
1545 selection to reload a msg. For example if we have selected a header
1546 in offline mode, when Modest become online, we want to reload the
1547 message automatically without an user click over the header */
1549 _modest_header_view_change_selection (GtkTreeSelection *selection,
1552 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1553 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1555 on_selection_changed (selection, user_data);
1559 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1566 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1570 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1574 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1582 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1590 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1591 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1595 case TNY_HEADER_FLAG_ATTACHMENTS:
1597 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1598 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1599 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1600 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1602 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1603 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1605 return cmp ? cmp : t1 - t2;
1607 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1608 TnyHeader *header1 = NULL, *header2 = NULL;
1610 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1611 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1612 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1613 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1615 /* This is for making priority values respect the intuitive sort relationship
1616 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1618 if (header1 && header2) {
1619 cmp = compare_priorities (tny_header_get_priority (header1),
1620 tny_header_get_priority (header2));
1621 g_object_unref (header1);
1622 g_object_unref (header2);
1624 return cmp ? cmp : t1 - t2;
1630 return &iter1 - &iter2; /* oughhhh */
1635 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1642 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1644 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1645 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1646 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1647 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1649 /* Do not use the prefixes for sorting. Consume all the blank
1650 spaces for sorting */
1651 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1652 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1655 /* If they're equal based on subject without prefix then just
1656 sort them by length. This will show messages like this.
1663 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1670 /* Drag and drop stuff */
1672 drag_data_get_cb (GtkWidget *widget,
1673 GdkDragContext *context,
1674 GtkSelectionData *selection_data,
1679 ModestHeaderView *self = NULL;
1680 ModestHeaderViewPrivate *priv = NULL;
1682 self = MODEST_HEADER_VIEW (widget);
1683 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1685 /* Set the data. Do not use the current selection because it
1686 could be different than the selection at the beginning of
1688 modest_dnd_selection_data_set_paths (selection_data,
1689 priv->drag_begin_cached_selected_rows);
1693 * We're caching the selected rows at the beginning because the
1694 * selection could change between drag-begin and drag-data-get, for
1695 * example if we have a set of rows already selected, and then we
1696 * click in one of them (without SHIFT key pressed) and begin a drag,
1697 * the selection at that moment contains all the selected lines, but
1698 * after dropping the selection, the release event provokes that only
1699 * the row used to begin the drag is selected, so at the end the
1700 * drag&drop affects only one rows instead of all the selected ones.
1704 drag_begin_cb (GtkWidget *widget,
1705 GdkDragContext *context,
1708 ModestHeaderView *self = NULL;
1709 ModestHeaderViewPrivate *priv = NULL;
1710 GtkTreeSelection *selection;
1712 self = MODEST_HEADER_VIEW (widget);
1713 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1715 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1716 priv->drag_begin_cached_selected_rows =
1717 gtk_tree_selection_get_selected_rows (selection, NULL);
1721 * We use the drag-end signal to clear the cached selection, we use
1722 * this because this allways happens, whether or not the d&d was a
1726 drag_end_cb (GtkWidget *widget,
1730 ModestHeaderView *self = NULL;
1731 ModestHeaderViewPrivate *priv = NULL;
1733 self = MODEST_HEADER_VIEW (widget);
1734 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1736 /* Free cached data */
1737 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1738 g_list_free (priv->drag_begin_cached_selected_rows);
1739 priv->drag_begin_cached_selected_rows = NULL;
1742 /* Header view drag types */
1743 const GtkTargetEntry header_view_drag_types[] = {
1744 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1748 enable_drag_and_drop (GtkWidget *self)
1750 #ifdef MODEST_TOOLKIT_HILDON2
1753 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1754 header_view_drag_types,
1755 G_N_ELEMENTS (header_view_drag_types),
1756 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1760 disable_drag_and_drop (GtkWidget *self)
1762 #ifdef MODEST_TOOLKIT_HILDON2
1765 gtk_drag_source_unset (self);
1769 setup_drag_and_drop (GtkWidget *self)
1771 #ifdef MODEST_TOOLKIT_HILDON2
1774 enable_drag_and_drop(self);
1775 g_signal_connect(G_OBJECT (self), "drag_data_get",
1776 G_CALLBACK(drag_data_get_cb), NULL);
1778 g_signal_connect(G_OBJECT (self), "drag_begin",
1779 G_CALLBACK(drag_begin_cb), NULL);
1781 g_signal_connect(G_OBJECT (self), "drag_end",
1782 G_CALLBACK(drag_end_cb), NULL);
1785 static GtkTreePath *
1786 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1788 GtkTreePath *path = NULL;
1789 GtkTreeSelection *sel = NULL;
1792 sel = gtk_tree_view_get_selection(self);
1793 rows = gtk_tree_selection_get_selected_rows (sel, model);
1795 if ((rows == NULL) || (g_list_length(rows) != 1))
1798 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1803 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1809 #ifndef MODEST_TOOLKIT_HILDON2
1811 * This function moves the tree view scroll to the current selected
1812 * row when the widget grabs the focus
1815 on_focus_in (GtkWidget *self,
1816 GdkEventFocus *event,
1819 GtkTreeSelection *selection;
1820 GtkTreeModel *model;
1821 GList *selected = NULL;
1822 GtkTreePath *selected_path = NULL;
1824 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1828 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1829 /* If none selected yet, pick the first one */
1830 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1834 /* Return if the model is empty */
1835 if (!gtk_tree_model_get_iter_first (model, &iter))
1838 path = gtk_tree_model_get_path (model, &iter);
1839 gtk_tree_selection_select_path (selection, path);
1840 gtk_tree_path_free (path);
1843 /* Need to get the all the rows because is selection multiple */
1844 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1845 if (selected == NULL) return FALSE;
1846 selected_path = (GtkTreePath *) selected->data;
1849 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1850 g_list_free (selected);
1856 on_focus_out (GtkWidget *self,
1857 GdkEventFocus *event,
1861 if (!gtk_widget_is_focus (self)) {
1862 GtkTreeSelection *selection = NULL;
1863 GList *selected_rows = NULL;
1864 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1865 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1866 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1867 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1868 gtk_tree_selection_unselect_all (selection);
1869 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1870 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1871 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1872 g_list_free (selected_rows);
1880 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1882 enable_drag_and_drop(self);
1887 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1889 GtkTreeSelection *selection = NULL;
1890 GtkTreePath *path = NULL;
1891 gboolean already_selected = FALSE, already_opened = FALSE;
1892 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1894 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1896 GtkTreeModel *model;
1898 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1899 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1901 /* Get header from model */
1902 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1903 if (gtk_tree_model_get_iter (model, &iter, path)) {
1904 GValue value = {0,};
1907 gtk_tree_model_get_value (model, &iter,
1908 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1910 header = (TnyHeader *) g_value_get_object (&value);
1911 if (TNY_IS_HEADER (header)) {
1912 status = modest_tny_all_send_queues_get_msg_status (header);
1913 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1916 g_value_unset (&value);
1920 /* Enable drag and drop only if the user clicks on a row that
1921 it's already selected. If not, let him select items using
1922 the pointer. If the message is in an OUTBOX and in sending
1923 status disable drag and drop as well */
1924 if (!already_selected ||
1925 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1927 disable_drag_and_drop(self);
1930 gtk_tree_path_free(path);
1932 /* If it's already opened then do not let the button-press
1933 event go on because it'll perform a message open because
1934 we're clicking on to an already selected header */
1939 folder_monitor_update (TnyFolderObserver *self,
1940 TnyFolderChange *change)
1942 ModestHeaderViewPrivate *priv = NULL;
1943 TnyFolderChangeChanged changed;
1944 TnyFolder *folder = NULL;
1946 changed = tny_folder_change_get_changed (change);
1948 /* Do not notify the observers if the folder of the header
1949 view has changed before this call to the observer
1951 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1952 folder = tny_folder_change_get_folder (change);
1953 if (folder != priv->folder)
1956 MODEST_DEBUG_BLOCK (
1957 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1958 g_print ("ADDED %d/%d (r/t) \n",
1959 tny_folder_change_get_new_unread_count (change),
1960 tny_folder_change_get_new_all_count (change));
1961 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1962 g_print ("ALL COUNT %d\n",
1963 tny_folder_change_get_new_all_count (change));
1964 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1965 g_print ("UNREAD COUNT %d\n",
1966 tny_folder_change_get_new_unread_count (change));
1967 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1968 g_print ("EXPUNGED %d/%d (r/t) \n",
1969 tny_folder_change_get_new_unread_count (change),
1970 tny_folder_change_get_new_all_count (change));
1971 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1972 g_print ("FOLDER RENAME\n");
1973 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1974 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1975 tny_folder_change_get_new_unread_count (change),
1976 tny_folder_change_get_new_all_count (change));
1977 g_print ("---------------------------------------------------\n");
1980 /* Check folder count */
1981 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1982 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1984 g_mutex_lock (priv->observers_lock);
1986 /* Emit signal to evaluate how headers changes affects
1987 to the window view */
1988 g_signal_emit (G_OBJECT(self),
1989 signals[MSG_COUNT_CHANGED_SIGNAL],
1992 /* Added or removed headers, so data stored on cliboard are invalid */
1993 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1994 modest_email_clipboard_clear (priv->clipboard);
1996 g_mutex_unlock (priv->observers_lock);
2002 g_object_unref (folder);
2006 modest_header_view_is_empty (ModestHeaderView *self)
2008 ModestHeaderViewPrivate *priv;
2010 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2012 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2014 return priv->status == HEADER_VIEW_EMPTY;
2018 modest_header_view_clear (ModestHeaderView *self)
2020 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2022 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2026 modest_header_view_copy_selection (ModestHeaderView *header_view)
2028 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2030 /* Copy selection */
2031 _clipboard_set_selected_data (header_view, FALSE);
2035 modest_header_view_cut_selection (ModestHeaderView *header_view)
2037 ModestHeaderViewPrivate *priv = NULL;
2038 const gchar **hidding = NULL;
2039 guint i, n_selected;
2041 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2043 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2045 /* Copy selection */
2046 _clipboard_set_selected_data (header_view, TRUE);
2048 /* Get hidding ids */
2049 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2051 /* Clear hidding array created by previous cut operation */
2052 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2054 /* Copy hidding array */
2055 priv->n_selected = n_selected;
2056 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2057 for (i=0; i < n_selected; i++)
2058 priv->hidding_ids[i] = g_strdup(hidding[i]);
2060 /* Hide cut headers */
2061 modest_header_view_refilter (header_view);
2068 _clipboard_set_selected_data (ModestHeaderView *header_view,
2071 ModestHeaderViewPrivate *priv = NULL;
2072 TnyList *headers = NULL;
2074 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2075 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2077 /* Set selected data on clipboard */
2078 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2079 headers = modest_header_view_get_selected_headers (header_view);
2080 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2083 g_object_unref (headers);
2087 ModestHeaderView *self;
2092 notify_filter_change (gpointer data)
2094 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2096 g_signal_emit (info->self,
2097 signals[MSG_COUNT_CHANGED_SIGNAL],
2098 0, info->folder, NULL);
2104 notify_filter_change_destroy (gpointer data)
2106 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2107 ModestHeaderViewPrivate *priv;
2109 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2110 priv->status_timeout = 0;
2112 g_object_unref (info->self);
2113 g_object_unref (info->folder);
2114 g_slice_free (NotifyFilterInfo, info);
2118 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2120 /* For the moment we only need to filter outbox */
2121 return priv->is_outbox;
2125 header_match_string (TnyHeader *header, gchar **words)
2127 gchar **current_word;
2132 for (current_word = words; !found && *current_word != NULL; current_word++) {
2139 subject = tny_header_dup_subject (header);
2140 cc = tny_header_dup_cc (header);
2141 bcc = tny_header_dup_bcc (header);
2142 to = tny_header_dup_to (header);
2143 from = tny_header_dup_from (header);
2145 if ((subject && g_strstr_len (subject, -1, *current_word))
2146 || (cc && g_strstr_len (cc, -1, *current_word))
2147 || (bcc && g_strstr_len (bcc, -1, *current_word))
2148 || (to && g_strstr_len (to, -1, *current_word))
2149 || (from && g_strstr_len (from, -1, *current_word)))
2161 filter_row (GtkTreeModel *model,
2165 ModestHeaderViewPrivate *priv = NULL;
2166 TnyHeaderFlags flags;
2167 TnyHeader *header = NULL;
2170 gboolean visible = TRUE;
2171 gboolean found = FALSE;
2172 GValue value = {0,};
2173 HeaderViewStatus old_status;
2175 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2176 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2178 /* Get header from model */
2179 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2180 flags = (TnyHeaderFlags) g_value_get_int (&value);
2181 g_value_unset (&value);
2182 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2183 header = (TnyHeader *) g_value_get_object (&value);
2184 g_value_unset (&value);
2186 /* Get message id from header (ensure is a valid id) */
2192 /* Hide deleted and mark as deleted heders */
2193 if (flags & TNY_HEADER_FLAG_DELETED ||
2194 flags & TNY_HEADER_FLAG_EXPUNGED) {
2199 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2200 if (current_folder_needs_filtering (priv) &&
2201 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2207 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2208 if (current_folder_needs_filtering (priv) &&
2209 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2215 if (visible && priv->filter_string) {
2216 if (!header_match_string (header, priv->filter_string_splitted)) {
2222 /* If no data on clipboard, return always TRUE */
2223 if (modest_email_clipboard_cleared(priv->clipboard)) {
2229 if (priv->hidding_ids != NULL) {
2230 id = tny_header_dup_message_id (header);
2231 for (i=0; i < priv->n_selected && !found; i++)
2232 if (priv->hidding_ids[i] != NULL && id != NULL)
2233 found = (!strcmp (priv->hidding_ids[i], id));
2240 old_status = priv->status;
2241 priv->status = ((gboolean) priv->status) && !visible;
2242 if ((priv->notify_status) && (priv->status != old_status)) {
2243 if (priv->status_timeout)
2244 g_source_remove (priv->status_timeout);
2247 NotifyFilterInfo *info;
2249 info = g_slice_new0 (NotifyFilterInfo);
2250 info->self = g_object_ref (G_OBJECT (user_data));
2252 info->folder = tny_header_get_folder (header);
2253 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2254 notify_filter_change,
2256 notify_filter_change_destroy);
2264 _clear_hidding_filter (ModestHeaderView *header_view)
2266 ModestHeaderViewPrivate *priv = NULL;
2269 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2270 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2272 if (priv->hidding_ids != NULL) {
2273 for (i=0; i < priv->n_selected; i++)
2274 g_free (priv->hidding_ids[i]);
2275 g_free(priv->hidding_ids);
2280 modest_header_view_refilter (ModestHeaderView *header_view)
2282 GtkTreeModel *model, *sortable = NULL;
2283 ModestHeaderViewPrivate *priv = NULL;
2285 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2286 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2288 /* Hide cut headers */
2289 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2290 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2291 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2292 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2293 priv->status = HEADER_VIEW_INIT;
2294 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2300 * Called when an account is removed. If I'm showing a folder of the
2301 * account that has been removed then clear the view
2304 on_account_removed (TnyAccountStore *self,
2305 TnyAccount *account,
2308 ModestHeaderViewPrivate *priv = NULL;
2310 /* Ignore changes in transport accounts */
2311 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2314 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2317 TnyAccount *my_account;
2319 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2320 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2321 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2322 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2324 my_account = tny_folder_get_account (priv->folder);
2328 if (my_account == account)
2329 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2330 g_object_unref (my_account);
2336 modest_header_view_add_observer(ModestHeaderView *header_view,
2337 ModestHeaderViewObserver *observer)
2339 ModestHeaderViewPrivate *priv;
2341 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2342 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2344 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2346 g_mutex_lock(priv->observer_list_lock);
2347 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2348 g_mutex_unlock(priv->observer_list_lock);
2352 modest_header_view_remove_observer(ModestHeaderView *header_view,
2353 ModestHeaderViewObserver *observer)
2355 ModestHeaderViewPrivate *priv;
2357 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2358 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2360 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2362 g_mutex_lock(priv->observer_list_lock);
2363 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2364 g_mutex_unlock(priv->observer_list_lock);
2368 modest_header_view_notify_observers(ModestHeaderView *header_view,
2369 GtkTreeModel *model,
2370 const gchar *tny_folder_id)
2372 ModestHeaderViewPrivate *priv = NULL;
2374 ModestHeaderViewObserver *observer;
2377 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2379 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2381 g_mutex_lock(priv->observer_list_lock);
2382 iter = priv->observer_list;
2383 while(iter != NULL){
2384 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2385 modest_header_view_observer_update(observer, model,
2387 iter = g_slist_next(iter);
2389 g_mutex_unlock(priv->observer_list_lock);
2393 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2395 ModestHeaderViewPrivate *priv = NULL;
2397 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2398 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2402 modest_header_view_set_filter (ModestHeaderView *self,
2403 ModestHeaderViewFilter filter)
2405 ModestHeaderViewPrivate *priv;
2407 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2408 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2410 priv->filter |= filter;
2412 if (current_folder_needs_filtering (priv))
2413 modest_header_view_refilter (self);
2417 modest_header_view_unset_filter (ModestHeaderView *self,
2418 ModestHeaderViewFilter filter)
2420 ModestHeaderViewPrivate *priv;
2422 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2423 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2425 priv->filter &= ~filter;
2427 if (current_folder_needs_filtering (priv))
2428 modest_header_view_refilter (self);
2432 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2434 if (strcmp ("style", spec->name) == 0) {
2435 update_style (MODEST_HEADER_VIEW (obj));
2436 gtk_widget_queue_draw (GTK_WIDGET (obj));
2441 update_style (ModestHeaderView *self)
2443 ModestHeaderViewPrivate *priv;
2444 GdkColor style_color;
2445 GdkColor style_active_color;
2446 PangoAttrList *attr_list;
2448 PangoAttribute *attr;
2450 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2451 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2455 attr_list = pango_attr_list_new ();
2456 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
2457 gdk_color_parse (MODEST_SECONDARY_COLOR, &style_color);
2459 priv->secondary_color = style_color;
2460 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2461 pango_attr_list_insert (attr_list, attr);
2464 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2466 "SmallSystemFont", NULL,
2469 attr = pango_attr_font_desc_new (pango_font_description_copy
2470 (style->font_desc));
2471 pango_attr_list_insert (attr_list, attr);
2473 g_object_set (G_OBJECT (priv->renderer_address),
2474 "foreground-gdk", &(priv->secondary_color),
2475 "foreground-set", TRUE,
2476 "attributes", attr_list,
2478 g_object_set (G_OBJECT (priv->renderer_date_status),
2479 "foreground-gdk", &(priv->secondary_color),
2480 "foreground-set", TRUE,
2481 "attributes", attr_list,
2483 pango_attr_list_unref (attr_list);
2485 g_object_set (G_OBJECT (priv->renderer_address),
2486 "foreground-gdk", &(priv->secondary_color),
2487 "foreground-set", TRUE,
2488 "scale", PANGO_SCALE_SMALL,
2491 g_object_set (G_OBJECT (priv->renderer_date_status),
2492 "foreground-gdk", &(priv->secondary_color),
2493 "foreground-set", TRUE,
2494 "scale", PANGO_SCALE_SMALL,
2499 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
2500 priv->active_color = style_active_color;
2501 #ifdef MODEST_TOOLKIT_HILDON2
2502 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2503 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2506 #ifdef MODEST_TOOLKIT_HILDON2
2507 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2513 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2518 GtkTreeModel *tree_model;
2523 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2531 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2532 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2536 gtk_tree_model_get (tree_model, &iter,
2537 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2544 modest_header_view_set_filter_string (ModestHeaderView *self,
2545 const gchar *filter_string)
2547 ModestHeaderViewPrivate *priv;
2549 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2550 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2552 if (priv->filter_string)
2553 g_free (priv->filter_string);
2555 priv->filter_string = g_strdup (filter_string);
2557 if (priv->filter_string_splitted) {
2558 g_strfreev (priv->filter_string_splitted);
2559 priv->filter_string_splitted = NULL;
2562 if (priv->filter_string) {
2563 priv->filter_string_splitted = g_strsplit (priv->filter_string, " ", 0);
2565 modest_header_view_refilter (MODEST_HEADER_VIEW (self));