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>
37 #include <modest-header-view.h>
38 #include <modest-header-view-priv.h>
39 #include <modest-dnd.h>
40 #include <modest-tny-folder.h>
42 #include <modest-main-window.h>
44 #include <modest-marshal.h>
45 #include <modest-text-utils.h>
46 #include <modest-icon-names.h>
47 #include <modest-runtime.h>
48 #include "modest-platform.h"
49 #include <modest-hbox-cell-renderer.h>
50 #include <modest-vbox-cell-renderer.h>
52 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
53 static void modest_header_view_init (ModestHeaderView *obj);
54 static void modest_header_view_finalize (GObject *obj);
55 static void modest_header_view_dispose (GObject *obj);
57 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
58 GtkTreeViewColumn *column, gpointer userdata);
60 static gint cmp_rows (GtkTreeModel *tree_model,
65 static gint cmp_subject_rows (GtkTreeModel *tree_model,
70 static gboolean filter_row (GtkTreeModel *model,
74 static void on_selection_changed (GtkTreeSelection *sel,
77 static void setup_drag_and_drop (GtkTreeView *self);
79 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
81 static gboolean on_focus_in (GtkWidget *sef,
85 static void folder_monitor_update (TnyFolderObserver *self,
86 TnyFolderChange *change);
88 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
90 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
92 static void _clear_hidding_filter (ModestHeaderView *header_view);
95 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
96 struct _ModestHeaderViewPrivate {
98 ModestHeaderViewStyle style;
100 TnyFolderMonitor *monitor;
101 GMutex *observers_lock;
103 /* not unref this object, its a singlenton */
104 ModestEmailClipboard *clipboard;
106 /* Filter tree model */
110 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
111 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
113 gulong selection_changed_handler;
116 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
117 struct _HeadersCountChangedHelper {
118 ModestHeaderView *self;
119 TnyFolderChange *change;
123 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
124 MODEST_TYPE_HEADER_VIEW, \
125 ModestHeaderViewPrivate))
129 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
132 HEADER_SELECTED_SIGNAL,
133 HEADER_ACTIVATED_SIGNAL,
134 ITEM_NOT_FOUND_SIGNAL,
135 MSG_COUNT_CHANGED_SIGNAL,
140 static GObjectClass *parent_class = NULL;
142 /* uncomment the following if you have defined any signals */
143 static guint signals[LAST_SIGNAL] = {0};
146 modest_header_view_get_type (void)
148 static GType my_type = 0;
150 static const GTypeInfo my_info = {
151 sizeof(ModestHeaderViewClass),
152 NULL, /* base init */
153 NULL, /* base finalize */
154 (GClassInitFunc) modest_header_view_class_init,
155 NULL, /* class finalize */
156 NULL, /* class data */
157 sizeof(ModestHeaderView),
159 (GInstanceInitFunc) modest_header_view_init,
163 static const GInterfaceInfo tny_folder_observer_info =
165 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
166 NULL, /* interface_finalize */
167 NULL /* interface_data */
169 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
173 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
174 &tny_folder_observer_info);
182 modest_header_view_class_init (ModestHeaderViewClass *klass)
184 GObjectClass *gobject_class;
185 gobject_class = (GObjectClass*) klass;
187 parent_class = g_type_class_peek_parent (klass);
188 gobject_class->finalize = modest_header_view_finalize;
189 gobject_class->dispose = modest_header_view_dispose;
191 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
193 signals[HEADER_SELECTED_SIGNAL] =
194 g_signal_new ("header_selected",
195 G_TYPE_FROM_CLASS (gobject_class),
197 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
199 g_cclosure_marshal_VOID__POINTER,
200 G_TYPE_NONE, 1, G_TYPE_POINTER);
202 signals[HEADER_ACTIVATED_SIGNAL] =
203 g_signal_new ("header_activated",
204 G_TYPE_FROM_CLASS (gobject_class),
206 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
208 g_cclosure_marshal_VOID__POINTER,
209 G_TYPE_NONE, 1, G_TYPE_POINTER);
212 signals[ITEM_NOT_FOUND_SIGNAL] =
213 g_signal_new ("item_not_found",
214 G_TYPE_FROM_CLASS (gobject_class),
216 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
218 g_cclosure_marshal_VOID__INT,
219 G_TYPE_NONE, 1, G_TYPE_INT);
221 signals[MSG_COUNT_CHANGED_SIGNAL] =
222 g_signal_new ("msg_count_changed",
223 G_TYPE_FROM_CLASS (gobject_class),
225 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
227 modest_marshal_VOID__POINTER_POINTER,
228 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
232 tny_folder_observer_init (TnyFolderObserverIface *klass)
234 klass->update_func = folder_monitor_update;
237 static GtkTreeViewColumn*
238 get_new_column (const gchar *name, GtkCellRenderer *renderer,
239 gboolean resizable, gint sort_col_id, gboolean show_as_text,
240 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
242 GtkTreeViewColumn *column;
244 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
245 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
247 gtk_tree_view_column_set_resizable (column, resizable);
249 gtk_tree_view_column_set_expand (column, TRUE);
252 gtk_tree_view_column_add_attribute (column, renderer, "text",
254 if (sort_col_id >= 0)
255 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
257 gtk_tree_view_column_set_sort_indicator (column, FALSE);
258 gtk_tree_view_column_set_reorderable (column, TRUE);
261 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
268 remove_all_columns (ModestHeaderView *obj)
270 GList *columns, *cursor;
272 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
274 for (cursor = columns; cursor; cursor = cursor->next)
275 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
276 GTK_TREE_VIEW_COLUMN(cursor->data));
277 g_list_free (columns);
281 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
283 GtkTreeModel *tree_filter, *sortable;
284 GtkTreeViewColumn *column=NULL;
285 GtkTreeSelection *selection = NULL;
286 GtkCellRenderer *renderer_msgtype,*renderer_header,
287 *renderer_attach, *renderer_compact_date_or_status;
288 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
289 *renderer_subject, *renderer_subject_box, *renderer_recpt,
291 ModestHeaderViewPrivate *priv;
292 GtkTreeViewColumn *compact_column = NULL;
295 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
297 /* FIXME: check whether these renderers need to be freed */
298 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
299 renderer_attach = gtk_cell_renderer_pixbuf_new ();
300 renderer_priority = gtk_cell_renderer_pixbuf_new ();
301 renderer_header = gtk_cell_renderer_text_new ();
303 renderer_compact_header = modest_vbox_cell_renderer_new ();
304 renderer_recpt_box = modest_hbox_cell_renderer_new ();
305 renderer_subject_box = modest_hbox_cell_renderer_new ();
306 renderer_recpt = gtk_cell_renderer_text_new ();
307 renderer_subject = gtk_cell_renderer_text_new ();
308 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
310 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
311 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
312 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
313 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
314 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
315 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
316 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
317 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
318 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
319 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
320 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
321 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
322 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
323 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
325 g_object_set(G_OBJECT(renderer_header),
326 "ellipsize", PANGO_ELLIPSIZE_END,
328 g_object_set (G_OBJECT (renderer_subject),
329 "ellipsize", PANGO_ELLIPSIZE_END,
331 g_object_set (G_OBJECT (renderer_recpt),
332 "ellipsize", PANGO_ELLIPSIZE_END,
334 g_object_set(G_OBJECT(renderer_compact_date_or_status),
338 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 32);
339 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 32);
341 remove_all_columns (self);
343 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
344 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
345 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
346 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
348 /* Add new columns */
349 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
350 ModestHeaderViewColumn col =
351 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
353 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
354 g_printerr ("modest: invalid column %d in column list\n", col);
360 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
361 column = get_new_column (_("M"), renderer_msgtype, FALSE,
362 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
364 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
366 gtk_tree_view_column_set_fixed_width (column, 45);
369 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
370 column = get_new_column (_("A"), renderer_attach, FALSE,
371 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
373 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
375 gtk_tree_view_column_set_fixed_width (column, 45);
379 case MODEST_HEADER_VIEW_COLUMN_FROM:
380 column = get_new_column (_("From"), renderer_header, TRUE,
381 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
383 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
384 GINT_TO_POINTER(TRUE));
387 case MODEST_HEADER_VIEW_COLUMN_TO:
388 column = get_new_column (_("To"), renderer_header, TRUE,
389 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
391 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
392 GINT_TO_POINTER(FALSE));
395 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
396 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
397 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
399 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
400 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
401 compact_column = column;
404 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
405 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
406 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
408 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
409 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
410 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
411 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
412 compact_column = column;
416 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
417 column = get_new_column (_("Subject"), renderer_header, TRUE,
418 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
420 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
424 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
425 column = get_new_column (_("Received"), renderer_header, TRUE,
426 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
428 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
429 GINT_TO_POINTER(TRUE));
432 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
433 column = get_new_column (_("Sent"), renderer_header, TRUE,
434 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
436 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
437 GINT_TO_POINTER(FALSE));
440 case MODEST_HEADER_VIEW_COLUMN_SIZE:
441 column = get_new_column (_("Size"), renderer_header, TRUE,
442 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
444 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
447 case MODEST_HEADER_VIEW_COLUMN_STATUS:
448 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
449 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
451 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
456 g_return_val_if_reached(FALSE);
459 /* we keep the column id around */
460 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
461 GINT_TO_POINTER(col));
463 /* we need this ptr when sorting the rows */
464 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
466 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
470 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
471 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
472 (GtkTreeIterCompareFunc) cmp_rows,
473 compact_column, NULL);
474 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
475 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
476 (GtkTreeIterCompareFunc) cmp_subject_rows,
477 compact_column, NULL);
485 modest_header_view_init (ModestHeaderView *obj)
487 ModestHeaderViewPrivate *priv;
490 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
494 priv->monitor = NULL;
495 priv->observers_lock = g_mutex_new ();
497 priv->clipboard = modest_runtime_get_email_clipboard ();
498 priv->hidding_ids = NULL;
499 priv->n_selected = 0;
500 priv->selection_changed_handler = 0;
502 /* Sort parameters */
503 for (j=0; j < 2; j++) {
504 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
505 priv->sort_colid[j][i] = -1;
506 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
510 setup_drag_and_drop (GTK_TREE_VIEW (obj));
514 modest_header_view_dispose (GObject *obj)
516 ModestHeaderView *self;
517 ModestHeaderViewPrivate *priv;
519 self = MODEST_HEADER_VIEW(obj);
520 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
523 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
524 g_object_unref (G_OBJECT (priv->folder));
528 G_OBJECT_CLASS(parent_class)->dispose (obj);
532 modest_header_view_finalize (GObject *obj)
534 ModestHeaderView *self;
535 ModestHeaderViewPrivate *priv;
537 self = MODEST_HEADER_VIEW(obj);
538 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
540 if (priv->selection_changed_handler) {
541 g_signal_handler_disconnect (self, priv->selection_changed_handler);
542 priv->selection_changed_handler = 0;
545 g_mutex_lock (priv->observers_lock);
547 tny_folder_monitor_stop (priv->monitor);
548 g_object_unref (G_OBJECT (priv->monitor));
550 g_mutex_unlock (priv->observers_lock);
551 g_mutex_free (priv->observers_lock);
553 /* Clear hidding array created by cut operation */
554 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
556 G_OBJECT_CLASS(parent_class)->finalize (obj);
561 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
564 GtkTreeSelection *sel;
565 ModestHeaderView *self;
566 ModestHeaderViewPrivate *priv;
568 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
571 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
572 self = MODEST_HEADER_VIEW(obj);
573 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
575 modest_header_view_set_style (self, style);
576 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
578 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
579 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
580 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
582 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
583 TRUE); /* alternating row colors */
584 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
586 priv->selection_changed_handler =
587 g_signal_connect_after (sel, "changed",
588 G_CALLBACK(on_selection_changed), self);
590 g_signal_connect (self, "row-activated",
591 G_CALLBACK (on_header_row_activated), NULL);
593 g_signal_connect (self, "focus-in-event",
594 G_CALLBACK(on_focus_in), NULL);
596 return GTK_WIDGET(self);
601 modest_header_view_count_selected_headers (ModestHeaderView *self)
603 GtkTreeSelection *sel;
606 g_return_val_if_fail (self, 0);
608 /* Get selection object and check selected rows count */
609 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
610 selected_rows = gtk_tree_selection_count_selected_rows (sel);
612 return selected_rows;
616 modest_header_view_has_selected_headers (ModestHeaderView *self)
618 GtkTreeSelection *sel;
621 g_return_val_if_fail (self, FALSE);
623 /* Get selection object and check selected rows count */
624 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
625 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
632 modest_header_view_get_selected_headers (ModestHeaderView *self)
634 GtkTreeSelection *sel;
635 ModestHeaderViewPrivate *priv;
636 TnyList *header_list = NULL;
638 GList *list, *tmp = NULL;
639 GtkTreeModel *tree_model = NULL;
642 g_return_val_if_fail (self, NULL);
644 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
646 /* Get selected rows */
647 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
648 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
651 header_list = tny_simple_list_new();
653 list = g_list_reverse (list);
656 /* get header from selection */
657 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
658 gtk_tree_model_get (tree_model, &iter,
659 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
661 /* Prepend to list */
662 tny_list_prepend (header_list, G_OBJECT (header));
663 g_object_unref (G_OBJECT (header));
665 tmp = g_list_next (tmp);
668 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
675 /* scroll our list view so the selected item is visible */
677 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
679 #ifdef MODEST_PLATFORM_GNOME
681 GtkTreePath *selected_path;
682 GtkTreePath *start, *end;
686 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
687 selected_path = gtk_tree_model_get_path (model, iter);
689 start = gtk_tree_path_new ();
690 end = gtk_tree_path_new ();
692 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
694 if (gtk_tree_path_compare (selected_path, start) < 0 ||
695 gtk_tree_path_compare (end, selected_path) < 0)
696 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
697 selected_path, NULL, TRUE,
700 gtk_tree_path_free (selected_path);
701 gtk_tree_path_free (start);
702 gtk_tree_path_free (end);
704 #endif /* MODEST_PLATFORM_GNOME */
709 modest_header_view_select_next (ModestHeaderView *self)
711 GtkTreeSelection *sel;
716 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
717 path = get_selected_row (GTK_TREE_VIEW(self), &model);
718 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
719 /* Unselect previous path */
720 gtk_tree_selection_unselect_path (sel, path);
722 /* Move path down and selects new one */
723 if (gtk_tree_model_iter_next (model, &iter)) {
724 gtk_tree_selection_select_iter (sel, &iter);
725 scroll_to_selected (self, &iter, FALSE);
727 gtk_tree_path_free(path);
733 modest_header_view_select_prev (ModestHeaderView *self)
735 GtkTreeSelection *sel;
740 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
741 path = get_selected_row (GTK_TREE_VIEW(self), &model);
742 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
743 /* Unselect previous path */
744 gtk_tree_selection_unselect_path (sel, path);
747 if (gtk_tree_path_prev (path)) {
748 gtk_tree_model_get_iter (model, &iter, path);
750 /* Select the new one */
751 gtk_tree_selection_select_iter (sel, &iter);
752 scroll_to_selected (self, &iter, TRUE);
755 gtk_tree_path_free (path);
760 modest_header_view_get_columns (ModestHeaderView *self)
762 g_return_val_if_fail (self, FALSE);
763 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
768 modest_header_view_is_empty (ModestHeaderView *self)
770 g_return_val_if_fail (self, FALSE);
771 return FALSE; /* FIXME */
776 modest_header_view_set_style (ModestHeaderView *self,
777 ModestHeaderViewStyle style)
779 ModestHeaderViewPrivate *priv;
780 gboolean show_col_headers = FALSE;
781 ModestHeaderViewStyle old_style;
783 g_return_val_if_fail (self, FALSE);
784 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
787 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
788 if (priv->style == style)
789 return TRUE; /* nothing to do */
792 case MODEST_HEADER_VIEW_STYLE_DETAILS:
793 show_col_headers = TRUE;
795 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
798 g_return_val_if_reached (FALSE);
800 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
801 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
803 old_style = priv->style;
810 ModestHeaderViewStyle
811 modest_header_view_get_style (ModestHeaderView *self)
813 g_return_val_if_fail (self, FALSE);
814 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
818 * This function sets a sortable model in the header view. It's just
819 * used for developing purposes, because it only does a
820 * gtk_tree_view_set_model
823 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
825 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
826 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
827 /* GtkTreeModel *old_model; */
828 /* ModestHeaderViewPrivate *priv; */
829 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
830 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
832 /* /\* Set new model *\/ */
833 /* gtk_tree_view_set_model (header_view, model); */
835 gtk_tree_view_set_model (header_view, model);
839 modest_header_view_get_folder (ModestHeaderView *self)
841 ModestHeaderViewPrivate *priv;
842 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
845 g_object_ref (priv->folder);
851 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
855 ModestHeaderViewPrivate *priv;
856 GList *cols, *cursor;
857 GtkTreeModel *filter_model, *sortable;
859 GtkSortType sort_type;
861 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
863 headers = TNY_LIST (tny_gtk_header_list_model_new ());
865 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
868 /* Add IDLE observer (monitor) and another folder observer for
869 new messages (self) */
870 g_mutex_lock (priv->observers_lock);
872 tny_folder_monitor_stop (priv->monitor);
873 g_object_unref (G_OBJECT (priv->monitor));
875 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
876 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
877 tny_folder_monitor_start (priv->monitor);
878 g_mutex_unlock (priv->observers_lock);
880 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
881 g_object_unref (G_OBJECT (headers));
883 /* Create a tree model filter to hide and show rows for cut operations */
884 filter_model = gtk_tree_model_filter_new (sortable, NULL);
885 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
889 g_object_unref (G_OBJECT (sortable));
891 /* install our special sorting functions */
892 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
894 /* Restore sort column id */
896 type = modest_tny_folder_guess_folder_type (folder);
897 sort_colid = modest_header_view_get_sort_column_id (self, type);
898 sort_type = modest_header_view_get_sort_type (self, type);
899 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
902 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
903 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
904 (GtkTreeIterCompareFunc) cmp_rows,
906 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
907 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
908 (GtkTreeIterCompareFunc) cmp_subject_rows,
913 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
914 g_object_unref (G_OBJECT (filter_model));
915 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
916 /* g_object_unref (G_OBJECT (sortable)); */
923 modest_header_view_sort_by_column_id (ModestHeaderView *self,
925 GtkSortType sort_type)
927 ModestHeaderViewPrivate *priv = NULL;
928 GtkTreeModel *tree_filter, *sortable = NULL;
931 /* Get model and private data */
932 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
933 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
934 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
935 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
937 /* Sort tree model */
938 type = modest_tny_folder_guess_folder_type (priv->folder);
939 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
942 /* Store new sort parameters */
943 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
945 /* Save GConf parameters */
946 /* modest_widget_memory_save (modest_runtime_get_conf(), */
947 /* G_OBJECT(self), "header-view"); */
952 modest_header_view_set_sort_params (ModestHeaderView *self,
954 GtkSortType sort_type,
957 ModestHeaderViewPrivate *priv;
958 ModestHeaderViewStyle style;
960 style = modest_header_view_get_style (self);
961 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
963 priv->sort_colid[style][type] = sort_colid;
964 priv->sort_type[style][type] = sort_type;
968 modest_header_view_get_sort_column_id (ModestHeaderView *self,
971 ModestHeaderViewPrivate *priv;
972 ModestHeaderViewStyle style;
974 style = modest_header_view_get_style (self);
975 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
977 return priv->sort_colid[style][type];
981 modest_header_view_get_sort_type (ModestHeaderView *self,
984 ModestHeaderViewPrivate *priv;
985 ModestHeaderViewStyle style;
987 style = modest_header_view_get_style (self);
988 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
990 return priv->sort_type[style][type];
994 ModestHeaderView *header_view;
995 RefreshAsyncUserCallback cb;
1000 folder_refreshed_cb (ModestMailOperation *mail_op,
1004 ModestHeaderViewPrivate *priv;
1005 SetFolderHelper *info;
1007 info = (SetFolderHelper*) user_data;
1009 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1013 info->cb (mail_op, folder, info->user_data);
1015 /* Start the folder count changes observer. We do not need it
1016 before the refresh. Note that the monitor could still be
1017 called for this refresh but now we know that the callback
1018 was previously called */
1019 g_mutex_lock (priv->observers_lock);
1020 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1021 g_mutex_unlock (priv->observers_lock);
1028 modest_header_view_set_folder (ModestHeaderView *self,
1030 RefreshAsyncUserCallback callback,
1033 ModestHeaderViewPrivate *priv;
1034 ModestWindowMgr *mgr = NULL;
1035 GObject *source = NULL;
1036 SetFolderHelper *info;
1038 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1041 g_mutex_lock (priv->observers_lock);
1042 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1043 g_object_unref (priv->folder);
1044 priv->folder = NULL;
1045 g_mutex_unlock (priv->observers_lock);
1049 ModestMailOperation *mail_op = NULL;
1050 GtkTreeSelection *selection;
1052 /* Get main window to use it as source of mail operation */
1053 mgr = modest_runtime_get_window_mgr ();
1054 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1056 /* Set folder in the model */
1057 modest_header_view_set_folder_intern (self, folder);
1059 /* Pick my reference. Nothing to do with the mail operation */
1060 priv->folder = g_object_ref (folder);
1062 /* no message selected */
1063 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1065 info = g_malloc0 (sizeof(SetFolderHelper));
1066 info->header_view = self;
1067 info->cb = callback;
1068 info->user_data = user_data;
1070 /* bug 57631: Clear the selection if exists */
1071 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1072 gtk_tree_selection_unselect_all(selection);
1074 /* Create the mail operation (source will be the parent widget) */
1075 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1076 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1079 /* Refresh the folder asynchronously */
1080 modest_mail_operation_refresh_folder (mail_op,
1082 folder_refreshed_cb,
1086 g_object_unref (mail_op);
1088 g_mutex_lock (priv->observers_lock);
1090 if (priv->monitor) {
1091 tny_folder_monitor_stop (priv->monitor);
1092 g_object_unref (G_OBJECT (priv->monitor));
1093 priv->monitor = NULL;
1095 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1097 g_mutex_unlock (priv->observers_lock);
1102 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1103 GtkTreeViewColumn *column, gpointer userdata)
1105 ModestHeaderView *self = NULL;
1106 ModestHeaderViewPrivate *priv = NULL;
1108 GtkTreeModel *model = NULL;
1109 TnyHeader *header = NULL;
1110 TnyHeaderFlags flags;
1112 self = MODEST_HEADER_VIEW (treeview);
1113 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1115 model = gtk_tree_view_get_model (treeview);
1116 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1119 /* get the first selected item */
1120 gtk_tree_model_get (model, &iter,
1121 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1122 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1125 /* Dont open DELETED messages */
1126 if (flags & TNY_HEADER_FLAG_DELETED) {
1127 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1132 g_signal_emit (G_OBJECT(self),
1133 signals[HEADER_ACTIVATED_SIGNAL],
1139 g_object_unref (G_OBJECT (header));
1144 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1146 GtkTreeModel *model;
1148 GtkTreePath *path = NULL;
1150 ModestHeaderView *self;
1151 ModestHeaderViewPrivate *priv;
1152 GList *selected = NULL;
1154 g_return_if_fail (sel);
1155 g_return_if_fail (user_data);
1157 self = MODEST_HEADER_VIEW (user_data);
1158 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1160 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1161 if (selected != NULL)
1162 path = (GtkTreePath *) selected->data;
1163 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1164 return; /* msg was _un_selected */
1166 gtk_tree_model_get (model, &iter,
1167 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1171 g_signal_emit (G_OBJECT(self),
1172 signals[HEADER_SELECTED_SIGNAL],
1175 g_object_unref (G_OBJECT (header));
1177 /* free all items in 'selected' */
1178 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1179 g_list_free (selected);
1183 /* PROTECTED method. It's useful when we want to force a given
1184 selection to reload a msg. For example if we have selected a header
1185 in offline mode, when Modest become online, we want to reload the
1186 message automatically without an user click over the header */
1188 _modest_header_view_change_selection (GtkTreeSelection *selection,
1191 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1192 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1194 on_selection_changed (selection, user_data);
1197 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1199 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1200 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1202 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1204 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1209 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1216 /* static int counter = 0; */
1218 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1219 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1220 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1224 case TNY_HEADER_FLAG_ATTACHMENTS:
1226 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1227 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1228 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1229 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1231 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1232 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1234 return cmp ? cmp : t1 - t2;
1236 case TNY_HEADER_FLAG_PRIORITY:
1237 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1238 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1239 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1240 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1242 /* This is for making priority values respect the intuitive sort relationship
1243 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1244 cmp = compare_priorities (val1, val2);
1246 return cmp ? cmp : t1 - t2;
1249 return &iter1 - &iter2; /* oughhhh */
1254 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1260 /* static int counter = 0; */
1262 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1264 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1265 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1266 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1267 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1269 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1270 val2 + modest_text_utils_get_subject_prefix_len(val2),
1277 /* Drag and drop stuff */
1279 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1280 GtkSelectionData *selection_data,
1281 guint info, guint time, gpointer data)
1283 GtkTreeModel *model = NULL;
1285 GtkTreePath *source_row = NULL;
1286 GtkTreeSelection *sel = NULL;
1288 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1289 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1292 case MODEST_HEADER_ROW:
1293 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1297 gtk_tree_model_get (model, &iter,
1298 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1301 g_object_unref (G_OBJECT(hdr));
1306 g_message ("%s: default switch case.", __FUNCTION__);
1309 /* Set focus on next header */
1310 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1311 gtk_tree_path_next (source_row);
1312 gtk_tree_selection_select_path (sel, source_row);
1314 gtk_tree_path_free (source_row);
1317 /* Header view drag types */
1318 const GtkTargetEntry header_view_drag_types[] = {
1319 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1320 { "text/uri-list", 0, MODEST_MSG },
1324 setup_drag_and_drop (GtkTreeView *self)
1326 gtk_drag_source_set (GTK_WIDGET (self),
1328 header_view_drag_types,
1329 G_N_ELEMENTS (header_view_drag_types),
1330 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1332 g_signal_connect(G_OBJECT (self), "drag_data_get",
1333 G_CALLBACK(drag_data_get_cb), NULL);
1336 static GtkTreePath *
1337 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1339 GtkTreePath *path = NULL;
1340 GtkTreeSelection *sel = NULL;
1343 sel = gtk_tree_view_get_selection(self);
1344 rows = gtk_tree_selection_get_selected_rows (sel, model);
1346 if ((rows == NULL) || (g_list_length(rows) != 1))
1349 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1354 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1361 * This function moves the tree view scroll to the current selected
1362 * row when the widget grabs the focus
1365 on_focus_in (GtkWidget *self,
1366 GdkEventFocus *event,
1369 GtkTreeSelection *selection;
1370 GtkTreeModel *model;
1371 GList *selected = NULL;
1372 GtkTreePath *selected_path = NULL;
1374 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1378 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1379 /* If none selected yet, pick the first one */
1380 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1384 /* Return if the model is empty */
1385 if (!gtk_tree_model_get_iter_first (model, &iter))
1388 path = gtk_tree_model_get_path (model, &iter);
1389 gtk_tree_selection_select_path (selection, path);
1390 gtk_tree_path_free (path);
1393 /* Need to get the all the rows because is selection multiple */
1394 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1395 if (selected == NULL) return FALSE;
1396 selected_path = (GtkTreePath *) selected->data;
1398 /* Check if we need to scroll */
1399 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1400 GtkTreePath *start_path = NULL;
1401 GtkTreePath *end_path = NULL;
1402 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1406 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1407 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1409 /* Scroll to first path */
1410 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1419 gtk_tree_path_free (start_path);
1421 gtk_tree_path_free (end_path);
1423 #endif /* GTK_CHECK_VERSION */
1426 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1427 g_list_free (selected);
1433 idle_notify_headers_count_changed_destroy (gpointer data)
1435 HeadersCountChangedHelper *helper = NULL;
1437 g_return_if_fail (data != NULL);
1438 helper = (HeadersCountChangedHelper *) data;
1440 g_object_unref (helper->change);
1441 g_slice_free (HeadersCountChangedHelper, helper);
1445 idle_notify_headers_count_changed (gpointer data)
1447 TnyFolder *folder = NULL;
1448 ModestHeaderViewPrivate *priv = NULL;
1449 HeadersCountChangedHelper *helper = NULL;
1451 g_return_val_if_fail (data != NULL, FALSE);
1452 helper = (HeadersCountChangedHelper *) data;
1453 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1454 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1456 folder = tny_folder_change_get_folder (helper->change);
1458 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1460 g_mutex_lock (priv->observers_lock);
1462 /* Emit signal to evaluate how headers changes affects to the window view */
1463 g_signal_emit (G_OBJECT(helper->self),
1464 signals[MSG_COUNT_CHANGED_SIGNAL],
1465 0, folder, helper->change);
1467 /* Added or removed headers, so data stored on cliboard are invalid */
1468 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1469 modest_email_clipboard_clear (priv->clipboard);
1471 g_mutex_unlock (priv->observers_lock);
1477 folder_monitor_update (TnyFolderObserver *self,
1478 TnyFolderChange *change)
1480 ModestHeaderViewPrivate *priv;
1481 TnyFolderChangeChanged changed;
1482 HeadersCountChangedHelper *helper = NULL;
1484 changed = tny_folder_change_get_changed (change);
1486 /* Do not notify the observers if the folder of the header
1487 view has changed before this call to the observer
1489 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1490 if (tny_folder_change_get_folder (change) != priv->folder)
1493 /* Check folder count */
1494 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1495 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1496 helper = g_slice_new0 (HeadersCountChangedHelper);
1497 helper->self = MODEST_HEADER_VIEW(self);
1498 helper->change = g_object_ref(change);
1500 g_idle_add_full (G_PRIORITY_DEFAULT,
1501 idle_notify_headers_count_changed,
1503 idle_notify_headers_count_changed_destroy);
1508 modest_header_view_clear (ModestHeaderView *self)
1510 modest_header_view_set_folder (self, NULL, NULL, NULL);
1514 modest_header_view_copy_selection (ModestHeaderView *header_view)
1516 /* Copy selection */
1517 _clipboard_set_selected_data (header_view, FALSE);
1521 modest_header_view_cut_selection (ModestHeaderView *header_view)
1523 ModestHeaderViewPrivate *priv = NULL;
1524 const gchar **hidding = NULL;
1525 guint i, n_selected;
1527 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1528 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1530 /* Copy selection */
1531 _clipboard_set_selected_data (header_view, TRUE);
1533 /* Get hidding ids */
1534 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1536 /* Clear hidding array created by previous cut operation */
1537 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1539 /* Copy hidding array */
1540 priv->n_selected = n_selected;
1541 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1542 for (i=0; i < n_selected; i++)
1543 priv->hidding_ids[i] = g_strdup(hidding[i]);
1545 /* Hide cut headers */
1546 modest_header_view_refilter (header_view);
1553 _clipboard_set_selected_data (ModestHeaderView *header_view,
1556 ModestHeaderViewPrivate *priv = NULL;
1557 TnyList *headers = NULL;
1559 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1560 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1562 /* Set selected data on clipboard */
1563 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1564 headers = modest_header_view_get_selected_headers (header_view);
1565 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1568 g_object_unref (headers);
1574 filter_row (GtkTreeModel *model,
1578 ModestHeaderViewPrivate *priv = NULL;
1579 TnyHeaderFlags flags;
1580 TnyHeader *header = NULL;
1583 gboolean visible = TRUE;
1584 gboolean found = FALSE;
1586 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1587 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1589 /* Get header from model */
1590 gtk_tree_model_get (model, iter,
1591 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1592 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1595 /* Hide mark as deleted heders */
1596 if (flags & TNY_HEADER_FLAG_DELETED) {
1601 /* If no data on clipboard, return always TRUE */
1602 if (modest_email_clipboard_cleared(priv->clipboard)) {
1607 /* Get message id from header (ensure is a valid id) */
1608 if (!header) return FALSE;
1609 id = g_strdup(tny_header_get_message_id (header));
1612 if (priv->hidding_ids != NULL) {
1613 for (i=0; i < priv->n_selected && !found; i++)
1614 if (priv->hidding_ids[i] != NULL && id != NULL)
1615 found = (!strcmp (priv->hidding_ids[i], id));
1623 g_object_unref (header);
1630 _clear_hidding_filter (ModestHeaderView *header_view)
1632 ModestHeaderViewPrivate *priv;
1635 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1636 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1638 if (priv->hidding_ids != NULL) {
1639 for (i=0; i < priv->n_selected; i++)
1640 g_free (priv->hidding_ids[i]);
1641 g_free(priv->hidding_ids);
1646 modest_header_view_refilter (ModestHeaderView *header_view)
1648 GtkTreeModel *model;
1650 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1652 /* Hide cut headers */
1653 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1654 if (GTK_IS_TREE_MODEL_FILTER (model))
1655 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1659 * Protected method, selects a row pointed by path
1662 _modest_header_view_select_from_path (ModestHeaderView *self,
1665 GtkTreeSelection *selection = NULL;
1666 ModestHeaderViewPrivate *priv;
1668 g_return_if_fail (MODEST_HEADER_VIEW (self));
1669 g_return_if_fail (path != NULL);
1671 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1672 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1674 /* Unselect previous selection */
1675 gtk_tree_selection_unselect_all (selection);
1677 /* Select new path*/
1678 gtk_tree_selection_select_path (selection, path);