1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
38 #include <modest-header-view.h>
39 #include <modest-header-view-priv.h>
40 #include <modest-dnd.h>
41 #include <modest-tny-folder.h>
42 #include <modest-debug.h>
43 #include <modest-main-window.h>
44 #include <modest-ui-actions.h>
45 #include <modest-marshal.h>
46 #include <modest-text-utils.h>
47 #include <modest-icon-names.h>
48 #include <modest-runtime.h>
49 #include "modest-platform.h"
50 #include <modest-hbox-cell-renderer.h>
51 #include <modest-vbox-cell-renderer.h>
53 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
54 static void modest_header_view_init (ModestHeaderView *obj);
55 static void modest_header_view_finalize (GObject *obj);
56 static void modest_header_view_dispose (GObject *obj);
58 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
59 GtkTreeViewColumn *column, gpointer userdata);
61 static gint cmp_rows (GtkTreeModel *tree_model,
66 static gint cmp_subject_rows (GtkTreeModel *tree_model,
71 static gboolean filter_row (GtkTreeModel *model,
75 static void on_account_removed (TnyAccountStore *self,
79 static void on_selection_changed (GtkTreeSelection *sel,
82 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
85 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
88 static void setup_drag_and_drop (GtkWidget *self);
90 static void enable_drag_and_drop (GtkWidget *self);
92 static void disable_drag_and_drop (GtkWidget *self);
94 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
96 static gboolean on_focus_in (GtkWidget *sef,
100 static gboolean on_focus_out (GtkWidget *self,
101 GdkEventFocus *event,
104 static void folder_monitor_update (TnyFolderObserver *self,
105 TnyFolderChange *change);
107 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
109 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
111 static void _clear_hidding_filter (ModestHeaderView *header_view);
113 static void modest_header_view_notify_observers(
114 ModestHeaderView *header_view,
116 const gchar *tny_folder_id);
118 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
119 GdkEventExpose *event,
123 HEADER_VIEW_NON_EMPTY,
128 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
129 struct _ModestHeaderViewPrivate {
131 ModestHeaderViewStyle style;
133 TnyFolderMonitor *monitor;
134 GMutex *observers_lock;
136 /*header-view-observer observer*/
137 GMutex *observer_list_lock;
138 GSList *observer_list;
140 /* not unref this object, its a singlenton */
141 ModestEmailClipboard *clipboard;
143 /* Filter tree model */
146 GtkTreeRowReference *autoselect_reference;
148 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
149 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
151 gulong selection_changed_handler;
152 gulong acc_removed_handler;
154 GList *drag_begin_cached_selected_rows;
156 HeaderViewStatus status;
157 guint status_timeout;
158 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
161 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
162 struct _HeadersCountChangedHelper {
163 ModestHeaderView *self;
164 TnyFolderChange *change;
168 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
169 MODEST_TYPE_HEADER_VIEW, \
170 ModestHeaderViewPrivate))
174 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
177 HEADER_SELECTED_SIGNAL,
178 HEADER_ACTIVATED_SIGNAL,
179 ITEM_NOT_FOUND_SIGNAL,
180 MSG_COUNT_CHANGED_SIGNAL,
181 UPDATING_MSG_LIST_SIGNAL,
186 static GObjectClass *parent_class = NULL;
188 /* uncomment the following if you have defined any signals */
189 static guint signals[LAST_SIGNAL] = {0};
192 modest_header_view_get_type (void)
194 static GType my_type = 0;
196 static const GTypeInfo my_info = {
197 sizeof(ModestHeaderViewClass),
198 NULL, /* base init */
199 NULL, /* base finalize */
200 (GClassInitFunc) modest_header_view_class_init,
201 NULL, /* class finalize */
202 NULL, /* class data */
203 sizeof(ModestHeaderView),
205 (GInstanceInitFunc) modest_header_view_init,
209 static const GInterfaceInfo tny_folder_observer_info =
211 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
212 NULL, /* interface_finalize */
213 NULL /* interface_data */
215 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
219 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
220 &tny_folder_observer_info);
228 modest_header_view_class_init (ModestHeaderViewClass *klass)
230 GObjectClass *gobject_class;
231 gobject_class = (GObjectClass*) klass;
233 parent_class = g_type_class_peek_parent (klass);
234 gobject_class->finalize = modest_header_view_finalize;
235 gobject_class->dispose = modest_header_view_dispose;
237 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
239 signals[HEADER_SELECTED_SIGNAL] =
240 g_signal_new ("header_selected",
241 G_TYPE_FROM_CLASS (gobject_class),
243 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
245 g_cclosure_marshal_VOID__POINTER,
246 G_TYPE_NONE, 1, G_TYPE_POINTER);
248 signals[HEADER_ACTIVATED_SIGNAL] =
249 g_signal_new ("header_activated",
250 G_TYPE_FROM_CLASS (gobject_class),
252 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
254 g_cclosure_marshal_VOID__POINTER,
255 G_TYPE_NONE, 1, G_TYPE_POINTER);
258 signals[ITEM_NOT_FOUND_SIGNAL] =
259 g_signal_new ("item_not_found",
260 G_TYPE_FROM_CLASS (gobject_class),
262 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
264 g_cclosure_marshal_VOID__INT,
265 G_TYPE_NONE, 1, G_TYPE_INT);
267 signals[MSG_COUNT_CHANGED_SIGNAL] =
268 g_signal_new ("msg_count_changed",
269 G_TYPE_FROM_CLASS (gobject_class),
271 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
273 modest_marshal_VOID__POINTER_POINTER,
274 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
276 signals[UPDATING_MSG_LIST_SIGNAL] =
277 g_signal_new ("updating-msg-list",
278 G_TYPE_FROM_CLASS (gobject_class),
280 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
282 g_cclosure_marshal_VOID__BOOLEAN,
283 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
287 tny_folder_observer_init (TnyFolderObserverIface *klass)
289 klass->update = folder_monitor_update;
292 static GtkTreeViewColumn*
293 get_new_column (const gchar *name, GtkCellRenderer *renderer,
294 gboolean resizable, gint sort_col_id, gboolean show_as_text,
295 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
297 GtkTreeViewColumn *column;
299 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
300 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
302 gtk_tree_view_column_set_resizable (column, resizable);
304 gtk_tree_view_column_set_expand (column, TRUE);
307 gtk_tree_view_column_add_attribute (column, renderer, "text",
309 if (sort_col_id >= 0)
310 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
312 gtk_tree_view_column_set_sort_indicator (column, FALSE);
313 gtk_tree_view_column_set_reorderable (column, TRUE);
316 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
323 remove_all_columns (ModestHeaderView *obj)
325 GList *columns, *cursor;
327 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
329 for (cursor = columns; cursor; cursor = cursor->next)
330 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
331 GTK_TREE_VIEW_COLUMN(cursor->data));
332 g_list_free (columns);
336 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
338 GtkTreeModel *tree_filter, *sortable;
339 GtkTreeViewColumn *column=NULL;
340 GtkTreeSelection *selection = NULL;
341 GtkCellRenderer *renderer_msgtype,*renderer_header,
342 *renderer_attach, *renderer_compact_date_or_status;
343 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
344 *renderer_subject, *renderer_subject_box, *renderer_recpt,
346 ModestHeaderViewPrivate *priv;
347 GtkTreeViewColumn *compact_column = NULL;
350 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
351 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
353 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
355 /* FIXME: check whether these renderers need to be freed */
356 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
357 renderer_attach = gtk_cell_renderer_pixbuf_new ();
358 renderer_priority = gtk_cell_renderer_pixbuf_new ();
359 renderer_header = gtk_cell_renderer_text_new ();
361 renderer_compact_header = modest_vbox_cell_renderer_new ();
362 renderer_recpt_box = modest_hbox_cell_renderer_new ();
363 renderer_subject_box = modest_hbox_cell_renderer_new ();
364 renderer_recpt = gtk_cell_renderer_text_new ();
365 renderer_subject = gtk_cell_renderer_text_new ();
366 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
368 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
369 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
370 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
371 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
372 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
373 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
374 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
375 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
376 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
377 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
378 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
379 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
380 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
381 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
383 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
384 #ifdef MODEST_PLATFORM_MAEMO
385 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
386 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
388 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
389 g_object_set(G_OBJECT(renderer_header),
390 "ellipsize", PANGO_ELLIPSIZE_END,
392 g_object_set (G_OBJECT (renderer_subject),
393 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
395 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
396 g_object_set (G_OBJECT (renderer_recpt),
397 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
399 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
400 g_object_set(G_OBJECT(renderer_compact_date_or_status),
401 "xalign", 1.0, "yalign", 0.0,
403 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
404 g_object_set (G_OBJECT (renderer_priority),
405 "yalign", 1.0, NULL);
406 g_object_set (G_OBJECT (renderer_attach),
407 "yalign", 0.0, NULL);
409 #ifdef MODEST_PLATFORM_MAEMO
410 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
411 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
412 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
414 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
415 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
416 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
419 remove_all_columns (self);
421 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
422 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
423 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
424 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
426 /* Add new columns */
427 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
428 ModestHeaderViewColumn col =
429 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
431 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
432 g_printerr ("modest: invalid column %d in column list\n", col);
438 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
439 column = get_new_column (_("M"), renderer_msgtype, FALSE,
440 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
442 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
444 gtk_tree_view_column_set_fixed_width (column, 45);
447 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
448 column = get_new_column (_("A"), renderer_attach, FALSE,
449 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
451 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
453 gtk_tree_view_column_set_fixed_width (column, 45);
457 case MODEST_HEADER_VIEW_COLUMN_FROM:
458 column = get_new_column (_("From"), renderer_header, TRUE,
459 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
461 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
462 GINT_TO_POINTER(TRUE));
465 case MODEST_HEADER_VIEW_COLUMN_TO:
466 column = get_new_column (_("To"), renderer_header, TRUE,
467 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
469 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
470 GINT_TO_POINTER(FALSE));
473 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
474 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
475 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
477 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
478 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
479 compact_column = column;
482 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
483 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
484 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
486 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
487 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
488 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
489 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
490 compact_column = column;
494 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
495 column = get_new_column (_("Subject"), renderer_header, TRUE,
496 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
498 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
502 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
503 column = get_new_column (_("Received"), renderer_header, TRUE,
504 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
506 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
507 GINT_TO_POINTER(TRUE));
510 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
511 column = get_new_column (_("Sent"), renderer_header, TRUE,
512 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
514 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
515 GINT_TO_POINTER(FALSE));
518 case MODEST_HEADER_VIEW_COLUMN_SIZE:
519 column = get_new_column (_("Size"), renderer_header, TRUE,
520 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
522 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
525 case MODEST_HEADER_VIEW_COLUMN_STATUS:
526 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
527 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
529 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
534 g_return_val_if_reached(FALSE);
537 /* we keep the column id around */
538 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
539 GINT_TO_POINTER(col));
541 /* we need this ptr when sorting the rows */
542 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
544 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
548 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
549 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
550 (GtkTreeIterCompareFunc) cmp_rows,
551 compact_column, NULL);
552 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
553 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
554 (GtkTreeIterCompareFunc) cmp_subject_rows,
555 compact_column, NULL);
563 modest_header_view_init (ModestHeaderView *obj)
565 ModestHeaderViewPrivate *priv;
568 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
572 priv->monitor = NULL;
573 priv->observers_lock = g_mutex_new ();
574 priv->autoselect_reference = NULL;
576 priv->status = HEADER_VIEW_INIT;
577 priv->status_timeout = 0;
578 priv->notify_status = TRUE;
580 priv->observer_list_lock = g_mutex_new();
581 priv->observer_list = NULL;
583 priv->clipboard = modest_runtime_get_email_clipboard ();
584 priv->hidding_ids = NULL;
585 priv->n_selected = 0;
586 priv->selection_changed_handler = 0;
587 priv->acc_removed_handler = 0;
589 /* Sort parameters */
590 for (j=0; j < 2; j++) {
591 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
592 priv->sort_colid[j][i] = -1;
593 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
597 setup_drag_and_drop (GTK_WIDGET(obj));
601 modest_header_view_dispose (GObject *obj)
603 ModestHeaderView *self;
604 ModestHeaderViewPrivate *priv;
605 GtkTreeSelection *sel;
607 self = MODEST_HEADER_VIEW(obj);
608 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
610 /* Free in the dispose to avoid unref cycles */
612 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
613 g_object_unref (G_OBJECT (priv->folder));
617 /* We need to do this here in the dispose because the
618 selection won't exist when finalizing */
619 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
620 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
621 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
622 priv->selection_changed_handler = 0;
625 G_OBJECT_CLASS(parent_class)->dispose (obj);
629 modest_header_view_finalize (GObject *obj)
631 ModestHeaderView *self;
632 ModestHeaderViewPrivate *priv;
634 self = MODEST_HEADER_VIEW(obj);
635 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
637 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
638 priv->acc_removed_handler)) {
639 g_signal_handler_disconnect (modest_runtime_get_account_store (),
640 priv->acc_removed_handler);
643 /* There is no need to lock because there should not be any
644 * reference to self now. */
645 g_mutex_free(priv->observer_list_lock);
646 g_slist_free(priv->observer_list);
648 g_mutex_lock (priv->observers_lock);
650 tny_folder_monitor_stop (priv->monitor);
651 g_object_unref (G_OBJECT (priv->monitor));
653 g_mutex_unlock (priv->observers_lock);
654 g_mutex_free (priv->observers_lock);
656 /* Clear hidding array created by cut operation */
657 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
659 if (priv->autoselect_reference != NULL) {
660 gtk_tree_row_reference_free (priv->autoselect_reference);
661 priv->autoselect_reference = NULL;
664 G_OBJECT_CLASS(parent_class)->finalize (obj);
669 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
672 GtkTreeSelection *sel;
673 ModestHeaderView *self;
674 ModestHeaderViewPrivate *priv;
676 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
679 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
680 self = MODEST_HEADER_VIEW(obj);
681 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
683 modest_header_view_set_style (self, style);
685 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
686 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
687 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
689 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
690 TRUE); /* alternating row colors */
692 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
693 priv->selection_changed_handler =
694 g_signal_connect_after (sel, "changed",
695 G_CALLBACK(on_selection_changed), self);
697 g_signal_connect (self, "row-activated",
698 G_CALLBACK (on_header_row_activated), NULL);
700 g_signal_connect (self, "focus-in-event",
701 G_CALLBACK(on_focus_in), NULL);
702 g_signal_connect (self, "focus-out-event",
703 G_CALLBACK(on_focus_out), NULL);
705 g_signal_connect (self, "button-press-event",
706 G_CALLBACK(on_button_press_event), NULL);
707 g_signal_connect (self, "button-release-event",
708 G_CALLBACK(on_button_release_event), NULL);
710 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
712 G_CALLBACK (on_account_removed),
715 g_signal_connect (self, "expose-event",
716 G_CALLBACK(modest_header_view_on_expose_event),
719 return GTK_WIDGET(self);
724 modest_header_view_count_selected_headers (ModestHeaderView *self)
726 GtkTreeSelection *sel;
729 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
731 /* Get selection object and check selected rows count */
732 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
733 selected_rows = gtk_tree_selection_count_selected_rows (sel);
735 return selected_rows;
739 modest_header_view_has_selected_headers (ModestHeaderView *self)
741 GtkTreeSelection *sel;
744 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
746 /* Get selection object and check selected rows count */
747 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
748 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
755 modest_header_view_get_selected_headers (ModestHeaderView *self)
757 GtkTreeSelection *sel;
758 ModestHeaderViewPrivate *priv;
759 TnyList *header_list = NULL;
761 GList *list, *tmp = NULL;
762 GtkTreeModel *tree_model = NULL;
765 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
767 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
769 /* Get selected rows */
770 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
771 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
774 header_list = tny_simple_list_new();
776 list = g_list_reverse (list);
779 /* get header from selection */
780 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
781 gtk_tree_model_get (tree_model, &iter,
782 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
784 /* Prepend to list */
785 tny_list_prepend (header_list, G_OBJECT (header));
786 g_object_unref (G_OBJECT (header));
788 tmp = g_list_next (tmp);
791 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
798 /* scroll our list view so the selected item is visible */
800 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
802 #ifdef MODEST_PLATFORM_GNOME
804 GtkTreePath *selected_path;
805 GtkTreePath *start, *end;
809 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
810 selected_path = gtk_tree_model_get_path (model, iter);
812 start = gtk_tree_path_new ();
813 end = gtk_tree_path_new ();
815 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
817 if (gtk_tree_path_compare (selected_path, start) < 0 ||
818 gtk_tree_path_compare (end, selected_path) < 0)
819 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
820 selected_path, NULL, TRUE,
823 gtk_tree_path_free (selected_path);
824 gtk_tree_path_free (start);
825 gtk_tree_path_free (end);
827 #endif /* MODEST_PLATFORM_GNOME */
832 modest_header_view_select_next (ModestHeaderView *self)
834 GtkTreeSelection *sel;
839 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
841 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
842 path = get_selected_row (GTK_TREE_VIEW(self), &model);
843 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
844 /* Unselect previous path */
845 gtk_tree_selection_unselect_path (sel, path);
847 /* Move path down and selects new one */
848 if (gtk_tree_model_iter_next (model, &iter)) {
849 gtk_tree_selection_select_iter (sel, &iter);
850 scroll_to_selected (self, &iter, FALSE);
852 gtk_tree_path_free(path);
858 modest_header_view_select_prev (ModestHeaderView *self)
860 GtkTreeSelection *sel;
865 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
867 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
868 path = get_selected_row (GTK_TREE_VIEW(self), &model);
869 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
870 /* Unselect previous path */
871 gtk_tree_selection_unselect_path (sel, path);
874 if (gtk_tree_path_prev (path)) {
875 gtk_tree_model_get_iter (model, &iter, path);
877 /* Select the new one */
878 gtk_tree_selection_select_iter (sel, &iter);
879 scroll_to_selected (self, &iter, TRUE);
882 gtk_tree_path_free (path);
887 modest_header_view_get_columns (ModestHeaderView *self)
889 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
891 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
897 modest_header_view_set_style (ModestHeaderView *self,
898 ModestHeaderViewStyle style)
900 ModestHeaderViewPrivate *priv;
901 gboolean show_col_headers = FALSE;
902 ModestHeaderViewStyle old_style;
904 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
905 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
908 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
909 if (priv->style == style)
910 return TRUE; /* nothing to do */
913 case MODEST_HEADER_VIEW_STYLE_DETAILS:
914 show_col_headers = TRUE;
916 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
919 g_return_val_if_reached (FALSE);
921 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
922 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
924 old_style = priv->style;
931 ModestHeaderViewStyle
932 modest_header_view_get_style (ModestHeaderView *self)
934 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
936 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
939 /* This is used to automatically select the first header if the user
940 * has not selected any header yet.
943 modest_header_view_on_expose_event(GtkTreeView *header_view,
944 GdkEventExpose *event,
947 GtkTreeSelection *sel;
949 GtkTreeIter tree_iter;
950 ModestHeaderViewPrivate *priv;
952 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
953 model = gtk_tree_view_get_model(header_view);
958 sel = gtk_tree_view_get_selection(header_view);
959 if(!gtk_tree_selection_count_selected_rows(sel)) {
960 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
961 GtkTreePath *tree_iter_path;
962 /* Prevent the widget from getting the focus
963 when selecting the first item */
964 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
965 g_object_set(header_view, "can-focus", FALSE, NULL);
966 gtk_tree_selection_select_iter(sel, &tree_iter);
967 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
968 g_object_set(header_view, "can-focus", TRUE, NULL);
969 if (priv->autoselect_reference) {
970 gtk_tree_row_reference_free (priv->autoselect_reference);
972 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
973 gtk_tree_path_free (tree_iter_path);
976 if (priv->autoselect_reference != NULL) {
977 gboolean moved_selection = FALSE;
978 GtkTreePath * last_path;
979 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
980 moved_selection = TRUE;
981 g_message ("MULTISELECTION %d -> MOVED", gtk_tree_selection_count_selected_rows (sel));
985 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
986 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
987 g_message ("SELECTION PATH %s LAST PATH %s", gtk_tree_path_to_string (rows->data), gtk_tree_path_to_string (last_path));
988 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
989 moved_selection = TRUE;
990 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
993 if (moved_selection) {
994 gtk_tree_row_reference_free (priv->autoselect_reference);
995 priv->autoselect_reference = NULL;
998 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
999 GtkTreePath *current_path;
1000 current_path = gtk_tree_model_get_path (model, &tree_iter);
1001 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1002 g_message ("CURRENT PATH %s LAST PATH %s", gtk_tree_path_to_string (current_path), gtk_tree_path_to_string (last_path));
1003 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1004 g_object_set(header_view, "can-focus", FALSE, NULL);
1005 gtk_tree_selection_unselect_all (sel);
1006 gtk_tree_selection_select_iter(sel, &tree_iter);
1007 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1008 g_object_set(header_view, "can-focus", TRUE, NULL);
1009 gtk_tree_row_reference_free (priv->autoselect_reference);
1010 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1012 gtk_tree_path_free (current_path);
1013 gtk_tree_path_free (last_path);
1023 modest_header_view_get_folder (ModestHeaderView *self)
1025 ModestHeaderViewPrivate *priv;
1027 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1029 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1032 g_object_ref (priv->folder);
1034 return priv->folder;
1038 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1044 ModestHeaderView *self;
1045 ModestHeaderViewPrivate *priv;
1047 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1049 self = MODEST_HEADER_VIEW (user_data);
1050 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1052 if (cancelled || err)
1055 /* Add IDLE observer (monitor) and another folder observer for
1056 new messages (self) */
1057 g_mutex_lock (priv->observers_lock);
1058 if (priv->monitor) {
1059 tny_folder_monitor_stop (priv->monitor);
1060 g_object_unref (G_OBJECT (priv->monitor));
1062 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1063 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1064 tny_folder_monitor_start (priv->monitor);
1065 g_mutex_unlock (priv->observers_lock);
1069 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1073 ModestHeaderViewPrivate *priv;
1074 GList *cols, *cursor;
1075 GtkTreeModel *filter_model, *sortable;
1077 GtkSortType sort_type;
1079 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1081 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1083 /* Start the monitor in the callback of the
1084 tny_gtk_header_list_model_set_folder call. It's crucial to
1085 do it there and not just after the call because we want the
1086 monitor to observe only the headers returned by the
1087 tny_folder_get_headers_async call that it's inside the
1088 tny_gtk_header_list_model_set_folder call. This way the
1089 monitor infrastructure could successfully cope with
1090 duplicates. For example if a tny_folder_add_msg_async is
1091 happening while tny_gtk_header_list_model_set_folder is
1092 invoked, then the first call could add a header that will
1093 be added again by tny_gtk_header_list_model_set_folder, so
1094 we'd end up with duplicate headers. sergio */
1095 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1097 set_folder_intern_get_headers_async_cb,
1100 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1101 g_object_unref (G_OBJECT (headers));
1103 /* Init filter_row function to examine empty status */
1104 priv->status = HEADER_VIEW_INIT;
1106 /* Create a tree model filter to hide and show rows for cut operations */
1107 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1108 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1112 g_object_unref (G_OBJECT (sortable));
1114 /* install our special sorting functions */
1115 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1117 /* Restore sort column id */
1119 type = modest_tny_folder_guess_folder_type (folder);
1120 if (type == TNY_FOLDER_TYPE_INVALID)
1121 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1123 sort_colid = modest_header_view_get_sort_column_id (self, type);
1124 sort_type = modest_header_view_get_sort_type (self, type);
1125 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1128 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1129 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1130 (GtkTreeIterCompareFunc) cmp_rows,
1132 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1133 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1134 (GtkTreeIterCompareFunc) cmp_subject_rows,
1139 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1140 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1141 tny_folder_get_id(folder));
1142 g_object_unref (G_OBJECT (filter_model));
1149 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1151 GtkSortType sort_type)
1153 ModestHeaderViewPrivate *priv = NULL;
1154 GtkTreeModel *tree_filter, *sortable = NULL;
1157 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1158 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1160 /* Get model and private data */
1161 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1162 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1163 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1164 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1166 /* Sort tree model */
1167 type = modest_tny_folder_guess_folder_type (priv->folder);
1168 if (type == TNY_FOLDER_TYPE_INVALID)
1169 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1171 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1174 /* Store new sort parameters */
1175 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1180 modest_header_view_set_sort_params (ModestHeaderView *self,
1182 GtkSortType sort_type,
1185 ModestHeaderViewPrivate *priv;
1186 ModestHeaderViewStyle style;
1188 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1189 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1190 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1192 style = modest_header_view_get_style (self);
1193 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1195 priv->sort_colid[style][type] = sort_colid;
1196 priv->sort_type[style][type] = sort_type;
1200 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1203 ModestHeaderViewPrivate *priv;
1204 ModestHeaderViewStyle style;
1206 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1207 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1209 style = modest_header_view_get_style (self);
1210 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1212 return priv->sort_colid[style][type];
1216 modest_header_view_get_sort_type (ModestHeaderView *self,
1219 ModestHeaderViewPrivate *priv;
1220 ModestHeaderViewStyle style;
1222 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1223 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1225 style = modest_header_view_get_style (self);
1226 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1228 return priv->sort_type[style][type];
1232 ModestHeaderView *header_view;
1233 RefreshAsyncUserCallback cb;
1238 folder_refreshed_cb (ModestMailOperation *mail_op,
1242 ModestHeaderViewPrivate *priv;
1243 SetFolderHelper *info;
1245 info = (SetFolderHelper*) user_data;
1247 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1251 info->cb (mail_op, folder, info->user_data);
1253 /* Start the folder count changes observer. We do not need it
1254 before the refresh. Note that the monitor could still be
1255 called for this refresh but now we know that the callback
1256 was previously called */
1257 g_mutex_lock (priv->observers_lock);
1258 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1259 g_mutex_unlock (priv->observers_lock);
1261 /* Notify the observers that the update is over */
1262 g_signal_emit (G_OBJECT (info->header_view),
1263 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1265 /* Allow filtering notifications from now on if the current
1266 folder is still the same (if not then the user has selected
1267 another one to refresh, we should wait until that refresh
1269 if (priv->folder == folder)
1270 priv->notify_status = TRUE;
1273 g_object_unref (info->header_view);
1278 refresh_folder_error_handler (ModestMailOperation *mail_op,
1281 const GError *error = modest_mail_operation_get_error (mail_op);
1283 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1284 error->code == TNY_IO_ERROR_WRITE ||
1285 error->code == TNY_IO_ERROR_READ) {
1286 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1287 /* If the mail op has been cancelled then it's not an error: don't show any message */
1288 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1289 modest_platform_information_banner (NULL, NULL,
1291 "cerm_device_memory_full"));
1297 modest_header_view_set_folder (ModestHeaderView *self,
1300 RefreshAsyncUserCallback callback,
1303 ModestHeaderViewPrivate *priv;
1304 ModestWindow *main_win;
1306 g_return_if_fail (self);
1308 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1310 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1311 FALSE); /* don't create */
1313 g_warning ("%s: BUG: no main window", __FUNCTION__);
1318 if (priv->status_timeout) {
1319 g_source_remove (priv->status_timeout);
1320 priv->status_timeout = 0;
1323 g_mutex_lock (priv->observers_lock);
1324 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1325 g_object_unref (priv->folder);
1326 priv->folder = NULL;
1327 g_mutex_unlock (priv->observers_lock);
1331 GtkTreeSelection *selection;
1332 SetFolderHelper *info;
1333 ModestMailOperation *mail_op = NULL;
1335 /* Set folder in the model */
1336 modest_header_view_set_folder_intern (self, folder);
1338 /* Pick my reference. Nothing to do with the mail operation */
1339 priv->folder = g_object_ref (folder);
1341 /* Do not notify about filterings until the refresh finishes */
1342 priv->notify_status = FALSE;
1344 /* Clear the selection if exists */
1345 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1346 gtk_tree_selection_unselect_all(selection);
1347 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1349 /* Notify the observers that the update begins */
1350 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1353 /* create the helper */
1354 info = g_malloc0 (sizeof (SetFolderHelper));
1355 info->header_view = g_object_ref (self);
1356 info->cb = callback;
1357 info->user_data = user_data;
1359 /* Create the mail operation (source will be the parent widget) */
1360 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1361 refresh_folder_error_handler,
1364 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1367 /* Refresh the folder asynchronously */
1368 modest_mail_operation_refresh_folder (mail_op,
1370 folder_refreshed_cb,
1373 folder_refreshed_cb (mail_op, folder, info);
1376 g_object_unref (mail_op);
1378 g_mutex_lock (priv->observers_lock);
1380 if (priv->monitor) {
1381 tny_folder_monitor_stop (priv->monitor);
1382 g_object_unref (G_OBJECT (priv->monitor));
1383 priv->monitor = NULL;
1385 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1387 modest_header_view_notify_observers(self, NULL, NULL);
1389 g_mutex_unlock (priv->observers_lock);
1391 /* Notify the observers that the update is over */
1392 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1398 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1399 GtkTreeViewColumn *column, gpointer userdata)
1401 ModestHeaderView *self = NULL;
1402 ModestHeaderViewPrivate *priv = NULL;
1404 GtkTreeModel *model = NULL;
1405 TnyHeader *header = NULL;
1406 TnyHeaderFlags flags;
1408 self = MODEST_HEADER_VIEW (treeview);
1409 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1411 model = gtk_tree_view_get_model (treeview);
1412 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1415 /* get the first selected item */
1416 gtk_tree_model_get (model, &iter,
1417 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1418 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1421 /* Dont open DELETED messages */
1422 if (flags & TNY_HEADER_FLAG_DELETED) {
1425 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1426 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1427 modest_platform_information_banner (NULL, NULL, msg);
1433 g_signal_emit (G_OBJECT(self),
1434 signals[HEADER_ACTIVATED_SIGNAL],
1440 g_object_unref (G_OBJECT (header));
1445 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1447 GtkTreeModel *model;
1448 TnyHeader *header = NULL;
1449 GtkTreePath *path = NULL;
1451 ModestHeaderView *self;
1452 ModestHeaderViewPrivate *priv;
1453 GList *selected = NULL;
1455 g_return_if_fail (sel);
1456 g_return_if_fail (user_data);
1458 self = MODEST_HEADER_VIEW (user_data);
1459 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1461 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1462 if (selected != NULL)
1463 path = (GtkTreePath *) selected->data;
1464 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1465 return; /* msg was _un_selected */
1467 gtk_tree_model_get (model, &iter,
1468 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1472 g_signal_emit (G_OBJECT(self),
1473 signals[HEADER_SELECTED_SIGNAL],
1476 g_object_unref (G_OBJECT (header));
1478 /* free all items in 'selected' */
1479 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1480 g_list_free (selected);
1484 /* PROTECTED method. It's useful when we want to force a given
1485 selection to reload a msg. For example if we have selected a header
1486 in offline mode, when Modest become online, we want to reload the
1487 message automatically without an user click over the header */
1489 _modest_header_view_change_selection (GtkTreeSelection *selection,
1492 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1493 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1495 on_selection_changed (selection, user_data);
1499 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1506 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1510 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1514 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1522 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1529 /* static int counter = 0; */
1531 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1532 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1533 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1537 case TNY_HEADER_FLAG_ATTACHMENTS:
1539 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1540 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1541 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1542 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1544 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1545 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1547 return cmp ? cmp : t1 - t2;
1549 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1550 TnyHeader *header1 = NULL, *header2 = NULL;
1552 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1553 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1554 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1555 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1557 /* This is for making priority values respect the intuitive sort relationship
1558 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1560 if (header1 && header2) {
1561 cmp = compare_priorities (tny_header_get_priority (header1),
1562 tny_header_get_priority (header2));
1563 g_object_unref (header1);
1564 g_object_unref (header2);
1566 return cmp ? cmp : t1 - t2;
1572 return &iter1 - &iter2; /* oughhhh */
1577 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1583 /* static int counter = 0; */
1585 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1587 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1588 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1589 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1590 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1592 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1593 val2 + modest_text_utils_get_subject_prefix_len(val2),
1600 /* Drag and drop stuff */
1602 drag_data_get_cb (GtkWidget *widget,
1603 GdkDragContext *context,
1604 GtkSelectionData *selection_data,
1609 ModestHeaderView *self = NULL;
1610 ModestHeaderViewPrivate *priv = NULL;
1612 self = MODEST_HEADER_VIEW (widget);
1613 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1615 /* Set the data. Do not use the current selection because it
1616 could be different than the selection at the beginning of
1618 modest_dnd_selection_data_set_paths (selection_data,
1619 priv->drag_begin_cached_selected_rows);
1623 * We're caching the selected rows at the beginning because the
1624 * selection could change between drag-begin and drag-data-get, for
1625 * example if we have a set of rows already selected, and then we
1626 * click in one of them (without SHIFT key pressed) and begin a drag,
1627 * the selection at that moment contains all the selected lines, but
1628 * after dropping the selection, the release event provokes that only
1629 * the row used to begin the drag is selected, so at the end the
1630 * drag&drop affects only one rows instead of all the selected ones.
1634 drag_begin_cb (GtkWidget *widget,
1635 GdkDragContext *context,
1638 ModestHeaderView *self = NULL;
1639 ModestHeaderViewPrivate *priv = NULL;
1640 GtkTreeSelection *selection;
1642 self = MODEST_HEADER_VIEW (widget);
1643 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1645 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1646 priv->drag_begin_cached_selected_rows =
1647 gtk_tree_selection_get_selected_rows (selection, NULL);
1651 * We use the drag-end signal to clear the cached selection, we use
1652 * this because this allways happens, whether or not the d&d was a
1656 drag_end_cb (GtkWidget *widget,
1660 ModestHeaderView *self = NULL;
1661 ModestHeaderViewPrivate *priv = NULL;
1663 self = MODEST_HEADER_VIEW (widget);
1664 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1666 /* Free cached data */
1667 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1668 g_list_free (priv->drag_begin_cached_selected_rows);
1669 priv->drag_begin_cached_selected_rows = NULL;
1672 /* Header view drag types */
1673 const GtkTargetEntry header_view_drag_types[] = {
1674 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1678 enable_drag_and_drop (GtkWidget *self)
1680 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1681 header_view_drag_types,
1682 G_N_ELEMENTS (header_view_drag_types),
1683 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1687 disable_drag_and_drop (GtkWidget *self)
1689 gtk_drag_source_unset (self);
1693 setup_drag_and_drop (GtkWidget *self)
1695 enable_drag_and_drop(self);
1696 g_signal_connect(G_OBJECT (self), "drag_data_get",
1697 G_CALLBACK(drag_data_get_cb), NULL);
1699 g_signal_connect(G_OBJECT (self), "drag_begin",
1700 G_CALLBACK(drag_begin_cb), NULL);
1702 g_signal_connect(G_OBJECT (self), "drag_end",
1703 G_CALLBACK(drag_end_cb), NULL);
1706 static GtkTreePath *
1707 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1709 GtkTreePath *path = NULL;
1710 GtkTreeSelection *sel = NULL;
1713 sel = gtk_tree_view_get_selection(self);
1714 rows = gtk_tree_selection_get_selected_rows (sel, model);
1716 if ((rows == NULL) || (g_list_length(rows) != 1))
1719 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1724 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1731 * This function moves the tree view scroll to the current selected
1732 * row when the widget grabs the focus
1735 on_focus_in (GtkWidget *self,
1736 GdkEventFocus *event,
1739 GtkTreeSelection *selection;
1740 GtkTreeModel *model;
1741 GList *selected = NULL;
1742 GtkTreePath *selected_path = NULL;
1744 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1748 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1749 /* If none selected yet, pick the first one */
1750 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1754 /* Return if the model is empty */
1755 if (!gtk_tree_model_get_iter_first (model, &iter))
1758 path = gtk_tree_model_get_path (model, &iter);
1759 gtk_tree_selection_select_path (selection, path);
1760 gtk_tree_path_free (path);
1763 /* Need to get the all the rows because is selection multiple */
1764 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1765 if (selected == NULL) return FALSE;
1766 selected_path = (GtkTreePath *) selected->data;
1768 /* Check if we need to scroll */
1769 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1770 GtkTreePath *start_path = NULL;
1771 GtkTreePath *end_path = NULL;
1772 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1776 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1777 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1779 /* Scroll to first path */
1780 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1789 gtk_tree_path_free (start_path);
1791 gtk_tree_path_free (end_path);
1793 #endif /* GTK_CHECK_VERSION */
1796 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1797 g_list_free (selected);
1803 on_focus_out (GtkWidget *self,
1804 GdkEventFocus *event,
1808 if (!gtk_widget_is_focus (self)) {
1809 GtkTreeSelection *selection = NULL;
1810 GList *selected_rows = NULL;
1811 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1812 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1813 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1814 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1815 gtk_tree_selection_unselect_all (selection);
1816 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1817 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1818 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1819 g_list_free (selected_rows);
1826 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1828 enable_drag_and_drop(self);
1833 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1835 GtkTreeSelection *selection = NULL;
1836 GtkTreePath *path = NULL;
1837 gboolean already_selected = FALSE, already_opened = FALSE;
1838 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1840 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1842 GtkTreeModel *model;
1844 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1845 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1847 /* Get header from model */
1848 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1849 if (gtk_tree_model_get_iter (model, &iter, path)) {
1850 GValue value = {0,};
1853 gtk_tree_model_get_value (model, &iter,
1854 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1856 header = (TnyHeader *) g_value_get_object (&value);
1857 if (TNY_IS_HEADER (header)) {
1858 status = modest_tny_all_send_queues_get_msg_status (header);
1859 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1862 g_value_unset (&value);
1866 /* Enable drag and drop only if the user clicks on a row that
1867 it's already selected. If not, let him select items using
1868 the pointer. If the message is in an OUTBOX and in sending
1869 status disable drag and drop as well */
1870 if (!already_selected ||
1871 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1873 disable_drag_and_drop(self);
1876 gtk_tree_path_free(path);
1878 /* If it's already opened then do not let the button-press
1879 event go on because it'll perform a message open because
1880 we're clicking on to an already selected header */
1885 folder_monitor_update (TnyFolderObserver *self,
1886 TnyFolderChange *change)
1888 ModestHeaderViewPrivate *priv = NULL;
1889 TnyFolderChangeChanged changed;
1890 TnyFolder *folder = NULL;
1892 changed = tny_folder_change_get_changed (change);
1894 /* Do not notify the observers if the folder of the header
1895 view has changed before this call to the observer
1897 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1898 folder = tny_folder_change_get_folder (change);
1899 if (folder != priv->folder)
1902 MODEST_DEBUG_BLOCK (
1903 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1904 g_print ("ADDED %d/%d (r/t) \n",
1905 tny_folder_change_get_new_unread_count (change),
1906 tny_folder_change_get_new_all_count (change));
1907 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1908 g_print ("ALL COUNT %d\n",
1909 tny_folder_change_get_new_all_count (change));
1910 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1911 g_print ("UNREAD COUNT %d\n",
1912 tny_folder_change_get_new_unread_count (change));
1913 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1914 g_print ("EXPUNGED %d/%d (r/t) \n",
1915 tny_folder_change_get_new_unread_count (change),
1916 tny_folder_change_get_new_all_count (change));
1917 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1918 g_print ("FOLDER RENAME\n");
1919 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1920 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1921 tny_folder_change_get_new_unread_count (change),
1922 tny_folder_change_get_new_all_count (change));
1923 g_print ("---------------------------------------------------\n");
1926 /* Check folder count */
1927 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1928 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1930 g_mutex_lock (priv->observers_lock);
1932 /* Emit signal to evaluate how headers changes affects
1933 to the window view */
1934 g_signal_emit (G_OBJECT(self),
1935 signals[MSG_COUNT_CHANGED_SIGNAL],
1938 /* Added or removed headers, so data stored on cliboard are invalid */
1939 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1940 modest_email_clipboard_clear (priv->clipboard);
1942 g_mutex_unlock (priv->observers_lock);
1948 g_object_unref (folder);
1952 modest_header_view_is_empty (ModestHeaderView *self)
1954 ModestHeaderViewPrivate *priv;
1956 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1958 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1960 return priv->status == HEADER_VIEW_EMPTY;
1964 modest_header_view_clear (ModestHeaderView *self)
1966 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1968 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1972 modest_header_view_copy_selection (ModestHeaderView *header_view)
1974 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1976 /* Copy selection */
1977 _clipboard_set_selected_data (header_view, FALSE);
1981 modest_header_view_cut_selection (ModestHeaderView *header_view)
1983 ModestHeaderViewPrivate *priv = NULL;
1984 const gchar **hidding = NULL;
1985 guint i, n_selected;
1987 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1989 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1991 /* Copy selection */
1992 _clipboard_set_selected_data (header_view, TRUE);
1994 /* Get hidding ids */
1995 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1997 /* Clear hidding array created by previous cut operation */
1998 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2000 /* Copy hidding array */
2001 priv->n_selected = n_selected;
2002 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2003 for (i=0; i < n_selected; i++)
2004 priv->hidding_ids[i] = g_strdup(hidding[i]);
2006 /* Hide cut headers */
2007 modest_header_view_refilter (header_view);
2014 _clipboard_set_selected_data (ModestHeaderView *header_view,
2017 ModestHeaderViewPrivate *priv = NULL;
2018 TnyList *headers = NULL;
2020 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2021 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2023 /* Set selected data on clipboard */
2024 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2025 headers = modest_header_view_get_selected_headers (header_view);
2026 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2029 g_object_unref (headers);
2033 ModestHeaderView *self;
2038 notify_filter_change (gpointer data)
2040 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2042 g_signal_emit (info->self,
2043 signals[MSG_COUNT_CHANGED_SIGNAL],
2044 0, info->folder, NULL);
2050 notify_filter_change_destroy (gpointer data)
2052 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2053 ModestHeaderViewPrivate *priv;
2055 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2056 priv->status_timeout = 0;
2058 g_object_unref (info->self);
2059 g_object_unref (info->folder);
2060 g_slice_free (NotifyFilterInfo, info);
2064 filter_row (GtkTreeModel *model,
2068 ModestHeaderViewPrivate *priv = NULL;
2069 TnyHeaderFlags flags;
2070 TnyHeader *header = NULL;
2073 gboolean visible = TRUE;
2074 gboolean found = FALSE;
2075 GValue value = {0,};
2076 HeaderViewStatus old_status;
2078 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2079 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2081 /* Get header from model */
2082 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2083 flags = (TnyHeaderFlags) g_value_get_int (&value);
2084 g_value_unset (&value);
2085 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2086 header = (TnyHeader *) g_value_get_object (&value);
2087 g_value_unset (&value);
2089 /* Hide deleted and mark as deleted heders */
2090 if (flags & TNY_HEADER_FLAG_DELETED ||
2091 flags & TNY_HEADER_FLAG_EXPUNGED) {
2096 /* If no data on clipboard, return always TRUE */
2097 if (modest_email_clipboard_cleared(priv->clipboard)) {
2102 /* Get message id from header (ensure is a valid id) */
2109 if (priv->hidding_ids != NULL) {
2110 id = tny_header_dup_message_id (header);
2111 for (i=0; i < priv->n_selected && !found; i++)
2112 if (priv->hidding_ids[i] != NULL && id != NULL)
2113 found = (!strcmp (priv->hidding_ids[i], id));
2120 old_status = priv->status;
2121 priv->status = ((gboolean) priv->status) && !visible;
2122 if ((priv->notify_status) && (priv->status != old_status)) {
2123 NotifyFilterInfo *info;
2125 if (priv->status_timeout)
2126 g_source_remove (priv->status_timeout);
2128 info = g_slice_new0 (NotifyFilterInfo);
2129 info->self = g_object_ref (G_OBJECT (user_data));
2130 info->folder = tny_header_get_folder (header);
2131 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2132 notify_filter_change,
2134 notify_filter_change_destroy);
2141 _clear_hidding_filter (ModestHeaderView *header_view)
2143 ModestHeaderViewPrivate *priv = NULL;
2146 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2147 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2149 if (priv->hidding_ids != NULL) {
2150 for (i=0; i < priv->n_selected; i++)
2151 g_free (priv->hidding_ids[i]);
2152 g_free(priv->hidding_ids);
2157 modest_header_view_refilter (ModestHeaderView *header_view)
2159 GtkTreeModel *model = NULL;
2160 ModestHeaderViewPrivate *priv = NULL;
2162 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2163 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2165 /* Hide cut headers */
2166 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2167 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2168 priv->status = HEADER_VIEW_INIT;
2169 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2174 * Called when an account is removed. If I'm showing a folder of the
2175 * account that has been removed then clear the view
2178 on_account_removed (TnyAccountStore *self,
2179 TnyAccount *account,
2182 ModestHeaderViewPrivate *priv = NULL;
2184 /* Ignore changes in transport accounts */
2185 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2188 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2191 TnyAccount *my_account;
2193 my_account = tny_folder_get_account (priv->folder);
2194 if (my_account == account)
2195 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2196 g_object_unref (my_account);
2201 modest_header_view_add_observer(ModestHeaderView *header_view,
2202 ModestHeaderViewObserver *observer)
2204 ModestHeaderViewPrivate *priv;
2206 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2207 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2209 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2211 g_mutex_lock(priv->observer_list_lock);
2212 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2213 g_mutex_unlock(priv->observer_list_lock);
2217 modest_header_view_remove_observer(ModestHeaderView *header_view,
2218 ModestHeaderViewObserver *observer)
2220 ModestHeaderViewPrivate *priv;
2222 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2223 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2225 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2227 g_mutex_lock(priv->observer_list_lock);
2228 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2229 g_mutex_unlock(priv->observer_list_lock);
2233 modest_header_view_notify_observers(ModestHeaderView *header_view,
2234 GtkTreeModel *model,
2235 const gchar *tny_folder_id)
2237 ModestHeaderViewPrivate *priv = NULL;
2239 ModestHeaderViewObserver *observer;
2242 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2244 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2246 g_mutex_lock(priv->observer_list_lock);
2247 iter = priv->observer_list;
2248 while(iter != NULL){
2249 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2250 modest_header_view_observer_update(observer, model,
2252 iter = g_slist_next(iter);
2254 g_mutex_unlock(priv->observer_list_lock);