1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
36 #include <tny-merge-folder.h>
39 #include <modest-header-view.h>
40 #include <modest-header-view-priv.h>
41 #include <modest-dnd.h>
42 #include <modest-tny-folder.h>
43 #include <modest-debug.h>
44 #include <modest-main-window.h>
45 #include <modest-ui-actions.h>
46 #include <modest-marshal.h>
47 #include <modest-text-utils.h>
48 #include <modest-icon-names.h>
49 #include <modest-runtime.h>
50 #include "modest-platform.h"
51 #include <modest-hbox-cell-renderer.h>
52 #include <modest-vbox-cell-renderer.h>
53 #include <modest-datetime-formatter.h>
54 #include <modest-ui-constants.h>
56 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
57 static void modest_header_view_init (ModestHeaderView *obj);
58 static void modest_header_view_finalize (GObject *obj);
59 static void modest_header_view_dispose (GObject *obj);
61 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
62 GtkTreeViewColumn *column, gpointer userdata);
64 static gint cmp_rows (GtkTreeModel *tree_model,
69 static gint cmp_subject_rows (GtkTreeModel *tree_model,
74 static gboolean filter_row (GtkTreeModel *model,
78 static void on_account_removed (TnyAccountStore *self,
82 static void on_selection_changed (GtkTreeSelection *sel,
85 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
88 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
91 static void setup_drag_and_drop (GtkWidget *self);
93 static void enable_drag_and_drop (GtkWidget *self);
95 static void disable_drag_and_drop (GtkWidget *self);
97 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
99 #ifndef MODEST_TOOLKIT_HILDON2
100 static gboolean on_focus_in (GtkWidget *sef,
101 GdkEventFocus *event,
104 static gboolean on_focus_out (GtkWidget *self,
105 GdkEventFocus *event,
109 static void folder_monitor_update (TnyFolderObserver *self,
110 TnyFolderChange *change);
112 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
114 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
116 static void _clear_hidding_filter (ModestHeaderView *header_view);
118 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
120 const gchar *tny_folder_id);
122 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
123 GdkEventExpose *event,
126 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
127 static void update_style (ModestHeaderView *self);
130 HEADER_VIEW_NON_EMPTY,
135 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
136 struct _ModestHeaderViewPrivate {
138 ModestHeaderViewStyle style;
141 TnyFolderMonitor *monitor;
142 GMutex *observers_lock;
144 /*header-view-observer observer*/
145 GMutex *observer_list_lock;
146 GSList *observer_list;
148 /* not unref this object, its a singlenton */
149 ModestEmailClipboard *clipboard;
151 /* Filter tree model */
154 GtkTreeRowReference *autoselect_reference;
155 ModestHeaderViewFilter filter;
157 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
158 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
160 gulong selection_changed_handler;
161 gulong acc_removed_handler;
163 GList *drag_begin_cached_selected_rows;
165 HeaderViewStatus status;
166 guint status_timeout;
167 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
169 ModestDatetimeFormatter *datetime_formatter;
171 GtkCellRenderer *renderer_subject;
172 GtkCellRenderer *renderer_address;
173 GtkCellRenderer *renderer_date_status;
175 GdkColor active_color;
176 GdkColor secondary_color;
179 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
180 struct _HeadersCountChangedHelper {
181 ModestHeaderView *self;
182 TnyFolderChange *change;
186 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
187 MODEST_TYPE_HEADER_VIEW, \
188 ModestHeaderViewPrivate))
192 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
195 HEADER_SELECTED_SIGNAL,
196 HEADER_ACTIVATED_SIGNAL,
197 ITEM_NOT_FOUND_SIGNAL,
198 MSG_COUNT_CHANGED_SIGNAL,
199 UPDATING_MSG_LIST_SIGNAL,
204 static GObjectClass *parent_class = NULL;
206 /* uncomment the following if you have defined any signals */
207 static guint signals[LAST_SIGNAL] = {0};
210 modest_header_view_get_type (void)
212 static GType my_type = 0;
214 static const GTypeInfo my_info = {
215 sizeof(ModestHeaderViewClass),
216 NULL, /* base init */
217 NULL, /* base finalize */
218 (GClassInitFunc) modest_header_view_class_init,
219 NULL, /* class finalize */
220 NULL, /* class data */
221 sizeof(ModestHeaderView),
223 (GInstanceInitFunc) modest_header_view_init,
227 static const GInterfaceInfo tny_folder_observer_info =
229 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
230 NULL, /* interface_finalize */
231 NULL /* interface_data */
233 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
237 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
238 &tny_folder_observer_info);
246 modest_header_view_class_init (ModestHeaderViewClass *klass)
248 GObjectClass *gobject_class;
249 gobject_class = (GObjectClass*) klass;
251 parent_class = g_type_class_peek_parent (klass);
252 gobject_class->finalize = modest_header_view_finalize;
253 gobject_class->dispose = modest_header_view_dispose;
255 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
257 signals[HEADER_SELECTED_SIGNAL] =
258 g_signal_new ("header_selected",
259 G_TYPE_FROM_CLASS (gobject_class),
261 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
263 g_cclosure_marshal_VOID__POINTER,
264 G_TYPE_NONE, 1, G_TYPE_POINTER);
266 signals[HEADER_ACTIVATED_SIGNAL] =
267 g_signal_new ("header_activated",
268 G_TYPE_FROM_CLASS (gobject_class),
270 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
272 gtk_marshal_VOID__POINTER_POINTER,
273 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
276 signals[ITEM_NOT_FOUND_SIGNAL] =
277 g_signal_new ("item_not_found",
278 G_TYPE_FROM_CLASS (gobject_class),
280 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
282 g_cclosure_marshal_VOID__INT,
283 G_TYPE_NONE, 1, G_TYPE_INT);
285 signals[MSG_COUNT_CHANGED_SIGNAL] =
286 g_signal_new ("msg_count_changed",
287 G_TYPE_FROM_CLASS (gobject_class),
289 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
291 modest_marshal_VOID__POINTER_POINTER,
292 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
294 signals[UPDATING_MSG_LIST_SIGNAL] =
295 g_signal_new ("updating-msg-list",
296 G_TYPE_FROM_CLASS (gobject_class),
298 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
300 g_cclosure_marshal_VOID__BOOLEAN,
301 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
303 #ifdef MODEST_TOOLKIT_HILDON2
304 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
310 tny_folder_observer_init (TnyFolderObserverIface *klass)
312 klass->update = folder_monitor_update;
315 static GtkTreeViewColumn*
316 get_new_column (const gchar *name, GtkCellRenderer *renderer,
317 gboolean resizable, gint sort_col_id, gboolean show_as_text,
318 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
320 GtkTreeViewColumn *column;
322 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
323 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
325 gtk_tree_view_column_set_resizable (column, resizable);
327 gtk_tree_view_column_set_expand (column, TRUE);
330 gtk_tree_view_column_add_attribute (column, renderer, "text",
332 if (sort_col_id >= 0)
333 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
335 gtk_tree_view_column_set_sort_indicator (column, FALSE);
336 gtk_tree_view_column_set_reorderable (column, TRUE);
339 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
346 remove_all_columns (ModestHeaderView *obj)
348 GList *columns, *cursor;
350 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
352 for (cursor = columns; cursor; cursor = cursor->next)
353 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
354 GTK_TREE_VIEW_COLUMN(cursor->data));
355 g_list_free (columns);
359 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
361 GtkTreeModel *sortable;
362 GtkTreeViewColumn *column=NULL;
363 GtkTreeSelection *selection = NULL;
364 GtkCellRenderer *renderer_header,
365 *renderer_attach, *renderer_compact_date_or_status;
366 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
367 *renderer_subject_box, *renderer_recpt,
369 ModestHeaderViewPrivate *priv;
370 GtkTreeViewColumn *compact_column = NULL;
373 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
374 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
376 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
378 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
380 /* TODO: check whether these renderers need to be freed */
381 renderer_attach = gtk_cell_renderer_pixbuf_new ();
382 renderer_priority = gtk_cell_renderer_pixbuf_new ();
383 renderer_header = gtk_cell_renderer_text_new ();
385 renderer_compact_header = modest_vbox_cell_renderer_new ();
386 renderer_recpt_box = modest_hbox_cell_renderer_new ();
387 renderer_subject_box = modest_hbox_cell_renderer_new ();
388 renderer_recpt = gtk_cell_renderer_text_new ();
389 priv->renderer_address = renderer_recpt;
390 priv->renderer_subject = gtk_cell_renderer_text_new ();
391 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
392 priv->renderer_date_status = renderer_compact_date_or_status;
394 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
395 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
396 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
397 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
398 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
399 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
400 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
401 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
402 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
403 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
404 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
405 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
406 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
407 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
409 #ifdef MODEST_TOOLKIT_HILDON2
410 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
412 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
413 #ifndef MODEST_TOOLKIT_GTK
414 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
415 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
417 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
418 g_object_set(G_OBJECT(renderer_header),
419 "ellipsize", PANGO_ELLIPSIZE_END,
421 g_object_set (G_OBJECT (priv->renderer_subject),
422 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
424 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
425 g_object_set (G_OBJECT (renderer_recpt),
426 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
428 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
429 g_object_set(G_OBJECT(renderer_compact_date_or_status),
430 "xalign", 1.0, "yalign", 0.1,
432 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
433 #ifdef MODEST_TOOLKIT_HILDON2
434 g_object_set (G_OBJECT (renderer_priority),
436 "xalign", 0.0, NULL);
437 g_object_set (G_OBJECT (renderer_attach),
439 "xalign", 0.0, NULL);
441 g_object_set (G_OBJECT (renderer_priority),
442 "yalign", 0.5, NULL);
443 g_object_set (G_OBJECT (renderer_attach),
444 "yalign", 0.0, NULL);
447 #ifdef MODEST_TOOLKIT_HILDON1
448 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
449 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
450 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
451 #elif MODEST_TOOLKIT_HILDON2
452 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
453 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
454 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
456 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
457 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
458 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
461 remove_all_columns (self);
463 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
464 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
465 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
467 /* Add new columns */
468 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
469 ModestHeaderViewColumn col =
470 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
472 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
473 g_printerr ("modest: invalid column %d in column list\n", col);
479 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
480 column = get_new_column (_("A"), renderer_attach, FALSE,
481 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
483 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
485 gtk_tree_view_column_set_fixed_width (column, 45);
489 case MODEST_HEADER_VIEW_COLUMN_FROM:
490 column = get_new_column (_("From"), renderer_header, TRUE,
491 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
493 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
494 GINT_TO_POINTER(TRUE));
497 case MODEST_HEADER_VIEW_COLUMN_TO:
498 column = get_new_column (_("To"), renderer_header, TRUE,
499 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
501 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
502 GINT_TO_POINTER(FALSE));
505 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
506 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
507 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
509 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
510 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
511 compact_column = column;
514 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
515 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
516 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
518 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
519 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
520 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
521 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
522 compact_column = column;
526 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
527 column = get_new_column (_("Subject"), renderer_header, TRUE,
528 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
530 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
534 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
535 column = get_new_column (_("Received"), renderer_header, TRUE,
536 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
538 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
539 GINT_TO_POINTER(TRUE));
542 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
543 column = get_new_column (_("Sent"), renderer_header, TRUE,
544 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
546 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
547 GINT_TO_POINTER(FALSE));
550 case MODEST_HEADER_VIEW_COLUMN_SIZE:
551 column = get_new_column (_("Size"), renderer_header, TRUE,
552 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
554 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
557 case MODEST_HEADER_VIEW_COLUMN_STATUS:
558 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
559 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
561 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
566 g_return_val_if_reached(FALSE);
569 /* we keep the column id around */
570 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
571 GINT_TO_POINTER(col));
573 /* we need this ptr when sorting the rows */
574 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
576 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
580 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
581 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
582 (GtkTreeIterCompareFunc) cmp_rows,
583 compact_column, NULL);
584 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
585 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
586 (GtkTreeIterCompareFunc) cmp_subject_rows,
587 compact_column, NULL);
591 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
597 datetime_format_changed (ModestDatetimeFormatter *formatter,
598 ModestHeaderView *self)
600 gtk_widget_queue_draw (GTK_WIDGET (self));
604 modest_header_view_init (ModestHeaderView *obj)
606 ModestHeaderViewPrivate *priv;
609 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
612 priv->is_outbox = FALSE;
614 priv->monitor = NULL;
615 priv->observers_lock = g_mutex_new ();
616 priv->autoselect_reference = NULL;
618 priv->status = HEADER_VIEW_INIT;
619 priv->status_timeout = 0;
620 priv->notify_status = TRUE;
622 priv->observer_list_lock = g_mutex_new();
623 priv->observer_list = NULL;
625 priv->clipboard = modest_runtime_get_email_clipboard ();
626 priv->hidding_ids = NULL;
627 priv->n_selected = 0;
628 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
629 priv->selection_changed_handler = 0;
630 priv->acc_removed_handler = 0;
632 /* Sort parameters */
633 for (j=0; j < 2; j++) {
634 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
635 priv->sort_colid[j][i] = -1;
636 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
640 priv->datetime_formatter = modest_datetime_formatter_new ();
641 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
642 G_CALLBACK (datetime_format_changed), (gpointer) obj);
644 setup_drag_and_drop (GTK_WIDGET(obj));
648 modest_header_view_dispose (GObject *obj)
650 ModestHeaderView *self;
651 ModestHeaderViewPrivate *priv;
652 GtkTreeSelection *sel;
654 self = MODEST_HEADER_VIEW(obj);
655 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
657 if (priv->datetime_formatter) {
658 g_object_unref (priv->datetime_formatter);
659 priv->datetime_formatter = NULL;
662 /* Free in the dispose to avoid unref cycles */
664 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
665 g_object_unref (G_OBJECT (priv->folder));
669 /* We need to do this here in the dispose because the
670 selection won't exist when finalizing */
671 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
672 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
673 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
674 priv->selection_changed_handler = 0;
677 G_OBJECT_CLASS(parent_class)->dispose (obj);
681 modest_header_view_finalize (GObject *obj)
683 ModestHeaderView *self;
684 ModestHeaderViewPrivate *priv;
686 self = MODEST_HEADER_VIEW(obj);
687 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
689 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
690 priv->acc_removed_handler)) {
691 g_signal_handler_disconnect (modest_runtime_get_account_store (),
692 priv->acc_removed_handler);
695 /* There is no need to lock because there should not be any
696 * reference to self now. */
697 g_mutex_free(priv->observer_list_lock);
698 g_slist_free(priv->observer_list);
700 g_mutex_lock (priv->observers_lock);
702 tny_folder_monitor_stop (priv->monitor);
703 g_object_unref (G_OBJECT (priv->monitor));
705 g_mutex_unlock (priv->observers_lock);
706 g_mutex_free (priv->observers_lock);
708 /* Clear hidding array created by cut operation */
709 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
711 if (priv->autoselect_reference != NULL) {
712 gtk_tree_row_reference_free (priv->autoselect_reference);
713 priv->autoselect_reference = NULL;
716 G_OBJECT_CLASS(parent_class)->finalize (obj);
721 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
724 GtkTreeSelection *sel;
725 ModestHeaderView *self;
726 ModestHeaderViewPrivate *priv;
728 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
731 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
732 self = MODEST_HEADER_VIEW(obj);
733 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
735 modest_header_view_set_style (self, style);
737 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
738 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
739 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
741 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
742 TRUE); /* alternating row colors */
744 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
745 priv->selection_changed_handler =
746 g_signal_connect_after (sel, "changed",
747 G_CALLBACK(on_selection_changed), self);
749 g_signal_connect (self, "row-activated",
750 G_CALLBACK (on_header_row_activated), NULL);
752 #ifndef MODEST_TOOLKIT_HILDON2
753 g_signal_connect (self, "focus-in-event",
754 G_CALLBACK(on_focus_in), NULL);
755 g_signal_connect (self, "focus-out-event",
756 G_CALLBACK(on_focus_out), NULL);
759 g_signal_connect (self, "button-press-event",
760 G_CALLBACK(on_button_press_event), NULL);
761 g_signal_connect (self, "button-release-event",
762 G_CALLBACK(on_button_release_event), NULL);
764 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
766 G_CALLBACK (on_account_removed),
769 g_signal_connect (self, "expose-event",
770 G_CALLBACK(modest_header_view_on_expose_event),
773 return GTK_WIDGET(self);
778 modest_header_view_count_selected_headers (ModestHeaderView *self)
780 GtkTreeSelection *sel;
783 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
785 /* Get selection object and check selected rows count */
786 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
787 selected_rows = gtk_tree_selection_count_selected_rows (sel);
789 return selected_rows;
793 modest_header_view_has_selected_headers (ModestHeaderView *self)
795 GtkTreeSelection *sel;
798 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
800 /* Get selection object and check selected rows count */
801 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
802 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
809 modest_header_view_get_selected_headers (ModestHeaderView *self)
811 GtkTreeSelection *sel;
812 TnyList *header_list = NULL;
814 GList *list, *tmp = NULL;
815 GtkTreeModel *tree_model = NULL;
818 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
821 /* Get selected rows */
822 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
823 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
826 header_list = tny_simple_list_new();
828 list = g_list_reverse (list);
831 /* get header from selection */
832 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
833 gtk_tree_model_get (tree_model, &iter,
834 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
836 /* Prepend to list */
837 tny_list_prepend (header_list, G_OBJECT (header));
838 g_object_unref (G_OBJECT (header));
840 tmp = g_list_next (tmp);
843 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
850 /* scroll our list view so the selected item is visible */
852 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
854 #ifdef MODEST_TOOLKIT_GTK
856 GtkTreePath *selected_path;
857 GtkTreePath *start, *end;
861 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
862 selected_path = gtk_tree_model_get_path (model, iter);
864 start = gtk_tree_path_new ();
865 end = gtk_tree_path_new ();
867 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
869 if (gtk_tree_path_compare (selected_path, start) < 0 ||
870 gtk_tree_path_compare (end, selected_path) < 0)
871 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
872 selected_path, NULL, TRUE,
875 gtk_tree_path_free (selected_path);
876 gtk_tree_path_free (start);
877 gtk_tree_path_free (end);
879 #endif /* MODEST_TOOLKIT_GTK */
884 modest_header_view_select_next (ModestHeaderView *self)
886 GtkTreeSelection *sel;
891 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
893 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
894 path = get_selected_row (GTK_TREE_VIEW(self), &model);
895 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
896 /* Unselect previous path */
897 gtk_tree_selection_unselect_path (sel, path);
899 /* Move path down and selects new one */
900 if (gtk_tree_model_iter_next (model, &iter)) {
901 gtk_tree_selection_select_iter (sel, &iter);
902 scroll_to_selected (self, &iter, FALSE);
904 gtk_tree_path_free(path);
910 modest_header_view_select_prev (ModestHeaderView *self)
912 GtkTreeSelection *sel;
917 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
919 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
920 path = get_selected_row (GTK_TREE_VIEW(self), &model);
921 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
922 /* Unselect previous path */
923 gtk_tree_selection_unselect_path (sel, path);
926 if (gtk_tree_path_prev (path)) {
927 gtk_tree_model_get_iter (model, &iter, path);
929 /* Select the new one */
930 gtk_tree_selection_select_iter (sel, &iter);
931 scroll_to_selected (self, &iter, TRUE);
934 gtk_tree_path_free (path);
939 modest_header_view_get_columns (ModestHeaderView *self)
941 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
943 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
949 modest_header_view_set_style (ModestHeaderView *self,
950 ModestHeaderViewStyle style)
952 ModestHeaderViewPrivate *priv;
953 gboolean show_col_headers = FALSE;
954 ModestHeaderViewStyle old_style;
956 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
957 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
960 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
961 if (priv->style == style)
962 return TRUE; /* nothing to do */
965 case MODEST_HEADER_VIEW_STYLE_DETAILS:
966 show_col_headers = TRUE;
968 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
971 g_return_val_if_reached (FALSE);
973 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
974 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
976 old_style = priv->style;
983 ModestHeaderViewStyle
984 modest_header_view_get_style (ModestHeaderView *self)
986 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
988 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
991 /* This is used to automatically select the first header if the user
992 * has not selected any header yet.
995 modest_header_view_on_expose_event(GtkTreeView *header_view,
996 GdkEventExpose *event,
999 GtkTreeSelection *sel;
1000 GtkTreeModel *model;
1001 GtkTreeIter tree_iter;
1002 ModestHeaderViewPrivate *priv;
1004 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1005 model = gtk_tree_view_get_model(header_view);
1010 #ifdef MODEST_TOOLKIT_HILDON2
1013 sel = gtk_tree_view_get_selection(header_view);
1014 if(!gtk_tree_selection_count_selected_rows(sel)) {
1015 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1016 GtkTreePath *tree_iter_path;
1017 /* Prevent the widget from getting the focus
1018 when selecting the first item */
1019 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1020 g_object_set(header_view, "can-focus", FALSE, NULL);
1021 gtk_tree_selection_select_iter(sel, &tree_iter);
1022 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1023 g_object_set(header_view, "can-focus", TRUE, NULL);
1024 if (priv->autoselect_reference) {
1025 gtk_tree_row_reference_free (priv->autoselect_reference);
1027 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1028 gtk_tree_path_free (tree_iter_path);
1031 if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
1032 gboolean moved_selection = FALSE;
1033 GtkTreePath * last_path;
1034 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1035 moved_selection = TRUE;
1039 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1040 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1041 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1042 moved_selection = TRUE;
1043 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1045 gtk_tree_path_free (last_path);
1047 if (moved_selection) {
1048 gtk_tree_row_reference_free (priv->autoselect_reference);
1049 priv->autoselect_reference = NULL;
1052 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1053 GtkTreePath *current_path;
1054 current_path = gtk_tree_model_get_path (model, &tree_iter);
1055 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1056 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1057 g_object_set(header_view, "can-focus", FALSE, NULL);
1058 gtk_tree_selection_unselect_all (sel);
1059 gtk_tree_selection_select_iter(sel, &tree_iter);
1060 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1061 g_object_set(header_view, "can-focus", TRUE, NULL);
1062 gtk_tree_row_reference_free (priv->autoselect_reference);
1063 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1065 gtk_tree_path_free (current_path);
1066 gtk_tree_path_free (last_path);
1076 modest_header_view_get_folder (ModestHeaderView *self)
1078 ModestHeaderViewPrivate *priv;
1080 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1082 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1085 g_object_ref (priv->folder);
1087 return priv->folder;
1091 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1097 ModestHeaderView *self;
1098 ModestHeaderViewPrivate *priv;
1100 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1102 self = MODEST_HEADER_VIEW (user_data);
1103 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1105 if (cancelled || err)
1108 /* Add IDLE observer (monitor) and another folder observer for
1109 new messages (self) */
1110 g_mutex_lock (priv->observers_lock);
1111 if (priv->monitor) {
1112 tny_folder_monitor_stop (priv->monitor);
1113 g_object_unref (G_OBJECT (priv->monitor));
1115 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1116 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1117 tny_folder_monitor_start (priv->monitor);
1118 g_mutex_unlock (priv->observers_lock);
1122 modest_header_view_set_folder_intern (ModestHeaderView *self,
1128 ModestHeaderViewPrivate *priv;
1129 GList *cols, *cursor;
1130 GtkTreeModel *filter_model, *sortable;
1132 GtkSortType sort_type;
1134 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1136 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1138 /* Start the monitor in the callback of the
1139 tny_gtk_header_list_model_set_folder call. It's crucial to
1140 do it there and not just after the call because we want the
1141 monitor to observe only the headers returned by the
1142 tny_folder_get_headers_async call that it's inside the
1143 tny_gtk_header_list_model_set_folder call. This way the
1144 monitor infrastructure could successfully cope with
1145 duplicates. For example if a tny_folder_add_msg_async is
1146 happening while tny_gtk_header_list_model_set_folder is
1147 invoked, then the first call could add a header that will
1148 be added again by tny_gtk_header_list_model_set_folder, so
1149 we'd end up with duplicate headers. sergio */
1150 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1152 set_folder_intern_get_headers_async_cb,
1155 /* Create a tree model filter to hide and show rows for cut operations */
1156 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1157 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1158 filter_row, self, NULL);
1159 g_object_unref (headers);
1161 /* Init filter_row function to examine empty status */
1162 priv->status = HEADER_VIEW_INIT;
1164 /* Create sortable model */
1165 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1166 g_object_unref (filter_model);
1168 /* install our special sorting functions */
1169 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1171 /* Restore sort column id */
1173 type = modest_tny_folder_guess_folder_type (folder);
1174 if (type == TNY_FOLDER_TYPE_INVALID)
1175 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1177 sort_colid = modest_header_view_get_sort_column_id (self, type);
1178 sort_type = modest_header_view_get_sort_type (self, type);
1179 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1182 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1183 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1184 (GtkTreeIterCompareFunc) cmp_rows,
1186 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1187 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1188 (GtkTreeIterCompareFunc) cmp_subject_rows,
1193 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1194 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1195 g_object_unref (sortable);
1202 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1204 GtkSortType sort_type)
1206 ModestHeaderViewPrivate *priv = NULL;
1207 GtkTreeModel *sortable = NULL;
1210 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1211 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1213 /* Get model and private data */
1214 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1215 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1217 /* Sort tree model */
1218 type = modest_tny_folder_guess_folder_type (priv->folder);
1219 if (type == TNY_FOLDER_TYPE_INVALID)
1220 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1222 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1225 /* Store new sort parameters */
1226 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1231 modest_header_view_set_sort_params (ModestHeaderView *self,
1233 GtkSortType sort_type,
1236 ModestHeaderViewPrivate *priv;
1237 ModestHeaderViewStyle style;
1239 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1240 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1241 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1243 style = modest_header_view_get_style (self);
1244 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1246 priv->sort_colid[style][type] = sort_colid;
1247 priv->sort_type[style][type] = sort_type;
1251 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1254 ModestHeaderViewPrivate *priv;
1255 ModestHeaderViewStyle style;
1257 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1258 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1260 style = modest_header_view_get_style (self);
1261 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1263 return priv->sort_colid[style][type];
1267 modest_header_view_get_sort_type (ModestHeaderView *self,
1270 ModestHeaderViewPrivate *priv;
1271 ModestHeaderViewStyle style;
1273 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1274 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1276 style = modest_header_view_get_style (self);
1277 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1279 return priv->sort_type[style][type];
1283 ModestHeaderView *header_view;
1284 RefreshAsyncUserCallback cb;
1289 folder_refreshed_cb (ModestMailOperation *mail_op,
1293 ModestHeaderViewPrivate *priv;
1294 SetFolderHelper *info;
1296 info = (SetFolderHelper*) user_data;
1298 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1302 info->cb (mail_op, folder, info->user_data);
1304 /* Start the folder count changes observer. We do not need it
1305 before the refresh. Note that the monitor could still be
1306 called for this refresh but now we know that the callback
1307 was previously called */
1308 g_mutex_lock (priv->observers_lock);
1309 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1310 g_mutex_unlock (priv->observers_lock);
1312 /* Notify the observers that the update is over */
1313 g_signal_emit (G_OBJECT (info->header_view),
1314 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1316 /* Allow filtering notifications from now on if the current
1317 folder is still the same (if not then the user has selected
1318 another one to refresh, we should wait until that refresh
1320 if (priv->folder == folder)
1321 priv->notify_status = TRUE;
1324 g_object_unref (info->header_view);
1329 refresh_folder_error_handler (ModestMailOperation *mail_op,
1332 const GError *error = modest_mail_operation_get_error (mail_op);
1334 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1335 error->code == TNY_IO_ERROR_WRITE ||
1336 error->code == TNY_IO_ERROR_READ) {
1337 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1338 /* If the mail op has been cancelled then it's not an error: don't show any message */
1339 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1340 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1341 modest_platform_information_banner (NULL, NULL, msg);
1348 modest_header_view_set_folder (ModestHeaderView *self,
1351 ModestWindow *progress_window,
1352 RefreshAsyncUserCallback callback,
1355 ModestHeaderViewPrivate *priv;
1357 g_return_if_fail (self);
1359 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1362 if (priv->status_timeout) {
1363 g_source_remove (priv->status_timeout);
1364 priv->status_timeout = 0;
1367 g_mutex_lock (priv->observers_lock);
1368 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1369 g_object_unref (priv->folder);
1370 priv->folder = NULL;
1371 g_mutex_unlock (priv->observers_lock);
1375 GtkTreeSelection *selection;
1376 SetFolderHelper *info;
1377 ModestMailOperation *mail_op = NULL;
1379 /* Set folder in the model */
1380 modest_header_view_set_folder_intern (self, folder, refresh);
1382 /* Pick my reference. Nothing to do with the mail operation */
1383 priv->folder = g_object_ref (folder);
1385 /* Do not notify about filterings until the refresh finishes */
1386 priv->notify_status = FALSE;
1388 /* Clear the selection if exists */
1389 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1390 gtk_tree_selection_unselect_all(selection);
1391 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1393 /* Notify the observers that the update begins */
1394 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1397 /* create the helper */
1398 info = g_malloc0 (sizeof (SetFolderHelper));
1399 info->header_view = g_object_ref (self);
1400 info->cb = callback;
1401 info->user_data = user_data;
1403 /* Create the mail operation (source will be the parent widget) */
1404 if (progress_window)
1405 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1406 refresh_folder_error_handler,
1409 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1412 /* Refresh the folder asynchronously */
1413 modest_mail_operation_refresh_folder (mail_op,
1415 folder_refreshed_cb,
1418 folder_refreshed_cb (mail_op, folder, info);
1422 g_object_unref (mail_op);
1424 g_mutex_lock (priv->observers_lock);
1426 if (priv->monitor) {
1427 tny_folder_monitor_stop (priv->monitor);
1428 g_object_unref (G_OBJECT (priv->monitor));
1429 priv->monitor = NULL;
1432 if (priv->autoselect_reference) {
1433 gtk_tree_row_reference_free (priv->autoselect_reference);
1434 priv->autoselect_reference = NULL;
1437 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1439 modest_header_view_notify_observers(self, NULL, NULL);
1441 g_mutex_unlock (priv->observers_lock);
1443 /* Notify the observers that the update is over */
1444 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1450 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1451 GtkTreeViewColumn *column, gpointer userdata)
1453 ModestHeaderView *self = NULL;
1455 GtkTreeModel *model = NULL;
1456 TnyHeader *header = NULL;
1457 TnyHeaderFlags flags;
1459 self = MODEST_HEADER_VIEW (treeview);
1461 model = gtk_tree_view_get_model (treeview);
1462 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1465 /* get the first selected item */
1466 gtk_tree_model_get (model, &iter,
1467 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1468 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1471 /* Dont open DELETED messages */
1472 if (flags & TNY_HEADER_FLAG_DELETED) {
1475 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1476 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1477 modest_platform_information_banner (NULL, NULL, msg);
1483 g_signal_emit (G_OBJECT(self),
1484 signals[HEADER_ACTIVATED_SIGNAL],
1490 g_object_unref (G_OBJECT (header));
1495 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1497 GtkTreeModel *model;
1498 TnyHeader *header = NULL;
1499 GtkTreePath *path = NULL;
1501 ModestHeaderView *self;
1502 GList *selected = NULL;
1504 g_return_if_fail (sel);
1505 g_return_if_fail (user_data);
1507 self = MODEST_HEADER_VIEW (user_data);
1509 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1510 if (selected != NULL)
1511 path = (GtkTreePath *) selected->data;
1512 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1513 return; /* msg was _un_selected */
1515 gtk_tree_model_get (model, &iter,
1516 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1520 g_signal_emit (G_OBJECT(self),
1521 signals[HEADER_SELECTED_SIGNAL],
1524 g_object_unref (G_OBJECT (header));
1526 /* free all items in 'selected' */
1527 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1528 g_list_free (selected);
1532 /* PROTECTED method. It's useful when we want to force a given
1533 selection to reload a msg. For example if we have selected a header
1534 in offline mode, when Modest become online, we want to reload the
1535 message automatically without an user click over the header */
1537 _modest_header_view_change_selection (GtkTreeSelection *selection,
1540 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1541 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1543 on_selection_changed (selection, user_data);
1547 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1554 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1558 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1562 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1570 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1578 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1579 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1583 case TNY_HEADER_FLAG_ATTACHMENTS:
1585 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1586 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1587 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1588 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1590 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1591 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1593 return cmp ? cmp : t1 - t2;
1595 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1596 TnyHeader *header1 = NULL, *header2 = NULL;
1598 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1599 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1600 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1601 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1603 /* This is for making priority values respect the intuitive sort relationship
1604 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1606 if (header1 && header2) {
1607 cmp = compare_priorities (tny_header_get_priority (header1),
1608 tny_header_get_priority (header2));
1609 g_object_unref (header1);
1610 g_object_unref (header2);
1612 return cmp ? cmp : t1 - t2;
1618 return &iter1 - &iter2; /* oughhhh */
1623 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1630 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1632 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1633 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1634 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1635 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1637 /* Do not use the prefixes for sorting. Consume all the blank
1638 spaces for sorting */
1639 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1640 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1643 /* If they're equal based on subject without prefix then just
1644 sort them by length. This will show messages like this.
1651 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1658 /* Drag and drop stuff */
1660 drag_data_get_cb (GtkWidget *widget,
1661 GdkDragContext *context,
1662 GtkSelectionData *selection_data,
1667 ModestHeaderView *self = NULL;
1668 ModestHeaderViewPrivate *priv = NULL;
1670 self = MODEST_HEADER_VIEW (widget);
1671 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1673 /* Set the data. Do not use the current selection because it
1674 could be different than the selection at the beginning of
1676 modest_dnd_selection_data_set_paths (selection_data,
1677 priv->drag_begin_cached_selected_rows);
1681 * We're caching the selected rows at the beginning because the
1682 * selection could change between drag-begin and drag-data-get, for
1683 * example if we have a set of rows already selected, and then we
1684 * click in one of them (without SHIFT key pressed) and begin a drag,
1685 * the selection at that moment contains all the selected lines, but
1686 * after dropping the selection, the release event provokes that only
1687 * the row used to begin the drag is selected, so at the end the
1688 * drag&drop affects only one rows instead of all the selected ones.
1692 drag_begin_cb (GtkWidget *widget,
1693 GdkDragContext *context,
1696 ModestHeaderView *self = NULL;
1697 ModestHeaderViewPrivate *priv = NULL;
1698 GtkTreeSelection *selection;
1700 self = MODEST_HEADER_VIEW (widget);
1701 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1703 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1704 priv->drag_begin_cached_selected_rows =
1705 gtk_tree_selection_get_selected_rows (selection, NULL);
1709 * We use the drag-end signal to clear the cached selection, we use
1710 * this because this allways happens, whether or not the d&d was a
1714 drag_end_cb (GtkWidget *widget,
1718 ModestHeaderView *self = NULL;
1719 ModestHeaderViewPrivate *priv = NULL;
1721 self = MODEST_HEADER_VIEW (widget);
1722 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1724 /* Free cached data */
1725 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1726 g_list_free (priv->drag_begin_cached_selected_rows);
1727 priv->drag_begin_cached_selected_rows = NULL;
1730 /* Header view drag types */
1731 const GtkTargetEntry header_view_drag_types[] = {
1732 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1736 enable_drag_and_drop (GtkWidget *self)
1738 #ifdef MODEST_TOOLKIT_HILDON2
1741 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1742 header_view_drag_types,
1743 G_N_ELEMENTS (header_view_drag_types),
1744 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1748 disable_drag_and_drop (GtkWidget *self)
1750 #ifdef MODEST_TOOLKIT_HILDON2
1753 gtk_drag_source_unset (self);
1757 setup_drag_and_drop (GtkWidget *self)
1759 #ifdef MODEST_TOOLKIT_HILDON2
1762 enable_drag_and_drop(self);
1763 g_signal_connect(G_OBJECT (self), "drag_data_get",
1764 G_CALLBACK(drag_data_get_cb), NULL);
1766 g_signal_connect(G_OBJECT (self), "drag_begin",
1767 G_CALLBACK(drag_begin_cb), NULL);
1769 g_signal_connect(G_OBJECT (self), "drag_end",
1770 G_CALLBACK(drag_end_cb), NULL);
1773 static GtkTreePath *
1774 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1776 GtkTreePath *path = NULL;
1777 GtkTreeSelection *sel = NULL;
1780 sel = gtk_tree_view_get_selection(self);
1781 rows = gtk_tree_selection_get_selected_rows (sel, model);
1783 if ((rows == NULL) || (g_list_length(rows) != 1))
1786 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1791 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1797 #ifndef MODEST_TOOLKIT_HILDON2
1799 * This function moves the tree view scroll to the current selected
1800 * row when the widget grabs the focus
1803 on_focus_in (GtkWidget *self,
1804 GdkEventFocus *event,
1807 GtkTreeSelection *selection;
1808 GtkTreeModel *model;
1809 GList *selected = NULL;
1810 GtkTreePath *selected_path = NULL;
1812 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1816 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1817 /* If none selected yet, pick the first one */
1818 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1822 /* Return if the model is empty */
1823 if (!gtk_tree_model_get_iter_first (model, &iter))
1826 path = gtk_tree_model_get_path (model, &iter);
1827 gtk_tree_selection_select_path (selection, path);
1828 gtk_tree_path_free (path);
1831 /* Need to get the all the rows because is selection multiple */
1832 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1833 if (selected == NULL) return FALSE;
1834 selected_path = (GtkTreePath *) selected->data;
1837 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1838 g_list_free (selected);
1844 on_focus_out (GtkWidget *self,
1845 GdkEventFocus *event,
1849 if (!gtk_widget_is_focus (self)) {
1850 GtkTreeSelection *selection = NULL;
1851 GList *selected_rows = NULL;
1852 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1853 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1854 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1855 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1856 gtk_tree_selection_unselect_all (selection);
1857 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1858 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1859 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1860 g_list_free (selected_rows);
1868 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1870 enable_drag_and_drop(self);
1875 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1877 GtkTreeSelection *selection = NULL;
1878 GtkTreePath *path = NULL;
1879 gboolean already_selected = FALSE, already_opened = FALSE;
1880 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1882 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1884 GtkTreeModel *model;
1886 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1887 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1889 /* Get header from model */
1890 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1891 if (gtk_tree_model_get_iter (model, &iter, path)) {
1892 GValue value = {0,};
1895 gtk_tree_model_get_value (model, &iter,
1896 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1898 header = (TnyHeader *) g_value_get_object (&value);
1899 if (TNY_IS_HEADER (header)) {
1900 status = modest_tny_all_send_queues_get_msg_status (header);
1901 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1904 g_value_unset (&value);
1908 /* Enable drag and drop only if the user clicks on a row that
1909 it's already selected. If not, let him select items using
1910 the pointer. If the message is in an OUTBOX and in sending
1911 status disable drag and drop as well */
1912 if (!already_selected ||
1913 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1915 disable_drag_and_drop(self);
1918 gtk_tree_path_free(path);
1920 /* If it's already opened then do not let the button-press
1921 event go on because it'll perform a message open because
1922 we're clicking on to an already selected header */
1927 folder_monitor_update (TnyFolderObserver *self,
1928 TnyFolderChange *change)
1930 ModestHeaderViewPrivate *priv = NULL;
1931 TnyFolderChangeChanged changed;
1932 TnyFolder *folder = NULL;
1934 changed = tny_folder_change_get_changed (change);
1936 /* Do not notify the observers if the folder of the header
1937 view has changed before this call to the observer
1939 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1940 folder = tny_folder_change_get_folder (change);
1941 if (folder != priv->folder)
1944 MODEST_DEBUG_BLOCK (
1945 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1946 g_print ("ADDED %d/%d (r/t) \n",
1947 tny_folder_change_get_new_unread_count (change),
1948 tny_folder_change_get_new_all_count (change));
1949 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1950 g_print ("ALL COUNT %d\n",
1951 tny_folder_change_get_new_all_count (change));
1952 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1953 g_print ("UNREAD COUNT %d\n",
1954 tny_folder_change_get_new_unread_count (change));
1955 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1956 g_print ("EXPUNGED %d/%d (r/t) \n",
1957 tny_folder_change_get_new_unread_count (change),
1958 tny_folder_change_get_new_all_count (change));
1959 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1960 g_print ("FOLDER RENAME\n");
1961 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1962 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1963 tny_folder_change_get_new_unread_count (change),
1964 tny_folder_change_get_new_all_count (change));
1965 g_print ("---------------------------------------------------\n");
1968 /* Check folder count */
1969 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1970 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1972 g_mutex_lock (priv->observers_lock);
1974 /* Emit signal to evaluate how headers changes affects
1975 to the window view */
1976 g_signal_emit (G_OBJECT(self),
1977 signals[MSG_COUNT_CHANGED_SIGNAL],
1980 /* Added or removed headers, so data stored on cliboard are invalid */
1981 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1982 modest_email_clipboard_clear (priv->clipboard);
1984 g_mutex_unlock (priv->observers_lock);
1990 g_object_unref (folder);
1994 modest_header_view_is_empty (ModestHeaderView *self)
1996 ModestHeaderViewPrivate *priv;
1998 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2000 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2002 return priv->status == HEADER_VIEW_EMPTY;
2006 modest_header_view_clear (ModestHeaderView *self)
2008 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2010 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2014 modest_header_view_copy_selection (ModestHeaderView *header_view)
2016 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2018 /* Copy selection */
2019 _clipboard_set_selected_data (header_view, FALSE);
2023 modest_header_view_cut_selection (ModestHeaderView *header_view)
2025 ModestHeaderViewPrivate *priv = NULL;
2026 const gchar **hidding = NULL;
2027 guint i, n_selected;
2029 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2031 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2033 /* Copy selection */
2034 _clipboard_set_selected_data (header_view, TRUE);
2036 /* Get hidding ids */
2037 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2039 /* Clear hidding array created by previous cut operation */
2040 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2042 /* Copy hidding array */
2043 priv->n_selected = n_selected;
2044 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2045 for (i=0; i < n_selected; i++)
2046 priv->hidding_ids[i] = g_strdup(hidding[i]);
2048 /* Hide cut headers */
2049 modest_header_view_refilter (header_view);
2056 _clipboard_set_selected_data (ModestHeaderView *header_view,
2059 ModestHeaderViewPrivate *priv = NULL;
2060 TnyList *headers = NULL;
2062 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2063 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2065 /* Set selected data on clipboard */
2066 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2067 headers = modest_header_view_get_selected_headers (header_view);
2068 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2071 g_object_unref (headers);
2075 ModestHeaderView *self;
2080 notify_filter_change (gpointer data)
2082 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2084 g_signal_emit (info->self,
2085 signals[MSG_COUNT_CHANGED_SIGNAL],
2086 0, info->folder, NULL);
2092 notify_filter_change_destroy (gpointer data)
2094 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2095 ModestHeaderViewPrivate *priv;
2097 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2098 priv->status_timeout = 0;
2100 g_object_unref (info->self);
2101 g_object_unref (info->folder);
2102 g_slice_free (NotifyFilterInfo, info);
2106 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2108 /* For the moment we only need to filter outbox */
2109 return priv->is_outbox;
2113 filter_row (GtkTreeModel *model,
2117 ModestHeaderViewPrivate *priv = NULL;
2118 TnyHeaderFlags flags;
2119 TnyHeader *header = NULL;
2122 gboolean visible = TRUE;
2123 gboolean found = FALSE;
2124 GValue value = {0,};
2125 HeaderViewStatus old_status;
2127 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2128 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2130 /* Get header from model */
2131 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2132 flags = (TnyHeaderFlags) g_value_get_int (&value);
2133 g_value_unset (&value);
2134 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2135 header = (TnyHeader *) g_value_get_object (&value);
2136 g_value_unset (&value);
2138 /* Get message id from header (ensure is a valid id) */
2144 /* Hide deleted and mark as deleted heders */
2145 if (flags & TNY_HEADER_FLAG_DELETED ||
2146 flags & TNY_HEADER_FLAG_EXPUNGED) {
2151 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2152 if (current_folder_needs_filtering (priv) &&
2153 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2159 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2160 if (current_folder_needs_filtering (priv) &&
2161 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2167 /* If no data on clipboard, return always TRUE */
2168 if (modest_email_clipboard_cleared(priv->clipboard)) {
2174 if (priv->hidding_ids != NULL) {
2175 id = tny_header_dup_message_id (header);
2176 for (i=0; i < priv->n_selected && !found; i++)
2177 if (priv->hidding_ids[i] != NULL && id != NULL)
2178 found = (!strcmp (priv->hidding_ids[i], id));
2185 old_status = priv->status;
2186 priv->status = ((gboolean) priv->status) && !visible;
2187 if ((priv->notify_status) && (priv->status != old_status)) {
2188 if (priv->status_timeout)
2189 g_source_remove (priv->status_timeout);
2192 NotifyFilterInfo *info;
2194 info = g_slice_new0 (NotifyFilterInfo);
2195 info->self = g_object_ref (G_OBJECT (user_data));
2197 info->folder = tny_header_get_folder (header);
2198 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2199 notify_filter_change,
2201 notify_filter_change_destroy);
2209 _clear_hidding_filter (ModestHeaderView *header_view)
2211 ModestHeaderViewPrivate *priv = NULL;
2214 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2215 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2217 if (priv->hidding_ids != NULL) {
2218 for (i=0; i < priv->n_selected; i++)
2219 g_free (priv->hidding_ids[i]);
2220 g_free(priv->hidding_ids);
2225 modest_header_view_refilter (ModestHeaderView *header_view)
2227 GtkTreeModel *model, *sortable = NULL;
2228 ModestHeaderViewPrivate *priv = NULL;
2230 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2231 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2233 /* Hide cut headers */
2234 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2235 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2236 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2237 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2238 priv->status = HEADER_VIEW_INIT;
2239 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2245 * Called when an account is removed. If I'm showing a folder of the
2246 * account that has been removed then clear the view
2249 on_account_removed (TnyAccountStore *self,
2250 TnyAccount *account,
2253 ModestHeaderViewPrivate *priv = NULL;
2255 /* Ignore changes in transport accounts */
2256 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2259 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2262 TnyAccount *my_account;
2264 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2265 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2266 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2267 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2269 my_account = tny_folder_get_account (priv->folder);
2273 if (my_account == account)
2274 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2275 g_object_unref (my_account);
2281 modest_header_view_add_observer(ModestHeaderView *header_view,
2282 ModestHeaderViewObserver *observer)
2284 ModestHeaderViewPrivate *priv;
2286 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2287 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2289 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2291 g_mutex_lock(priv->observer_list_lock);
2292 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2293 g_mutex_unlock(priv->observer_list_lock);
2297 modest_header_view_remove_observer(ModestHeaderView *header_view,
2298 ModestHeaderViewObserver *observer)
2300 ModestHeaderViewPrivate *priv;
2302 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2303 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2305 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2307 g_mutex_lock(priv->observer_list_lock);
2308 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2309 g_mutex_unlock(priv->observer_list_lock);
2313 modest_header_view_notify_observers(ModestHeaderView *header_view,
2314 GtkTreeModel *model,
2315 const gchar *tny_folder_id)
2317 ModestHeaderViewPrivate *priv = NULL;
2319 ModestHeaderViewObserver *observer;
2322 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2324 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2326 g_mutex_lock(priv->observer_list_lock);
2327 iter = priv->observer_list;
2328 while(iter != NULL){
2329 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2330 modest_header_view_observer_update(observer, model,
2332 iter = g_slist_next(iter);
2334 g_mutex_unlock(priv->observer_list_lock);
2338 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2340 ModestHeaderViewPrivate *priv = NULL;
2342 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2343 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2347 modest_header_view_set_filter (ModestHeaderView *self,
2348 ModestHeaderViewFilter filter)
2350 ModestHeaderViewPrivate *priv;
2352 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2353 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2355 priv->filter |= filter;
2357 if (current_folder_needs_filtering (priv))
2358 modest_header_view_refilter (self);
2362 modest_header_view_unset_filter (ModestHeaderView *self,
2363 ModestHeaderViewFilter filter)
2365 ModestHeaderViewPrivate *priv;
2367 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2368 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2370 priv->filter &= ~filter;
2372 if (current_folder_needs_filtering (priv))
2373 modest_header_view_refilter (self);
2377 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2379 if (strcmp ("style", spec->name) == 0) {
2380 update_style (MODEST_HEADER_VIEW (obj));
2381 gtk_widget_queue_draw (GTK_WIDGET (obj));
2386 update_style (ModestHeaderView *self)
2388 ModestHeaderViewPrivate *priv;
2389 GdkColor style_color;
2390 GdkColor style_active_color;
2391 PangoAttrList *attr_list;
2393 PangoAttribute *attr;
2395 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2396 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2400 attr_list = pango_attr_list_new ();
2401 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
2402 gdk_color_parse ("grey", &style_color);
2404 priv->secondary_color = style_color;
2405 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2406 pango_attr_list_insert (attr_list, attr);
2409 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2411 "SmallSystemFont", NULL,
2414 attr = pango_attr_font_desc_new (pango_font_description_copy
2415 (style->font_desc));
2416 pango_attr_list_insert (attr_list, attr);
2418 g_object_set (G_OBJECT (priv->renderer_address),
2419 "foreground-gdk", &(priv->secondary_color),
2420 "foreground-set", TRUE,
2421 "attributes", attr_list,
2423 g_object_set (G_OBJECT (priv->renderer_date_status),
2424 "foreground-gdk", &(priv->secondary_color),
2425 "foreground-set", TRUE,
2426 "attributes", attr_list,
2428 pango_attr_list_unref (attr_list);
2430 g_object_set (G_OBJECT (priv->renderer_address),
2431 "foreground-gdk", &(priv->secondary_color),
2432 "foreground-set", TRUE,
2433 "scale", PANGO_SCALE_SMALL,
2436 g_object_set (G_OBJECT (priv->renderer_date_status),
2437 "foreground-gdk", &(priv->secondary_color),
2438 "foreground-set", TRUE,
2439 "scale", PANGO_SCALE_SMALL,
2444 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
2445 priv->active_color = style_active_color;
2446 #ifdef MODEST_TOOLKIT_HILDON2
2447 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2448 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2451 #ifdef MODEST_TOOLKIT_HILDON2
2452 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2458 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2463 GtkTreeModel *tree_model;
2468 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2476 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2477 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2481 gtk_tree_model_get (tree_model, &iter,
2482 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,