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_subject_box), "yalign", 1.0, NULL);
326 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
327 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
328 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
329 g_object_set(G_OBJECT(renderer_header),
330 "ellipsize", PANGO_ELLIPSIZE_END,
332 g_object_set (G_OBJECT (renderer_subject),
333 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
335 g_object_set (G_OBJECT (renderer_recpt),
336 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
338 g_object_set(G_OBJECT(renderer_compact_date_or_status),
339 "xalign", 1.0, "yalign", 0.0,
341 g_object_set (G_OBJECT (renderer_priority),
342 "yalign", 1.0, NULL);
343 g_object_set (G_OBJECT (renderer_attach),
344 "yalign", 0.0, NULL);
346 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
347 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
348 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
350 remove_all_columns (self);
352 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
353 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
354 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
357 /* Add new columns */
358 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
359 ModestHeaderViewColumn col =
360 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
362 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
363 g_printerr ("modest: invalid column %d in column list\n", col);
369 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
370 column = get_new_column (_("M"), renderer_msgtype, FALSE,
371 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
373 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
375 gtk_tree_view_column_set_fixed_width (column, 45);
378 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
379 column = get_new_column (_("A"), renderer_attach, FALSE,
380 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
382 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
384 gtk_tree_view_column_set_fixed_width (column, 45);
388 case MODEST_HEADER_VIEW_COLUMN_FROM:
389 column = get_new_column (_("From"), renderer_header, TRUE,
390 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
392 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
393 GINT_TO_POINTER(TRUE));
396 case MODEST_HEADER_VIEW_COLUMN_TO:
397 column = get_new_column (_("To"), renderer_header, TRUE,
398 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
400 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
401 GINT_TO_POINTER(FALSE));
404 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
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(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
410 compact_column = column;
413 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
414 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
415 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
417 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
418 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
419 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
420 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
421 compact_column = column;
425 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
426 column = get_new_column (_("Subject"), renderer_header, TRUE,
427 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
429 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
433 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
434 column = get_new_column (_("Received"), renderer_header, TRUE,
435 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
437 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
438 GINT_TO_POINTER(TRUE));
441 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
442 column = get_new_column (_("Sent"), renderer_header, TRUE,
443 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
445 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
446 GINT_TO_POINTER(FALSE));
449 case MODEST_HEADER_VIEW_COLUMN_SIZE:
450 column = get_new_column (_("Size"), renderer_header, TRUE,
451 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
453 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
456 case MODEST_HEADER_VIEW_COLUMN_STATUS:
457 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
465 g_return_val_if_reached(FALSE);
468 /* we keep the column id around */
469 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
470 GINT_TO_POINTER(col));
472 /* we need this ptr when sorting the rows */
473 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
475 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
479 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
480 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
481 (GtkTreeIterCompareFunc) cmp_rows,
482 compact_column, NULL);
483 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
484 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
485 (GtkTreeIterCompareFunc) cmp_subject_rows,
486 compact_column, NULL);
494 modest_header_view_init (ModestHeaderView *obj)
496 ModestHeaderViewPrivate *priv;
499 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
503 priv->monitor = NULL;
504 priv->observers_lock = g_mutex_new ();
506 priv->clipboard = modest_runtime_get_email_clipboard ();
507 priv->hidding_ids = NULL;
508 priv->n_selected = 0;
509 priv->selection_changed_handler = 0;
511 /* Sort parameters */
512 for (j=0; j < 2; j++) {
513 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
514 priv->sort_colid[j][i] = -1;
515 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
519 setup_drag_and_drop (GTK_TREE_VIEW (obj));
523 modest_header_view_dispose (GObject *obj)
525 ModestHeaderView *self;
526 ModestHeaderViewPrivate *priv;
528 self = MODEST_HEADER_VIEW(obj);
529 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
532 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
533 g_object_unref (G_OBJECT (priv->folder));
537 G_OBJECT_CLASS(parent_class)->dispose (obj);
541 modest_header_view_finalize (GObject *obj)
543 ModestHeaderView *self;
544 ModestHeaderViewPrivate *priv;
546 self = MODEST_HEADER_VIEW(obj);
547 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
549 if (priv->selection_changed_handler) {
550 g_signal_handler_disconnect (self, priv->selection_changed_handler);
551 priv->selection_changed_handler = 0;
554 g_mutex_lock (priv->observers_lock);
556 tny_folder_monitor_stop (priv->monitor);
557 g_object_unref (G_OBJECT (priv->monitor));
559 g_mutex_unlock (priv->observers_lock);
560 g_mutex_free (priv->observers_lock);
562 /* Clear hidding array created by cut operation */
563 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
565 G_OBJECT_CLASS(parent_class)->finalize (obj);
570 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
573 GtkTreeSelection *sel;
574 ModestHeaderView *self;
575 ModestHeaderViewPrivate *priv;
577 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
580 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
581 self = MODEST_HEADER_VIEW(obj);
582 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
584 modest_header_view_set_style (self, style);
585 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
587 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
588 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
589 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
591 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
592 TRUE); /* alternating row colors */
593 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
595 priv->selection_changed_handler =
596 g_signal_connect_after (sel, "changed",
597 G_CALLBACK(on_selection_changed), self);
599 g_signal_connect (self, "row-activated",
600 G_CALLBACK (on_header_row_activated), NULL);
602 g_signal_connect (self, "focus-in-event",
603 G_CALLBACK(on_focus_in), NULL);
605 return GTK_WIDGET(self);
610 modest_header_view_count_selected_headers (ModestHeaderView *self)
612 GtkTreeSelection *sel;
615 g_return_val_if_fail (self, 0);
617 /* Get selection object and check selected rows count */
618 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
619 selected_rows = gtk_tree_selection_count_selected_rows (sel);
621 return selected_rows;
625 modest_header_view_has_selected_headers (ModestHeaderView *self)
627 GtkTreeSelection *sel;
630 g_return_val_if_fail (self, FALSE);
632 /* Get selection object and check selected rows count */
633 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
634 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
641 modest_header_view_get_selected_headers (ModestHeaderView *self)
643 GtkTreeSelection *sel;
644 ModestHeaderViewPrivate *priv;
645 TnyList *header_list = NULL;
647 GList *list, *tmp = NULL;
648 GtkTreeModel *tree_model = NULL;
651 g_return_val_if_fail (self, NULL);
653 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
655 /* Get selected rows */
656 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
657 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
660 header_list = tny_simple_list_new();
662 list = g_list_reverse (list);
665 /* get header from selection */
666 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
667 gtk_tree_model_get (tree_model, &iter,
668 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
670 /* Prepend to list */
671 tny_list_prepend (header_list, G_OBJECT (header));
672 g_object_unref (G_OBJECT (header));
674 tmp = g_list_next (tmp);
677 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
684 /* scroll our list view so the selected item is visible */
686 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
688 #ifdef MODEST_PLATFORM_GNOME
690 GtkTreePath *selected_path;
691 GtkTreePath *start, *end;
695 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
696 selected_path = gtk_tree_model_get_path (model, iter);
698 start = gtk_tree_path_new ();
699 end = gtk_tree_path_new ();
701 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
703 if (gtk_tree_path_compare (selected_path, start) < 0 ||
704 gtk_tree_path_compare (end, selected_path) < 0)
705 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
706 selected_path, NULL, TRUE,
709 gtk_tree_path_free (selected_path);
710 gtk_tree_path_free (start);
711 gtk_tree_path_free (end);
713 #endif /* MODEST_PLATFORM_GNOME */
718 modest_header_view_select_next (ModestHeaderView *self)
720 GtkTreeSelection *sel;
725 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
726 path = get_selected_row (GTK_TREE_VIEW(self), &model);
727 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
728 /* Unselect previous path */
729 gtk_tree_selection_unselect_path (sel, path);
731 /* Move path down and selects new one */
732 if (gtk_tree_model_iter_next (model, &iter)) {
733 gtk_tree_selection_select_iter (sel, &iter);
734 scroll_to_selected (self, &iter, FALSE);
736 gtk_tree_path_free(path);
742 modest_header_view_select_prev (ModestHeaderView *self)
744 GtkTreeSelection *sel;
749 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
750 path = get_selected_row (GTK_TREE_VIEW(self), &model);
751 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
752 /* Unselect previous path */
753 gtk_tree_selection_unselect_path (sel, path);
756 if (gtk_tree_path_prev (path)) {
757 gtk_tree_model_get_iter (model, &iter, path);
759 /* Select the new one */
760 gtk_tree_selection_select_iter (sel, &iter);
761 scroll_to_selected (self, &iter, TRUE);
764 gtk_tree_path_free (path);
769 modest_header_view_get_columns (ModestHeaderView *self)
771 g_return_val_if_fail (self, FALSE);
772 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
777 modest_header_view_is_empty (ModestHeaderView *self)
779 g_return_val_if_fail (self, FALSE);
780 return FALSE; /* FIXME */
785 modest_header_view_set_style (ModestHeaderView *self,
786 ModestHeaderViewStyle style)
788 ModestHeaderViewPrivate *priv;
789 gboolean show_col_headers = FALSE;
790 ModestHeaderViewStyle old_style;
792 g_return_val_if_fail (self, FALSE);
793 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
796 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
797 if (priv->style == style)
798 return TRUE; /* nothing to do */
801 case MODEST_HEADER_VIEW_STYLE_DETAILS:
802 show_col_headers = TRUE;
804 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
807 g_return_val_if_reached (FALSE);
809 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
810 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
812 old_style = priv->style;
819 ModestHeaderViewStyle
820 modest_header_view_get_style (ModestHeaderView *self)
822 g_return_val_if_fail (self, FALSE);
823 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
827 * This function sets a sortable model in the header view. It's just
828 * used for developing purposes, because it only does a
829 * gtk_tree_view_set_model
832 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
834 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
835 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
836 /* GtkTreeModel *old_model; */
837 /* ModestHeaderViewPrivate *priv; */
838 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
839 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
841 /* /\* Set new model *\/ */
842 /* gtk_tree_view_set_model (header_view, model); */
844 gtk_tree_view_set_model (header_view, model);
848 modest_header_view_get_folder (ModestHeaderView *self)
850 ModestHeaderViewPrivate *priv;
851 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
854 g_object_ref (priv->folder);
860 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
864 ModestHeaderViewPrivate *priv;
865 GList *cols, *cursor;
866 GtkTreeModel *filter_model, *sortable;
868 GtkSortType sort_type;
870 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
872 headers = TNY_LIST (tny_gtk_header_list_model_new ());
874 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
877 /* Add IDLE observer (monitor) and another folder observer for
878 new messages (self) */
879 g_mutex_lock (priv->observers_lock);
881 tny_folder_monitor_stop (priv->monitor);
882 g_object_unref (G_OBJECT (priv->monitor));
884 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
885 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
886 tny_folder_monitor_start (priv->monitor);
887 g_mutex_unlock (priv->observers_lock);
889 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
890 g_object_unref (G_OBJECT (headers));
892 /* Create a tree model filter to hide and show rows for cut operations */
893 filter_model = gtk_tree_model_filter_new (sortable, NULL);
894 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
898 g_object_unref (G_OBJECT (sortable));
900 /* install our special sorting functions */
901 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
903 /* Restore sort column id */
905 type = modest_tny_folder_guess_folder_type (folder);
906 sort_colid = modest_header_view_get_sort_column_id (self, type);
907 sort_type = modest_header_view_get_sort_type (self, type);
908 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
911 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
912 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
913 (GtkTreeIterCompareFunc) cmp_rows,
915 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
916 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
917 (GtkTreeIterCompareFunc) cmp_subject_rows,
922 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
923 g_object_unref (G_OBJECT (filter_model));
924 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
925 /* g_object_unref (G_OBJECT (sortable)); */
932 modest_header_view_sort_by_column_id (ModestHeaderView *self,
934 GtkSortType sort_type)
936 ModestHeaderViewPrivate *priv = NULL;
937 GtkTreeModel *tree_filter, *sortable = NULL;
940 /* Get model and private data */
941 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
942 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
943 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
944 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
946 /* Sort tree model */
947 type = modest_tny_folder_guess_folder_type (priv->folder);
948 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
951 /* Store new sort parameters */
952 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
954 /* Save GConf parameters */
955 /* modest_widget_memory_save (modest_runtime_get_conf(), */
956 /* G_OBJECT(self), "header-view"); */
961 modest_header_view_set_sort_params (ModestHeaderView *self,
963 GtkSortType sort_type,
966 ModestHeaderViewPrivate *priv;
967 ModestHeaderViewStyle style;
969 style = modest_header_view_get_style (self);
970 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
972 priv->sort_colid[style][type] = sort_colid;
973 priv->sort_type[style][type] = sort_type;
977 modest_header_view_get_sort_column_id (ModestHeaderView *self,
980 ModestHeaderViewPrivate *priv;
981 ModestHeaderViewStyle style;
983 style = modest_header_view_get_style (self);
984 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
986 return priv->sort_colid[style][type];
990 modest_header_view_get_sort_type (ModestHeaderView *self,
993 ModestHeaderViewPrivate *priv;
994 ModestHeaderViewStyle style;
996 style = modest_header_view_get_style (self);
997 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
999 return priv->sort_type[style][type];
1003 ModestHeaderView *header_view;
1004 RefreshAsyncUserCallback cb;
1009 folder_refreshed_cb (ModestMailOperation *mail_op,
1013 ModestHeaderViewPrivate *priv;
1014 SetFolderHelper *info;
1016 info = (SetFolderHelper*) user_data;
1018 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1022 info->cb (mail_op, folder, info->user_data);
1024 /* Start the folder count changes observer. We do not need it
1025 before the refresh. Note that the monitor could still be
1026 called for this refresh but now we know that the callback
1027 was previously called */
1028 g_mutex_lock (priv->observers_lock);
1029 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1030 g_mutex_unlock (priv->observers_lock);
1037 modest_header_view_set_folder (ModestHeaderView *self,
1039 RefreshAsyncUserCallback callback,
1042 ModestHeaderViewPrivate *priv;
1043 ModestWindowMgr *mgr = NULL;
1044 GObject *source = NULL;
1045 SetFolderHelper *info;
1047 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1050 g_mutex_lock (priv->observers_lock);
1051 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1052 g_object_unref (priv->folder);
1053 priv->folder = NULL;
1054 g_mutex_unlock (priv->observers_lock);
1058 ModestMailOperation *mail_op = NULL;
1059 GtkTreeSelection *selection;
1061 /* Get main window to use it as source of mail operation */
1062 mgr = modest_runtime_get_window_mgr ();
1063 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1065 /* Set folder in the model */
1066 modest_header_view_set_folder_intern (self, folder);
1068 /* Pick my reference. Nothing to do with the mail operation */
1069 priv->folder = g_object_ref (folder);
1071 /* no message selected */
1072 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1074 info = g_malloc0 (sizeof(SetFolderHelper));
1075 info->header_view = self;
1076 info->cb = callback;
1077 info->user_data = user_data;
1079 /* bug 57631: Clear the selection if exists */
1080 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1081 gtk_tree_selection_unselect_all(selection);
1083 /* Create the mail operation (source will be the parent widget) */
1084 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1085 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1088 /* Refresh the folder asynchronously */
1089 modest_mail_operation_refresh_folder (mail_op,
1091 folder_refreshed_cb,
1095 g_object_unref (mail_op);
1097 g_mutex_lock (priv->observers_lock);
1099 if (priv->monitor) {
1100 tny_folder_monitor_stop (priv->monitor);
1101 g_object_unref (G_OBJECT (priv->monitor));
1102 priv->monitor = NULL;
1104 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1106 g_mutex_unlock (priv->observers_lock);
1111 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1112 GtkTreeViewColumn *column, gpointer userdata)
1114 ModestHeaderView *self = NULL;
1115 ModestHeaderViewPrivate *priv = NULL;
1117 GtkTreeModel *model = NULL;
1118 TnyHeader *header = NULL;
1119 TnyHeaderFlags flags;
1121 self = MODEST_HEADER_VIEW (treeview);
1122 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1124 model = gtk_tree_view_get_model (treeview);
1125 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1128 /* get the first selected item */
1129 gtk_tree_model_get (model, &iter,
1130 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1131 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1134 /* Dont open DELETED messages */
1135 if (flags & TNY_HEADER_FLAG_DELETED) {
1136 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1141 g_signal_emit (G_OBJECT(self),
1142 signals[HEADER_ACTIVATED_SIGNAL],
1148 g_object_unref (G_OBJECT (header));
1153 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1155 GtkTreeModel *model;
1156 TnyHeader *header = NULL;
1157 GtkTreePath *path = NULL;
1159 ModestHeaderView *self;
1160 ModestHeaderViewPrivate *priv;
1161 GList *selected = NULL;
1163 g_return_if_fail (sel);
1164 g_return_if_fail (user_data);
1166 self = MODEST_HEADER_VIEW (user_data);
1167 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1169 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1170 if (selected != NULL)
1171 path = (GtkTreePath *) selected->data;
1172 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1173 return; /* msg was _un_selected */
1175 gtk_tree_model_get (model, &iter,
1176 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1180 g_signal_emit (G_OBJECT(self),
1181 signals[HEADER_SELECTED_SIGNAL],
1184 g_object_unref (G_OBJECT (header));
1186 /* free all items in 'selected' */
1187 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1188 g_list_free (selected);
1192 /* PROTECTED method. It's useful when we want to force a given
1193 selection to reload a msg. For example if we have selected a header
1194 in offline mode, when Modest become online, we want to reload the
1195 message automatically without an user click over the header */
1197 _modest_header_view_change_selection (GtkTreeSelection *selection,
1200 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1201 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1203 on_selection_changed (selection, user_data);
1206 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1208 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1209 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1211 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1213 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1218 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1225 /* static int counter = 0; */
1227 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1228 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1229 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1233 case TNY_HEADER_FLAG_ATTACHMENTS:
1235 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1236 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1237 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1238 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1240 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1241 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1243 return cmp ? cmp : t1 - t2;
1245 case TNY_HEADER_FLAG_PRIORITY:
1246 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1247 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1248 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1249 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1251 /* This is for making priority values respect the intuitive sort relationship
1252 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1253 cmp = compare_priorities (val1, val2);
1255 return cmp ? cmp : t1 - t2;
1258 return &iter1 - &iter2; /* oughhhh */
1263 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1269 /* static int counter = 0; */
1271 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1273 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1274 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1275 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1276 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1278 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1279 val2 + modest_text_utils_get_subject_prefix_len(val2),
1286 /* Drag and drop stuff */
1288 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1289 GtkSelectionData *selection_data,
1290 guint info, guint time, gpointer data)
1292 GtkTreeModel *model = NULL;
1294 GtkTreePath *source_row = NULL;
1295 GtkTreeSelection *sel = NULL;
1297 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1298 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1301 case MODEST_HEADER_ROW:
1302 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1305 TnyHeader *hdr = NULL;
1306 gtk_tree_model_get (model, &iter,
1307 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1310 g_object_unref (G_OBJECT(hdr));
1315 g_message ("%s: default switch case.", __FUNCTION__);
1318 /* Set focus on next header */
1319 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1320 gtk_tree_path_next (source_row);
1321 gtk_tree_selection_select_path (sel, source_row);
1323 gtk_tree_path_free (source_row);
1326 /* Header view drag types */
1327 const GtkTargetEntry header_view_drag_types[] = {
1328 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1329 { "text/uri-list", 0, MODEST_MSG },
1333 setup_drag_and_drop (GtkTreeView *self)
1335 gtk_drag_source_set (GTK_WIDGET (self),
1337 header_view_drag_types,
1338 G_N_ELEMENTS (header_view_drag_types),
1339 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1341 g_signal_connect(G_OBJECT (self), "drag_data_get",
1342 G_CALLBACK(drag_data_get_cb), NULL);
1345 static GtkTreePath *
1346 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1348 GtkTreePath *path = NULL;
1349 GtkTreeSelection *sel = NULL;
1352 sel = gtk_tree_view_get_selection(self);
1353 rows = gtk_tree_selection_get_selected_rows (sel, model);
1355 if ((rows == NULL) || (g_list_length(rows) != 1))
1358 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1363 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1370 * This function moves the tree view scroll to the current selected
1371 * row when the widget grabs the focus
1374 on_focus_in (GtkWidget *self,
1375 GdkEventFocus *event,
1378 GtkTreeSelection *selection;
1379 GtkTreeModel *model;
1380 GList *selected = NULL;
1381 GtkTreePath *selected_path = NULL;
1383 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1387 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1388 /* If none selected yet, pick the first one */
1389 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1393 /* Return if the model is empty */
1394 if (!gtk_tree_model_get_iter_first (model, &iter))
1397 path = gtk_tree_model_get_path (model, &iter);
1398 gtk_tree_selection_select_path (selection, path);
1399 gtk_tree_path_free (path);
1402 /* Need to get the all the rows because is selection multiple */
1403 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1404 if (selected == NULL) return FALSE;
1405 selected_path = (GtkTreePath *) selected->data;
1407 /* Check if we need to scroll */
1408 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1409 GtkTreePath *start_path = NULL;
1410 GtkTreePath *end_path = NULL;
1411 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1415 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1416 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1418 /* Scroll to first path */
1419 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1428 gtk_tree_path_free (start_path);
1430 gtk_tree_path_free (end_path);
1432 #endif /* GTK_CHECK_VERSION */
1435 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1436 g_list_free (selected);
1442 idle_notify_headers_count_changed_destroy (gpointer data)
1444 HeadersCountChangedHelper *helper = NULL;
1446 g_return_if_fail (data != NULL);
1447 helper = (HeadersCountChangedHelper *) data;
1449 g_object_unref (helper->change);
1450 g_slice_free (HeadersCountChangedHelper, helper);
1454 idle_notify_headers_count_changed (gpointer data)
1456 TnyFolder *folder = NULL;
1457 ModestHeaderViewPrivate *priv = NULL;
1458 HeadersCountChangedHelper *helper = NULL;
1460 g_return_val_if_fail (data != NULL, FALSE);
1461 helper = (HeadersCountChangedHelper *) data;
1462 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1463 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1465 folder = tny_folder_change_get_folder (helper->change);
1467 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1469 g_mutex_lock (priv->observers_lock);
1471 /* Emit signal to evaluate how headers changes affects to the window view */
1472 g_signal_emit (G_OBJECT(helper->self),
1473 signals[MSG_COUNT_CHANGED_SIGNAL],
1474 0, folder, helper->change);
1476 /* Added or removed headers, so data stored on cliboard are invalid */
1477 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1478 modest_email_clipboard_clear (priv->clipboard);
1480 g_mutex_unlock (priv->observers_lock);
1486 folder_monitor_update (TnyFolderObserver *self,
1487 TnyFolderChange *change)
1489 ModestHeaderViewPrivate *priv;
1490 TnyFolderChangeChanged changed;
1491 HeadersCountChangedHelper *helper = NULL;
1493 changed = tny_folder_change_get_changed (change);
1495 /* Do not notify the observers if the folder of the header
1496 view has changed before this call to the observer
1498 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1499 if (tny_folder_change_get_folder (change) != priv->folder)
1502 /* Check header removed (hide marked as DELETED headers) */
1503 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS) {
1504 modest_header_view_refilter (MODEST_HEADER_VIEW(self));
1507 /* Check folder count */
1508 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1509 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1510 helper = g_slice_new0 (HeadersCountChangedHelper);
1511 helper->self = MODEST_HEADER_VIEW(self);
1512 helper->change = g_object_ref(change);
1514 g_idle_add_full (G_PRIORITY_DEFAULT,
1515 idle_notify_headers_count_changed,
1517 idle_notify_headers_count_changed_destroy);
1522 modest_header_view_clear (ModestHeaderView *self)
1524 modest_header_view_set_folder (self, NULL, NULL, NULL);
1528 modest_header_view_copy_selection (ModestHeaderView *header_view)
1530 /* Copy selection */
1531 _clipboard_set_selected_data (header_view, FALSE);
1535 modest_header_view_cut_selection (ModestHeaderView *header_view)
1537 ModestHeaderViewPrivate *priv = NULL;
1538 const gchar **hidding = NULL;
1539 guint i, n_selected;
1541 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1542 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1544 /* Copy selection */
1545 _clipboard_set_selected_data (header_view, TRUE);
1547 /* Get hidding ids */
1548 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1550 /* Clear hidding array created by previous cut operation */
1551 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1553 /* Copy hidding array */
1554 priv->n_selected = n_selected;
1555 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1556 for (i=0; i < n_selected; i++)
1557 priv->hidding_ids[i] = g_strdup(hidding[i]);
1559 /* Hide cut headers */
1560 modest_header_view_refilter (header_view);
1567 _clipboard_set_selected_data (ModestHeaderView *header_view,
1570 ModestHeaderViewPrivate *priv = NULL;
1571 TnyList *headers = NULL;
1573 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1574 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1576 /* Set selected data on clipboard */
1577 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1578 headers = modest_header_view_get_selected_headers (header_view);
1579 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1582 g_object_unref (headers);
1588 filter_row (GtkTreeModel *model,
1592 ModestHeaderViewPrivate *priv = NULL;
1593 TnyHeaderFlags flags;
1594 TnyHeader *header = NULL;
1597 gboolean visible = TRUE;
1598 gboolean found = FALSE;
1600 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1601 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1603 /* Get header from model */
1604 gtk_tree_model_get (model, iter,
1605 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1606 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1609 /* Hide mark as deleted heders */
1610 if (flags & TNY_HEADER_FLAG_DELETED) {
1615 /* If no data on clipboard, return always TRUE */
1616 if (modest_email_clipboard_cleared(priv->clipboard)) {
1621 /* Get message id from header (ensure is a valid id) */
1622 if (!header) return FALSE;
1623 id = g_strdup(tny_header_get_message_id (header));
1626 if (priv->hidding_ids != NULL) {
1627 for (i=0; i < priv->n_selected && !found; i++)
1628 if (priv->hidding_ids[i] != NULL && id != NULL)
1629 found = (!strcmp (priv->hidding_ids[i], id));
1637 g_object_unref (header);
1644 _clear_hidding_filter (ModestHeaderView *header_view)
1646 ModestHeaderViewPrivate *priv;
1649 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1650 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1652 if (priv->hidding_ids != NULL) {
1653 for (i=0; i < priv->n_selected; i++)
1654 g_free (priv->hidding_ids[i]);
1655 g_free(priv->hidding_ids);
1660 modest_header_view_refilter (ModestHeaderView *header_view)
1662 GtkTreeModel *model;
1664 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1666 /* Hide cut headers */
1667 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1668 if (GTK_IS_TREE_MODEL_FILTER (model))
1669 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));