1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
36 #include <tny-merge-folder.h>
39 #include <modest-header-view.h>
40 #include <modest-header-view-priv.h>
41 #include <modest-dnd.h>
42 #include <modest-tny-folder.h>
43 #include <modest-debug.h>
44 #include <modest-main-window.h>
45 #include <modest-ui-actions.h>
46 #include <modest-marshal.h>
47 #include <modest-text-utils.h>
48 #include <modest-icon-names.h>
49 #include <modest-runtime.h>
50 #include "modest-platform.h"
51 #include <modest-hbox-cell-renderer.h>
52 #include <modest-vbox-cell-renderer.h>
53 #include <modest-datetime-formatter.h>
54 #include <modest-ui-constants.h>
56 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
57 static void modest_header_view_init (ModestHeaderView *obj);
58 static void modest_header_view_finalize (GObject *obj);
59 static void modest_header_view_dispose (GObject *obj);
61 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
62 GtkTreeViewColumn *column, gpointer userdata);
64 static gint cmp_rows (GtkTreeModel *tree_model,
69 static gint cmp_subject_rows (GtkTreeModel *tree_model,
74 static gboolean filter_row (GtkTreeModel *model,
78 static void on_account_removed (TnyAccountStore *self,
82 static void on_selection_changed (GtkTreeSelection *sel,
85 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
88 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
91 static void setup_drag_and_drop (GtkWidget *self);
93 static void enable_drag_and_drop (GtkWidget *self);
95 static void disable_drag_and_drop (GtkWidget *self);
97 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
99 #ifndef MODEST_TOOLKIT_HILDON2
100 static gboolean on_focus_in (GtkWidget *sef,
101 GdkEventFocus *event,
104 static gboolean on_focus_out (GtkWidget *self,
105 GdkEventFocus *event,
109 static void folder_monitor_update (TnyFolderObserver *self,
110 TnyFolderChange *change);
112 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
114 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
116 static void _clear_hidding_filter (ModestHeaderView *header_view);
118 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
120 const gchar *tny_folder_id);
122 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
123 GdkEventExpose *event,
126 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
127 static void update_style (ModestHeaderView *self);
130 HEADER_VIEW_NON_EMPTY,
135 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
136 struct _ModestHeaderViewPrivate {
138 ModestHeaderViewStyle style;
141 TnyFolderMonitor *monitor;
142 GMutex *observers_lock;
144 /*header-view-observer observer*/
145 GMutex *observer_list_lock;
146 GSList *observer_list;
148 /* not unref this object, its a singlenton */
149 ModestEmailClipboard *clipboard;
151 /* Filter tree model */
154 GtkTreeRowReference *autoselect_reference;
155 ModestHeaderViewFilter filter;
157 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
158 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
160 gulong selection_changed_handler;
161 gulong acc_removed_handler;
163 GList *drag_begin_cached_selected_rows;
165 HeaderViewStatus status;
166 guint status_timeout;
167 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
169 ModestDatetimeFormatter *datetime_formatter;
171 GtkCellRenderer *renderer_subject;
172 GtkCellRenderer *renderer_address;
173 GtkCellRenderer *renderer_date_status;
175 GdkColor active_color;
176 GdkColor secondary_color;
180 gchar *filter_string;
181 gchar **filter_string_splitted;
184 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
185 struct _HeadersCountChangedHelper {
186 ModestHeaderView *self;
187 TnyFolderChange *change;
191 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
192 MODEST_TYPE_HEADER_VIEW, \
193 ModestHeaderViewPrivate))
197 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
200 HEADER_SELECTED_SIGNAL,
201 HEADER_ACTIVATED_SIGNAL,
202 ITEM_NOT_FOUND_SIGNAL,
203 MSG_COUNT_CHANGED_SIGNAL,
204 UPDATING_MSG_LIST_SIGNAL,
209 static GObjectClass *parent_class = NULL;
211 /* uncomment the following if you have defined any signals */
212 static guint signals[LAST_SIGNAL] = {0};
215 modest_header_view_get_type (void)
217 static GType my_type = 0;
219 static const GTypeInfo my_info = {
220 sizeof(ModestHeaderViewClass),
221 NULL, /* base init */
222 NULL, /* base finalize */
223 (GClassInitFunc) modest_header_view_class_init,
224 NULL, /* class finalize */
225 NULL, /* class data */
226 sizeof(ModestHeaderView),
228 (GInstanceInitFunc) modest_header_view_init,
232 static const GInterfaceInfo tny_folder_observer_info =
234 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
235 NULL, /* interface_finalize */
236 NULL /* interface_data */
238 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
242 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
243 &tny_folder_observer_info);
251 modest_header_view_class_init (ModestHeaderViewClass *klass)
253 GObjectClass *gobject_class;
254 gobject_class = (GObjectClass*) klass;
256 parent_class = g_type_class_peek_parent (klass);
257 gobject_class->finalize = modest_header_view_finalize;
258 gobject_class->dispose = modest_header_view_dispose;
260 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
262 signals[HEADER_SELECTED_SIGNAL] =
263 g_signal_new ("header_selected",
264 G_TYPE_FROM_CLASS (gobject_class),
266 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
268 g_cclosure_marshal_VOID__POINTER,
269 G_TYPE_NONE, 1, G_TYPE_POINTER);
271 signals[HEADER_ACTIVATED_SIGNAL] =
272 g_signal_new ("header_activated",
273 G_TYPE_FROM_CLASS (gobject_class),
275 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
277 gtk_marshal_VOID__POINTER_POINTER,
278 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
281 signals[ITEM_NOT_FOUND_SIGNAL] =
282 g_signal_new ("item_not_found",
283 G_TYPE_FROM_CLASS (gobject_class),
285 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
287 g_cclosure_marshal_VOID__INT,
288 G_TYPE_NONE, 1, G_TYPE_INT);
290 signals[MSG_COUNT_CHANGED_SIGNAL] =
291 g_signal_new ("msg_count_changed",
292 G_TYPE_FROM_CLASS (gobject_class),
294 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
296 modest_marshal_VOID__POINTER_POINTER,
297 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
299 signals[UPDATING_MSG_LIST_SIGNAL] =
300 g_signal_new ("updating-msg-list",
301 G_TYPE_FROM_CLASS (gobject_class),
303 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
305 g_cclosure_marshal_VOID__BOOLEAN,
306 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
308 #ifdef MODEST_TOOLKIT_HILDON2
309 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
315 tny_folder_observer_init (TnyFolderObserverIface *klass)
317 klass->update = folder_monitor_update;
320 static GtkTreeViewColumn*
321 get_new_column (const gchar *name, GtkCellRenderer *renderer,
322 gboolean resizable, gint sort_col_id, gboolean show_as_text,
323 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
325 GtkTreeViewColumn *column;
327 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
328 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
330 gtk_tree_view_column_set_resizable (column, resizable);
332 gtk_tree_view_column_set_expand (column, TRUE);
335 gtk_tree_view_column_add_attribute (column, renderer, "text",
337 if (sort_col_id >= 0)
338 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
340 gtk_tree_view_column_set_sort_indicator (column, FALSE);
341 gtk_tree_view_column_set_reorderable (column, TRUE);
344 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
351 remove_all_columns (ModestHeaderView *obj)
353 GList *columns, *cursor;
355 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
357 for (cursor = columns; cursor; cursor = cursor->next)
358 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
359 GTK_TREE_VIEW_COLUMN(cursor->data));
360 g_list_free (columns);
364 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
366 GtkTreeModel *sortable;
367 GtkTreeViewColumn *column=NULL;
368 GtkTreeSelection *selection = NULL;
369 GtkCellRenderer *renderer_header,
370 *renderer_attach, *renderer_compact_date_or_status;
371 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
372 *renderer_subject_box, *renderer_recpt,
374 ModestHeaderViewPrivate *priv;
375 GtkTreeViewColumn *compact_column = NULL;
378 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
379 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
381 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
383 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
385 /* TODO: check whether these renderers need to be freed */
386 renderer_attach = gtk_cell_renderer_pixbuf_new ();
387 renderer_priority = gtk_cell_renderer_pixbuf_new ();
388 renderer_header = gtk_cell_renderer_text_new ();
390 renderer_compact_header = modest_vbox_cell_renderer_new ();
391 renderer_recpt_box = modest_hbox_cell_renderer_new ();
392 renderer_subject_box = modest_hbox_cell_renderer_new ();
393 renderer_recpt = gtk_cell_renderer_text_new ();
394 priv->renderer_address = renderer_recpt;
395 priv->renderer_subject = gtk_cell_renderer_text_new ();
396 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
397 priv->renderer_date_status = renderer_compact_date_or_status;
399 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
400 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
401 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
402 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
403 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
404 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
405 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
406 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
407 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
408 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
409 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
410 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
411 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
412 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
414 #ifdef MODEST_TOOLKIT_HILDON2
415 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
417 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
418 #ifndef MODEST_TOOLKIT_GTK
419 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
420 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
422 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
423 g_object_set(G_OBJECT(renderer_header),
424 "ellipsize", PANGO_ELLIPSIZE_END,
426 g_object_set (G_OBJECT (priv->renderer_subject),
427 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
429 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
430 g_object_set (G_OBJECT (renderer_recpt),
431 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
433 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
434 g_object_set(G_OBJECT(renderer_compact_date_or_status),
435 "xalign", 1.0, "yalign", 0.1,
437 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
438 #ifdef MODEST_TOOLKIT_HILDON2
439 g_object_set (G_OBJECT (renderer_priority),
441 "xalign", 0.0, NULL);
442 g_object_set (G_OBJECT (renderer_attach),
444 "xalign", 0.0, NULL);
446 g_object_set (G_OBJECT (renderer_priority),
447 "yalign", 0.5, NULL);
448 g_object_set (G_OBJECT (renderer_attach),
449 "yalign", 0.0, NULL);
452 #ifdef MODEST_TOOLKIT_HILDON1
453 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
454 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
455 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
456 #elif MODEST_TOOLKIT_HILDON2
457 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
458 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
459 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
461 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
462 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
463 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
466 remove_all_columns (self);
468 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
469 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
470 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
472 /* Add new columns */
473 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
474 ModestHeaderViewColumn col =
475 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
477 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
478 g_printerr ("modest: invalid column %d in column list\n", col);
484 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
485 column = get_new_column (_("A"), renderer_attach, FALSE,
486 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
488 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
490 gtk_tree_view_column_set_fixed_width (column, 45);
494 case MODEST_HEADER_VIEW_COLUMN_FROM:
495 column = get_new_column (_("From"), renderer_header, TRUE,
496 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
498 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
499 GINT_TO_POINTER(TRUE));
502 case MODEST_HEADER_VIEW_COLUMN_TO:
503 column = get_new_column (_("To"), renderer_header, TRUE,
504 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
506 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
507 GINT_TO_POINTER(FALSE));
510 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
511 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
512 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
514 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
515 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
516 compact_column = column;
519 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
520 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
521 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
523 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
524 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
525 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
526 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
527 compact_column = column;
531 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
532 column = get_new_column (_("Subject"), renderer_header, TRUE,
533 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
535 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
539 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
540 column = get_new_column (_("Received"), renderer_header, TRUE,
541 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
543 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
544 GINT_TO_POINTER(TRUE));
547 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
548 column = get_new_column (_("Sent"), renderer_header, TRUE,
549 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
551 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
552 GINT_TO_POINTER(FALSE));
555 case MODEST_HEADER_VIEW_COLUMN_SIZE:
556 column = get_new_column (_("Size"), renderer_header, TRUE,
557 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
559 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
562 case MODEST_HEADER_VIEW_COLUMN_STATUS:
563 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
564 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
566 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
571 g_return_val_if_reached(FALSE);
574 /* we keep the column id around */
575 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
576 GINT_TO_POINTER(col));
578 /* we need this ptr when sorting the rows */
579 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
581 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
585 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
586 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
587 (GtkTreeIterCompareFunc) cmp_rows,
588 compact_column, NULL);
589 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
590 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
591 (GtkTreeIterCompareFunc) cmp_subject_rows,
592 compact_column, NULL);
596 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
602 datetime_format_changed (ModestDatetimeFormatter *formatter,
603 ModestHeaderView *self)
605 gtk_widget_queue_draw (GTK_WIDGET (self));
609 modest_header_view_init (ModestHeaderView *obj)
611 ModestHeaderViewPrivate *priv;
614 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
616 priv->show_latest = 0;
619 priv->is_outbox = FALSE;
621 priv->monitor = NULL;
622 priv->observers_lock = g_mutex_new ();
623 priv->autoselect_reference = NULL;
625 priv->status = HEADER_VIEW_INIT;
626 priv->status_timeout = 0;
627 priv->notify_status = TRUE;
629 priv->observer_list_lock = g_mutex_new();
630 priv->observer_list = NULL;
632 priv->clipboard = modest_runtime_get_email_clipboard ();
633 priv->hidding_ids = NULL;
634 priv->n_selected = 0;
635 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
636 priv->filter_string = NULL;
637 priv->filter_string_splitted = NULL;
638 priv->selection_changed_handler = 0;
639 priv->acc_removed_handler = 0;
641 /* Sort parameters */
642 for (j=0; j < 2; j++) {
643 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
644 priv->sort_colid[j][i] = -1;
645 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
649 priv->datetime_formatter = modest_datetime_formatter_new ();
650 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
651 G_CALLBACK (datetime_format_changed), (gpointer) obj);
653 setup_drag_and_drop (GTK_WIDGET(obj));
657 modest_header_view_dispose (GObject *obj)
659 ModestHeaderView *self;
660 ModestHeaderViewPrivate *priv;
661 GtkTreeSelection *sel;
663 self = MODEST_HEADER_VIEW(obj);
664 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
666 if (priv->datetime_formatter) {
667 g_object_unref (priv->datetime_formatter);
668 priv->datetime_formatter = NULL;
671 /* Free in the dispose to avoid unref cycles */
673 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
674 g_object_unref (G_OBJECT (priv->folder));
678 /* We need to do this here in the dispose because the
679 selection won't exist when finalizing */
680 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
681 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
682 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
683 priv->selection_changed_handler = 0;
686 G_OBJECT_CLASS(parent_class)->dispose (obj);
690 modest_header_view_finalize (GObject *obj)
692 ModestHeaderView *self;
693 ModestHeaderViewPrivate *priv;
695 self = MODEST_HEADER_VIEW(obj);
696 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
698 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
699 priv->acc_removed_handler)) {
700 g_signal_handler_disconnect (modest_runtime_get_account_store (),
701 priv->acc_removed_handler);
704 /* There is no need to lock because there should not be any
705 * reference to self now. */
706 g_mutex_free(priv->observer_list_lock);
707 g_slist_free(priv->observer_list);
709 g_mutex_lock (priv->observers_lock);
711 tny_folder_monitor_stop (priv->monitor);
712 g_object_unref (G_OBJECT (priv->monitor));
714 g_mutex_unlock (priv->observers_lock);
715 g_mutex_free (priv->observers_lock);
717 /* Clear hidding array created by cut operation */
718 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
720 if (priv->autoselect_reference != NULL) {
721 gtk_tree_row_reference_free (priv->autoselect_reference);
722 priv->autoselect_reference = NULL;
725 if (priv->filter_string) {
726 g_free (priv->filter_string);
729 if (priv->filter_string_splitted) {
730 g_strfreev (priv->filter_string_splitted);
733 G_OBJECT_CLASS(parent_class)->finalize (obj);
738 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
741 GtkTreeSelection *sel;
742 ModestHeaderView *self;
743 ModestHeaderViewPrivate *priv;
745 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
748 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
749 self = MODEST_HEADER_VIEW(obj);
750 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
752 modest_header_view_set_style (self, style);
754 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
755 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
756 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
758 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
759 TRUE); /* alternating row colors */
761 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
762 priv->selection_changed_handler =
763 g_signal_connect_after (sel, "changed",
764 G_CALLBACK(on_selection_changed), self);
766 g_signal_connect (self, "row-activated",
767 G_CALLBACK (on_header_row_activated), NULL);
769 #ifndef MODEST_TOOLKIT_HILDON2
770 g_signal_connect (self, "focus-in-event",
771 G_CALLBACK(on_focus_in), NULL);
772 g_signal_connect (self, "focus-out-event",
773 G_CALLBACK(on_focus_out), NULL);
776 g_signal_connect (self, "button-press-event",
777 G_CALLBACK(on_button_press_event), NULL);
778 g_signal_connect (self, "button-release-event",
779 G_CALLBACK(on_button_release_event), NULL);
781 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
783 G_CALLBACK (on_account_removed),
786 g_signal_connect (self, "expose-event",
787 G_CALLBACK(modest_header_view_on_expose_event),
790 return GTK_WIDGET(self);
795 modest_header_view_count_selected_headers (ModestHeaderView *self)
797 GtkTreeSelection *sel;
800 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
802 /* Get selection object and check selected rows count */
803 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
804 selected_rows = gtk_tree_selection_count_selected_rows (sel);
806 return selected_rows;
810 modest_header_view_has_selected_headers (ModestHeaderView *self)
812 GtkTreeSelection *sel;
815 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
817 /* Get selection object and check selected rows count */
818 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
819 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
826 modest_header_view_get_selected_headers (ModestHeaderView *self)
828 GtkTreeSelection *sel;
829 TnyList *header_list = NULL;
831 GList *list, *tmp = NULL;
832 GtkTreeModel *tree_model = NULL;
835 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
838 /* Get selected rows */
839 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
840 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
843 header_list = tny_simple_list_new();
845 list = g_list_reverse (list);
848 /* get header from selection */
849 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
850 gtk_tree_model_get (tree_model, &iter,
851 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
853 /* Prepend to list */
854 tny_list_prepend (header_list, G_OBJECT (header));
855 g_object_unref (G_OBJECT (header));
857 tmp = g_list_next (tmp);
860 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
867 /* scroll our list view so the selected item is visible */
869 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
871 #ifdef MODEST_TOOLKIT_GTK
873 GtkTreePath *selected_path;
874 GtkTreePath *start, *end;
878 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
879 selected_path = gtk_tree_model_get_path (model, iter);
881 start = gtk_tree_path_new ();
882 end = gtk_tree_path_new ();
884 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
886 if (gtk_tree_path_compare (selected_path, start) < 0 ||
887 gtk_tree_path_compare (end, selected_path) < 0)
888 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
889 selected_path, NULL, TRUE,
892 gtk_tree_path_free (selected_path);
893 gtk_tree_path_free (start);
894 gtk_tree_path_free (end);
896 #endif /* MODEST_TOOLKIT_GTK */
901 modest_header_view_select_next (ModestHeaderView *self)
903 GtkTreeSelection *sel;
908 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
910 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
911 path = get_selected_row (GTK_TREE_VIEW(self), &model);
912 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
913 /* Unselect previous path */
914 gtk_tree_selection_unselect_path (sel, path);
916 /* Move path down and selects new one */
917 if (gtk_tree_model_iter_next (model, &iter)) {
918 gtk_tree_selection_select_iter (sel, &iter);
919 scroll_to_selected (self, &iter, FALSE);
921 gtk_tree_path_free(path);
927 modest_header_view_select_prev (ModestHeaderView *self)
929 GtkTreeSelection *sel;
934 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
936 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
937 path = get_selected_row (GTK_TREE_VIEW(self), &model);
938 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
939 /* Unselect previous path */
940 gtk_tree_selection_unselect_path (sel, path);
943 if (gtk_tree_path_prev (path)) {
944 gtk_tree_model_get_iter (model, &iter, path);
946 /* Select the new one */
947 gtk_tree_selection_select_iter (sel, &iter);
948 scroll_to_selected (self, &iter, TRUE);
951 gtk_tree_path_free (path);
956 modest_header_view_get_columns (ModestHeaderView *self)
958 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
960 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
966 modest_header_view_set_style (ModestHeaderView *self,
967 ModestHeaderViewStyle style)
969 ModestHeaderViewPrivate *priv;
970 gboolean show_col_headers = FALSE;
971 ModestHeaderViewStyle old_style;
973 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
974 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
977 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
978 if (priv->style == style)
979 return TRUE; /* nothing to do */
982 case MODEST_HEADER_VIEW_STYLE_DETAILS:
983 show_col_headers = TRUE;
985 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
988 g_return_val_if_reached (FALSE);
990 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
991 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
993 old_style = priv->style;
1000 ModestHeaderViewStyle
1001 modest_header_view_get_style (ModestHeaderView *self)
1003 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1005 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
1008 /* This is used to automatically select the first header if the user
1009 * has not selected any header yet.
1012 modest_header_view_on_expose_event(GtkTreeView *header_view,
1013 GdkEventExpose *event,
1016 GtkTreeSelection *sel;
1017 GtkTreeModel *model;
1018 GtkTreeIter tree_iter;
1019 ModestHeaderViewPrivate *priv;
1021 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1022 model = gtk_tree_view_get_model(header_view);
1027 #ifdef MODEST_TOOLKIT_HILDON2
1030 sel = gtk_tree_view_get_selection(header_view);
1031 if(!gtk_tree_selection_count_selected_rows(sel)) {
1032 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1033 GtkTreePath *tree_iter_path;
1034 /* Prevent the widget from getting the focus
1035 when selecting the first item */
1036 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1037 g_object_set(header_view, "can-focus", FALSE, NULL);
1038 gtk_tree_selection_select_iter(sel, &tree_iter);
1039 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1040 g_object_set(header_view, "can-focus", TRUE, NULL);
1041 if (priv->autoselect_reference) {
1042 gtk_tree_row_reference_free (priv->autoselect_reference);
1044 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1045 gtk_tree_path_free (tree_iter_path);
1048 if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
1049 gboolean moved_selection = FALSE;
1050 GtkTreePath * last_path;
1051 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1052 moved_selection = TRUE;
1056 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1057 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1058 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1059 moved_selection = TRUE;
1060 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1062 gtk_tree_path_free (last_path);
1064 if (moved_selection) {
1065 gtk_tree_row_reference_free (priv->autoselect_reference);
1066 priv->autoselect_reference = NULL;
1069 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1070 GtkTreePath *current_path;
1071 current_path = gtk_tree_model_get_path (model, &tree_iter);
1072 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1073 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1074 g_object_set(header_view, "can-focus", FALSE, NULL);
1075 gtk_tree_selection_unselect_all (sel);
1076 gtk_tree_selection_select_iter(sel, &tree_iter);
1077 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1078 g_object_set(header_view, "can-focus", TRUE, NULL);
1079 gtk_tree_row_reference_free (priv->autoselect_reference);
1080 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1082 gtk_tree_path_free (current_path);
1083 gtk_tree_path_free (last_path);
1093 modest_header_view_get_folder (ModestHeaderView *self)
1095 ModestHeaderViewPrivate *priv;
1097 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1099 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1102 g_object_ref (priv->folder);
1104 return priv->folder;
1108 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1114 ModestHeaderView *self;
1115 ModestHeaderViewPrivate *priv;
1117 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1119 self = MODEST_HEADER_VIEW (user_data);
1120 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1122 if (cancelled || err)
1125 /* Add IDLE observer (monitor) and another folder observer for
1126 new messages (self) */
1127 g_mutex_lock (priv->observers_lock);
1128 if (priv->monitor) {
1129 tny_folder_monitor_stop (priv->monitor);
1130 g_object_unref (G_OBJECT (priv->monitor));
1132 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1133 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1134 tny_folder_monitor_start (priv->monitor);
1135 g_mutex_unlock (priv->observers_lock);
1139 modest_header_view_set_folder_intern (ModestHeaderView *self,
1145 ModestHeaderViewPrivate *priv;
1146 GList *cols, *cursor;
1147 GtkTreeModel *filter_model, *sortable;
1149 GtkSortType sort_type;
1151 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1153 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1154 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (headers), priv->show_latest);
1156 /* Start the monitor in the callback of the
1157 tny_gtk_header_list_model_set_folder call. It's crucial to
1158 do it there and not just after the call because we want the
1159 monitor to observe only the headers returned by the
1160 tny_folder_get_headers_async call that it's inside the
1161 tny_gtk_header_list_model_set_folder call. This way the
1162 monitor infrastructure could successfully cope with
1163 duplicates. For example if a tny_folder_add_msg_async is
1164 happening while tny_gtk_header_list_model_set_folder is
1165 invoked, then the first call could add a header that will
1166 be added again by tny_gtk_header_list_model_set_folder, so
1167 we'd end up with duplicate headers. sergio */
1168 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1170 set_folder_intern_get_headers_async_cb,
1173 /* Create a tree model filter to hide and show rows for cut operations */
1174 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1175 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1176 filter_row, self, NULL);
1177 g_object_unref (headers);
1179 /* Init filter_row function to examine empty status */
1180 priv->status = HEADER_VIEW_INIT;
1182 /* Create sortable model */
1183 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1184 g_object_unref (filter_model);
1186 /* install our special sorting functions */
1187 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1189 /* Restore sort column id */
1191 type = modest_tny_folder_guess_folder_type (folder);
1192 if (type == TNY_FOLDER_TYPE_INVALID)
1193 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1195 sort_colid = modest_header_view_get_sort_column_id (self, type);
1196 sort_type = modest_header_view_get_sort_type (self, type);
1197 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1200 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1201 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1202 (GtkTreeIterCompareFunc) cmp_rows,
1204 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1205 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1206 (GtkTreeIterCompareFunc) cmp_subject_rows,
1211 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1212 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1213 g_object_unref (sortable);
1220 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1222 GtkSortType sort_type)
1224 ModestHeaderViewPrivate *priv = NULL;
1225 GtkTreeModel *sortable = NULL;
1228 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1229 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1231 /* Get model and private data */
1232 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1233 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1235 /* Sort tree model */
1236 type = modest_tny_folder_guess_folder_type (priv->folder);
1237 if (type == TNY_FOLDER_TYPE_INVALID)
1238 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1240 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1243 /* Store new sort parameters */
1244 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1249 modest_header_view_set_sort_params (ModestHeaderView *self,
1251 GtkSortType sort_type,
1254 ModestHeaderViewPrivate *priv;
1255 ModestHeaderViewStyle style;
1257 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1258 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1259 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1261 style = modest_header_view_get_style (self);
1262 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1264 priv->sort_colid[style][type] = sort_colid;
1265 priv->sort_type[style][type] = sort_type;
1269 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1272 ModestHeaderViewPrivate *priv;
1273 ModestHeaderViewStyle style;
1275 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1276 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1278 style = modest_header_view_get_style (self);
1279 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1281 return priv->sort_colid[style][type];
1285 modest_header_view_get_sort_type (ModestHeaderView *self,
1288 ModestHeaderViewPrivate *priv;
1289 ModestHeaderViewStyle style;
1291 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1292 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1294 style = modest_header_view_get_style (self);
1295 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1297 return priv->sort_type[style][type];
1301 ModestHeaderView *header_view;
1302 RefreshAsyncUserCallback cb;
1307 folder_refreshed_cb (ModestMailOperation *mail_op,
1311 ModestHeaderViewPrivate *priv;
1312 SetFolderHelper *info;
1314 info = (SetFolderHelper*) user_data;
1316 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1320 info->cb (mail_op, folder, info->user_data);
1322 /* Start the folder count changes observer. We do not need it
1323 before the refresh. Note that the monitor could still be
1324 called for this refresh but now we know that the callback
1325 was previously called */
1326 g_mutex_lock (priv->observers_lock);
1327 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1328 g_mutex_unlock (priv->observers_lock);
1330 /* Notify the observers that the update is over */
1331 g_signal_emit (G_OBJECT (info->header_view),
1332 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1334 /* Allow filtering notifications from now on if the current
1335 folder is still the same (if not then the user has selected
1336 another one to refresh, we should wait until that refresh
1338 if (priv->folder == folder)
1339 priv->notify_status = TRUE;
1342 g_object_unref (info->header_view);
1347 refresh_folder_error_handler (ModestMailOperation *mail_op,
1350 const GError *error = modest_mail_operation_get_error (mail_op);
1352 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1353 error->code == TNY_IO_ERROR_WRITE ||
1354 error->code == TNY_IO_ERROR_READ) {
1355 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1356 /* If the mail op has been cancelled then it's not an error: don't show any message */
1357 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1358 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1359 modest_platform_information_banner (NULL, NULL, msg);
1366 modest_header_view_set_folder (ModestHeaderView *self,
1369 ModestWindow *progress_window,
1370 RefreshAsyncUserCallback callback,
1373 ModestHeaderViewPrivate *priv;
1375 g_return_if_fail (self);
1377 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1380 if (priv->status_timeout) {
1381 g_source_remove (priv->status_timeout);
1382 priv->status_timeout = 0;
1385 g_mutex_lock (priv->observers_lock);
1386 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1387 g_object_unref (priv->folder);
1388 priv->folder = NULL;
1389 g_mutex_unlock (priv->observers_lock);
1393 GtkTreeSelection *selection;
1394 SetFolderHelper *info;
1395 ModestMailOperation *mail_op = NULL;
1397 /* Set folder in the model */
1398 modest_header_view_set_folder_intern (self, folder, refresh);
1400 /* Pick my reference. Nothing to do with the mail operation */
1401 priv->folder = g_object_ref (folder);
1403 /* Do not notify about filterings until the refresh finishes */
1404 priv->notify_status = FALSE;
1406 /* Clear the selection if exists */
1407 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1408 gtk_tree_selection_unselect_all(selection);
1409 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1411 /* Notify the observers that the update begins */
1412 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1415 /* create the helper */
1416 info = g_malloc0 (sizeof (SetFolderHelper));
1417 info->header_view = g_object_ref (self);
1418 info->cb = callback;
1419 info->user_data = user_data;
1421 /* Create the mail operation (source will be the parent widget) */
1422 if (progress_window)
1423 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1424 refresh_folder_error_handler,
1427 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1430 /* Refresh the folder asynchronously */
1431 modest_mail_operation_refresh_folder (mail_op,
1433 folder_refreshed_cb,
1436 folder_refreshed_cb (mail_op, folder, info);
1440 g_object_unref (mail_op);
1442 g_mutex_lock (priv->observers_lock);
1444 if (priv->monitor) {
1445 tny_folder_monitor_stop (priv->monitor);
1446 g_object_unref (G_OBJECT (priv->monitor));
1447 priv->monitor = NULL;
1450 if (priv->autoselect_reference) {
1451 gtk_tree_row_reference_free (priv->autoselect_reference);
1452 priv->autoselect_reference = NULL;
1455 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1457 modest_header_view_notify_observers(self, NULL, NULL);
1459 g_mutex_unlock (priv->observers_lock);
1461 /* Notify the observers that the update is over */
1462 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1468 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1469 GtkTreeViewColumn *column, gpointer userdata)
1471 ModestHeaderView *self = NULL;
1473 GtkTreeModel *model = NULL;
1474 TnyHeader *header = NULL;
1475 TnyHeaderFlags flags;
1477 self = MODEST_HEADER_VIEW (treeview);
1479 model = gtk_tree_view_get_model (treeview);
1480 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1483 /* get the first selected item */
1484 gtk_tree_model_get (model, &iter,
1485 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1486 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1489 /* Dont open DELETED messages */
1490 if (flags & TNY_HEADER_FLAG_DELETED) {
1493 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1494 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1495 modest_platform_information_banner (NULL, NULL, msg);
1501 g_signal_emit (G_OBJECT(self),
1502 signals[HEADER_ACTIVATED_SIGNAL],
1508 g_object_unref (G_OBJECT (header));
1513 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1515 GtkTreeModel *model;
1516 TnyHeader *header = NULL;
1517 GtkTreePath *path = NULL;
1519 ModestHeaderView *self;
1520 GList *selected = NULL;
1522 g_return_if_fail (sel);
1523 g_return_if_fail (user_data);
1525 self = MODEST_HEADER_VIEW (user_data);
1527 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1528 if (selected != NULL)
1529 path = (GtkTreePath *) selected->data;
1530 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1531 return; /* msg was _un_selected */
1533 gtk_tree_model_get (model, &iter,
1534 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1538 g_signal_emit (G_OBJECT(self),
1539 signals[HEADER_SELECTED_SIGNAL],
1542 g_object_unref (G_OBJECT (header));
1544 /* free all items in 'selected' */
1545 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1546 g_list_free (selected);
1550 /* PROTECTED method. It's useful when we want to force a given
1551 selection to reload a msg. For example if we have selected a header
1552 in offline mode, when Modest become online, we want to reload the
1553 message automatically without an user click over the header */
1555 _modest_header_view_change_selection (GtkTreeSelection *selection,
1558 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1559 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1561 on_selection_changed (selection, user_data);
1565 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1572 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1576 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1580 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1588 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1596 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1597 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1601 case TNY_HEADER_FLAG_ATTACHMENTS:
1603 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1604 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1605 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1606 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1608 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1609 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1611 return cmp ? cmp : t1 - t2;
1613 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1614 TnyHeader *header1 = NULL, *header2 = NULL;
1616 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1617 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1618 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1619 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1621 /* This is for making priority values respect the intuitive sort relationship
1622 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1624 if (header1 && header2) {
1625 cmp = compare_priorities (tny_header_get_priority (header1),
1626 tny_header_get_priority (header2));
1627 g_object_unref (header1);
1628 g_object_unref (header2);
1630 return cmp ? cmp : t1 - t2;
1636 return &iter1 - &iter2; /* oughhhh */
1641 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1648 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1650 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1651 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1652 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1653 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1655 /* Do not use the prefixes for sorting. Consume all the blank
1656 spaces for sorting */
1657 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1658 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1661 /* If they're equal based on subject without prefix then just
1662 sort them by length. This will show messages like this.
1669 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1676 /* Drag and drop stuff */
1678 drag_data_get_cb (GtkWidget *widget,
1679 GdkDragContext *context,
1680 GtkSelectionData *selection_data,
1685 ModestHeaderView *self = NULL;
1686 ModestHeaderViewPrivate *priv = NULL;
1688 self = MODEST_HEADER_VIEW (widget);
1689 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1691 /* Set the data. Do not use the current selection because it
1692 could be different than the selection at the beginning of
1694 modest_dnd_selection_data_set_paths (selection_data,
1695 priv->drag_begin_cached_selected_rows);
1699 * We're caching the selected rows at the beginning because the
1700 * selection could change between drag-begin and drag-data-get, for
1701 * example if we have a set of rows already selected, and then we
1702 * click in one of them (without SHIFT key pressed) and begin a drag,
1703 * the selection at that moment contains all the selected lines, but
1704 * after dropping the selection, the release event provokes that only
1705 * the row used to begin the drag is selected, so at the end the
1706 * drag&drop affects only one rows instead of all the selected ones.
1710 drag_begin_cb (GtkWidget *widget,
1711 GdkDragContext *context,
1714 ModestHeaderView *self = NULL;
1715 ModestHeaderViewPrivate *priv = NULL;
1716 GtkTreeSelection *selection;
1718 self = MODEST_HEADER_VIEW (widget);
1719 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1721 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1722 priv->drag_begin_cached_selected_rows =
1723 gtk_tree_selection_get_selected_rows (selection, NULL);
1727 * We use the drag-end signal to clear the cached selection, we use
1728 * this because this allways happens, whether or not the d&d was a
1732 drag_end_cb (GtkWidget *widget,
1736 ModestHeaderView *self = NULL;
1737 ModestHeaderViewPrivate *priv = NULL;
1739 self = MODEST_HEADER_VIEW (widget);
1740 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1742 /* Free cached data */
1743 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1744 g_list_free (priv->drag_begin_cached_selected_rows);
1745 priv->drag_begin_cached_selected_rows = NULL;
1748 /* Header view drag types */
1749 const GtkTargetEntry header_view_drag_types[] = {
1750 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1754 enable_drag_and_drop (GtkWidget *self)
1756 #ifdef MODEST_TOOLKIT_HILDON2
1759 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1760 header_view_drag_types,
1761 G_N_ELEMENTS (header_view_drag_types),
1762 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1766 disable_drag_and_drop (GtkWidget *self)
1768 #ifdef MODEST_TOOLKIT_HILDON2
1771 gtk_drag_source_unset (self);
1775 setup_drag_and_drop (GtkWidget *self)
1777 #ifdef MODEST_TOOLKIT_HILDON2
1780 enable_drag_and_drop(self);
1781 g_signal_connect(G_OBJECT (self), "drag_data_get",
1782 G_CALLBACK(drag_data_get_cb), NULL);
1784 g_signal_connect(G_OBJECT (self), "drag_begin",
1785 G_CALLBACK(drag_begin_cb), NULL);
1787 g_signal_connect(G_OBJECT (self), "drag_end",
1788 G_CALLBACK(drag_end_cb), NULL);
1791 static GtkTreePath *
1792 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1794 GtkTreePath *path = NULL;
1795 GtkTreeSelection *sel = NULL;
1798 sel = gtk_tree_view_get_selection(self);
1799 rows = gtk_tree_selection_get_selected_rows (sel, model);
1801 if ((rows == NULL) || (g_list_length(rows) != 1))
1804 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1809 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1815 #ifndef MODEST_TOOLKIT_HILDON2
1817 * This function moves the tree view scroll to the current selected
1818 * row when the widget grabs the focus
1821 on_focus_in (GtkWidget *self,
1822 GdkEventFocus *event,
1825 GtkTreeSelection *selection;
1826 GtkTreeModel *model;
1827 GList *selected = NULL;
1828 GtkTreePath *selected_path = NULL;
1830 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1834 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1835 /* If none selected yet, pick the first one */
1836 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1840 /* Return if the model is empty */
1841 if (!gtk_tree_model_get_iter_first (model, &iter))
1844 path = gtk_tree_model_get_path (model, &iter);
1845 gtk_tree_selection_select_path (selection, path);
1846 gtk_tree_path_free (path);
1849 /* Need to get the all the rows because is selection multiple */
1850 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1851 if (selected == NULL) return FALSE;
1852 selected_path = (GtkTreePath *) selected->data;
1855 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1856 g_list_free (selected);
1862 on_focus_out (GtkWidget *self,
1863 GdkEventFocus *event,
1867 if (!gtk_widget_is_focus (self)) {
1868 GtkTreeSelection *selection = NULL;
1869 GList *selected_rows = NULL;
1870 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1871 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1872 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1873 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1874 gtk_tree_selection_unselect_all (selection);
1875 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1876 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1877 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1878 g_list_free (selected_rows);
1886 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1888 enable_drag_and_drop(self);
1893 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1895 GtkTreeSelection *selection = NULL;
1896 GtkTreePath *path = NULL;
1897 gboolean already_selected = FALSE, already_opened = FALSE;
1898 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1900 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1902 GtkTreeModel *model;
1904 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1905 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1907 /* Get header from model */
1908 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1909 if (gtk_tree_model_get_iter (model, &iter, path)) {
1910 GValue value = {0,};
1913 gtk_tree_model_get_value (model, &iter,
1914 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1916 header = (TnyHeader *) g_value_get_object (&value);
1917 if (TNY_IS_HEADER (header)) {
1918 status = modest_tny_all_send_queues_get_msg_status (header);
1919 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1922 g_value_unset (&value);
1926 /* Enable drag and drop only if the user clicks on a row that
1927 it's already selected. If not, let him select items using
1928 the pointer. If the message is in an OUTBOX and in sending
1929 status disable drag and drop as well */
1930 if (!already_selected ||
1931 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1933 disable_drag_and_drop(self);
1936 gtk_tree_path_free(path);
1938 /* If it's already opened then do not let the button-press
1939 event go on because it'll perform a message open because
1940 we're clicking on to an already selected header */
1945 folder_monitor_update (TnyFolderObserver *self,
1946 TnyFolderChange *change)
1948 ModestHeaderViewPrivate *priv = NULL;
1949 TnyFolderChangeChanged changed;
1950 TnyFolder *folder = NULL;
1952 changed = tny_folder_change_get_changed (change);
1954 /* Do not notify the observers if the folder of the header
1955 view has changed before this call to the observer
1957 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1958 folder = tny_folder_change_get_folder (change);
1959 if (folder != priv->folder)
1962 MODEST_DEBUG_BLOCK (
1963 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1964 g_print ("ADDED %d/%d (r/t) \n",
1965 tny_folder_change_get_new_unread_count (change),
1966 tny_folder_change_get_new_all_count (change));
1967 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1968 g_print ("ALL COUNT %d\n",
1969 tny_folder_change_get_new_all_count (change));
1970 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1971 g_print ("UNREAD COUNT %d\n",
1972 tny_folder_change_get_new_unread_count (change));
1973 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1974 g_print ("EXPUNGED %d/%d (r/t) \n",
1975 tny_folder_change_get_new_unread_count (change),
1976 tny_folder_change_get_new_all_count (change));
1977 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1978 g_print ("FOLDER RENAME\n");
1979 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1980 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1981 tny_folder_change_get_new_unread_count (change),
1982 tny_folder_change_get_new_all_count (change));
1983 g_print ("---------------------------------------------------\n");
1986 /* Check folder count */
1987 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1988 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1990 g_mutex_lock (priv->observers_lock);
1992 /* Emit signal to evaluate how headers changes affects
1993 to the window view */
1994 g_signal_emit (G_OBJECT(self),
1995 signals[MSG_COUNT_CHANGED_SIGNAL],
1998 /* Added or removed headers, so data stored on cliboard are invalid */
1999 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
2000 modest_email_clipboard_clear (priv->clipboard);
2002 g_mutex_unlock (priv->observers_lock);
2008 g_object_unref (folder);
2012 modest_header_view_is_empty (ModestHeaderView *self)
2014 ModestHeaderViewPrivate *priv;
2016 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2018 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2020 return priv->status == HEADER_VIEW_EMPTY;
2024 modest_header_view_clear (ModestHeaderView *self)
2026 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2028 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2032 modest_header_view_copy_selection (ModestHeaderView *header_view)
2034 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2036 /* Copy selection */
2037 _clipboard_set_selected_data (header_view, FALSE);
2041 modest_header_view_cut_selection (ModestHeaderView *header_view)
2043 ModestHeaderViewPrivate *priv = NULL;
2044 const gchar **hidding = NULL;
2045 guint i, n_selected;
2047 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2049 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2051 /* Copy selection */
2052 _clipboard_set_selected_data (header_view, TRUE);
2054 /* Get hidding ids */
2055 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2057 /* Clear hidding array created by previous cut operation */
2058 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2060 /* Copy hidding array */
2061 priv->n_selected = n_selected;
2062 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2063 for (i=0; i < n_selected; i++)
2064 priv->hidding_ids[i] = g_strdup(hidding[i]);
2066 /* Hide cut headers */
2067 modest_header_view_refilter (header_view);
2074 _clipboard_set_selected_data (ModestHeaderView *header_view,
2077 ModestHeaderViewPrivate *priv = NULL;
2078 TnyList *headers = NULL;
2080 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2081 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2083 /* Set selected data on clipboard */
2084 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2085 headers = modest_header_view_get_selected_headers (header_view);
2086 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2089 g_object_unref (headers);
2093 ModestHeaderView *self;
2098 notify_filter_change (gpointer data)
2100 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2102 g_signal_emit (info->self,
2103 signals[MSG_COUNT_CHANGED_SIGNAL],
2104 0, info->folder, NULL);
2110 notify_filter_change_destroy (gpointer data)
2112 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2113 ModestHeaderViewPrivate *priv;
2115 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2116 priv->status_timeout = 0;
2118 g_object_unref (info->self);
2119 g_object_unref (info->folder);
2120 g_slice_free (NotifyFilterInfo, info);
2124 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2126 /* For the moment we only need to filter outbox */
2127 return priv->is_outbox;
2131 header_match_string (TnyHeader *header, gchar **words)
2139 gchar **current_word;
2142 subject = tny_header_dup_subject (header);
2143 cc = tny_header_dup_cc (header);
2144 bcc = tny_header_dup_bcc (header);
2145 to = tny_header_dup_to (header);
2146 from = tny_header_dup_from (header);
2150 for (current_word = words; *current_word != NULL; current_word++) {
2152 if ((subject && g_strstr_len (subject, -1, *current_word))
2153 || (cc && g_strstr_len (cc, -1, *current_word))
2154 || (bcc && g_strstr_len (bcc, -1, *current_word))
2155 || (to && g_strstr_len (to, -1, *current_word))
2156 || (from && g_strstr_len (from, -1, *current_word))) {
2174 filter_row (GtkTreeModel *model,
2178 ModestHeaderViewPrivate *priv = NULL;
2179 TnyHeaderFlags flags;
2180 TnyHeader *header = NULL;
2183 gboolean visible = TRUE;
2184 gboolean found = FALSE;
2185 GValue value = {0,};
2186 HeaderViewStatus old_status;
2188 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2189 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2191 /* Get header from model */
2192 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2193 flags = (TnyHeaderFlags) g_value_get_int (&value);
2194 g_value_unset (&value);
2195 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2196 header = (TnyHeader *) g_value_get_object (&value);
2197 g_value_unset (&value);
2199 /* Get message id from header (ensure is a valid id) */
2205 /* Hide deleted and mark as deleted heders */
2206 if (flags & TNY_HEADER_FLAG_DELETED ||
2207 flags & TNY_HEADER_FLAG_EXPUNGED) {
2212 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2213 if (current_folder_needs_filtering (priv) &&
2214 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2220 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2221 if (current_folder_needs_filtering (priv) &&
2222 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2228 if (visible && priv->filter_string) {
2229 if (!header_match_string (header, priv->filter_string_splitted)) {
2235 /* If no data on clipboard, return always TRUE */
2236 if (modest_email_clipboard_cleared(priv->clipboard)) {
2242 if (priv->hidding_ids != NULL) {
2243 id = tny_header_dup_message_id (header);
2244 for (i=0; i < priv->n_selected && !found; i++)
2245 if (priv->hidding_ids[i] != NULL && id != NULL)
2246 found = (!strcmp (priv->hidding_ids[i], id));
2253 old_status = priv->status;
2254 priv->status = ((gboolean) priv->status) && !visible;
2255 if ((priv->notify_status) && (priv->status != old_status)) {
2256 if (priv->status_timeout)
2257 g_source_remove (priv->status_timeout);
2260 NotifyFilterInfo *info;
2262 info = g_slice_new0 (NotifyFilterInfo);
2263 info->self = g_object_ref (G_OBJECT (user_data));
2265 info->folder = tny_header_get_folder (header);
2266 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2267 notify_filter_change,
2269 notify_filter_change_destroy);
2277 _clear_hidding_filter (ModestHeaderView *header_view)
2279 ModestHeaderViewPrivate *priv = NULL;
2282 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2283 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2285 if (priv->hidding_ids != NULL) {
2286 for (i=0; i < priv->n_selected; i++)
2287 g_free (priv->hidding_ids[i]);
2288 g_free(priv->hidding_ids);
2293 modest_header_view_refilter (ModestHeaderView *header_view)
2295 GtkTreeModel *model, *sortable = NULL;
2296 ModestHeaderViewPrivate *priv = NULL;
2298 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2299 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2301 /* Hide cut headers */
2302 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2303 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2304 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2305 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2306 priv->status = HEADER_VIEW_INIT;
2307 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2313 * Called when an account is removed. If I'm showing a folder of the
2314 * account that has been removed then clear the view
2317 on_account_removed (TnyAccountStore *self,
2318 TnyAccount *account,
2321 ModestHeaderViewPrivate *priv = NULL;
2323 /* Ignore changes in transport accounts */
2324 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2327 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2330 TnyAccount *my_account;
2332 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2333 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2334 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2335 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2337 my_account = tny_folder_get_account (priv->folder);
2341 if (my_account == account)
2342 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2343 g_object_unref (my_account);
2349 modest_header_view_add_observer(ModestHeaderView *header_view,
2350 ModestHeaderViewObserver *observer)
2352 ModestHeaderViewPrivate *priv;
2354 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2355 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2357 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2359 g_mutex_lock(priv->observer_list_lock);
2360 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2361 g_mutex_unlock(priv->observer_list_lock);
2365 modest_header_view_remove_observer(ModestHeaderView *header_view,
2366 ModestHeaderViewObserver *observer)
2368 ModestHeaderViewPrivate *priv;
2370 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2371 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2373 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2375 g_mutex_lock(priv->observer_list_lock);
2376 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2377 g_mutex_unlock(priv->observer_list_lock);
2381 modest_header_view_notify_observers(ModestHeaderView *header_view,
2382 GtkTreeModel *model,
2383 const gchar *tny_folder_id)
2385 ModestHeaderViewPrivate *priv = NULL;
2387 ModestHeaderViewObserver *observer;
2390 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2392 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2394 g_mutex_lock(priv->observer_list_lock);
2395 iter = priv->observer_list;
2396 while(iter != NULL){
2397 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2398 modest_header_view_observer_update(observer, model,
2400 iter = g_slist_next(iter);
2402 g_mutex_unlock(priv->observer_list_lock);
2406 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2408 ModestHeaderViewPrivate *priv = NULL;
2410 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2411 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2415 modest_header_view_set_filter (ModestHeaderView *self,
2416 ModestHeaderViewFilter filter)
2418 ModestHeaderViewPrivate *priv;
2420 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2421 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2423 priv->filter |= filter;
2425 if (current_folder_needs_filtering (priv))
2426 modest_header_view_refilter (self);
2430 modest_header_view_unset_filter (ModestHeaderView *self,
2431 ModestHeaderViewFilter filter)
2433 ModestHeaderViewPrivate *priv;
2435 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2436 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2438 priv->filter &= ~filter;
2440 if (current_folder_needs_filtering (priv))
2441 modest_header_view_refilter (self);
2445 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2447 if (strcmp ("style", spec->name) == 0) {
2448 update_style (MODEST_HEADER_VIEW (obj));
2449 gtk_widget_queue_draw (GTK_WIDGET (obj));
2454 update_style (ModestHeaderView *self)
2456 ModestHeaderViewPrivate *priv;
2457 GdkColor style_color;
2458 GdkColor style_active_color;
2459 PangoAttrList *attr_list;
2461 PangoAttribute *attr;
2463 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2464 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2468 attr_list = pango_attr_list_new ();
2469 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2470 gdk_color_parse ("grey", &style_color);
2472 priv->secondary_color = style_color;
2473 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2474 pango_attr_list_insert (attr_list, attr);
2477 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2479 "SmallSystemFont", NULL,
2482 attr = pango_attr_font_desc_new (pango_font_description_copy
2483 (style->font_desc));
2484 pango_attr_list_insert (attr_list, attr);
2486 g_object_set (G_OBJECT (priv->renderer_address),
2487 "foreground-gdk", &(priv->secondary_color),
2488 "foreground-set", TRUE,
2489 "attributes", attr_list,
2491 g_object_set (G_OBJECT (priv->renderer_date_status),
2492 "foreground-gdk", &(priv->secondary_color),
2493 "foreground-set", TRUE,
2494 "attributes", attr_list,
2496 pango_attr_list_unref (attr_list);
2498 g_object_set (G_OBJECT (priv->renderer_address),
2499 "foreground-gdk", &(priv->secondary_color),
2500 "foreground-set", TRUE,
2501 "scale", PANGO_SCALE_SMALL,
2504 g_object_set (G_OBJECT (priv->renderer_date_status),
2505 "foreground-gdk", &(priv->secondary_color),
2506 "foreground-set", TRUE,
2507 "scale", PANGO_SCALE_SMALL,
2512 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2513 priv->active_color = style_active_color;
2514 #ifdef MODEST_TOOLKIT_HILDON2
2515 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2516 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2519 #ifdef MODEST_TOOLKIT_HILDON2
2520 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2526 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2531 GtkTreeModel *tree_model;
2536 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2544 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2545 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2549 gtk_tree_model_get (tree_model, &iter,
2550 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2557 modest_header_view_set_show_latest (ModestHeaderView *header_view,
2560 ModestHeaderViewPrivate *priv;
2561 GtkTreeModel *sortable, *filter, *model;
2563 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2564 priv->show_latest = show_latest;
2566 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2567 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2568 filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2569 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2570 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2572 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (model), priv->show_latest);
2579 modest_header_view_get_show_latest (ModestHeaderView *header_view)
2581 ModestHeaderViewPrivate *priv;
2582 GtkTreeModel *sortable, *filter, *model;
2585 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2587 result = priv->show_latest;
2588 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2589 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2590 filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2591 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2592 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2594 result = tny_gtk_header_list_model_get_show_latest (TNY_GTK_HEADER_LIST_MODEL (model));
2603 modest_header_view_get_not_latest (ModestHeaderView *header_view)
2605 ModestHeaderViewPrivate *priv;
2606 gint not_latest = 0;
2607 GtkTreeModel *sortable, *filter, *model;
2609 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2611 if (priv->show_latest == 0)
2614 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2615 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2616 filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2617 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2618 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2620 not_latest = MAX (0, tny_list_get_length (TNY_LIST (model)) - priv->show_latest);
2629 modest_header_view_set_filter_string (ModestHeaderView *self,
2630 const gchar *filter_string)
2632 ModestHeaderViewPrivate *priv;
2634 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2635 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2637 if (priv->filter_string)
2638 g_free (priv->filter_string);
2640 priv->filter_string = g_strdup (filter_string);
2642 if (priv->filter_string_splitted) {
2643 g_strfreev (priv->filter_string_splitted);
2644 priv->filter_string_splitted = NULL;
2647 if (priv->filter_string) {
2648 priv->filter_string_splitted = g_strsplit (priv->filter_string, " ", 0);
2650 modest_header_view_refilter (MODEST_HEADER_VIEW (self));