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, "yalign", 1.0,
331 g_object_set (G_OBJECT (renderer_recpt),
332 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
334 g_object_set(G_OBJECT(renderer_compact_date_or_status),
335 "xalign", 1.0, "yalign", 0.0,
337 g_object_set (G_OBJECT (renderer_priority),
338 "yalign", 1.0, NULL);
339 g_object_set (G_OBJECT (renderer_attach),
340 "yalign", 0.0, NULL);
342 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 32);
343 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 32);
345 remove_all_columns (self);
347 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
348 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
349 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
350 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
352 /* Add new columns */
353 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
354 ModestHeaderViewColumn col =
355 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
357 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
358 g_printerr ("modest: invalid column %d in column list\n", col);
364 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
365 column = get_new_column (_("M"), renderer_msgtype, FALSE,
366 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
368 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
370 gtk_tree_view_column_set_fixed_width (column, 45);
373 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
374 column = get_new_column (_("A"), renderer_attach, FALSE,
375 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
377 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
379 gtk_tree_view_column_set_fixed_width (column, 45);
383 case MODEST_HEADER_VIEW_COLUMN_FROM:
384 column = get_new_column (_("From"), renderer_header, TRUE,
385 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
387 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
388 GINT_TO_POINTER(TRUE));
391 case MODEST_HEADER_VIEW_COLUMN_TO:
392 column = get_new_column (_("To"), renderer_header, TRUE,
393 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
395 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
396 GINT_TO_POINTER(FALSE));
399 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
400 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
401 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
403 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
404 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
405 compact_column = column;
408 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
409 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
410 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
412 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
413 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
414 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
415 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
416 compact_column = column;
420 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
421 column = get_new_column (_("Subject"), renderer_header, TRUE,
422 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
424 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
428 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
429 column = get_new_column (_("Received"), renderer_header, TRUE,
430 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
432 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
433 GINT_TO_POINTER(TRUE));
436 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
437 column = get_new_column (_("Sent"), renderer_header, TRUE,
438 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
440 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
441 GINT_TO_POINTER(FALSE));
444 case MODEST_HEADER_VIEW_COLUMN_SIZE:
445 column = get_new_column (_("Size"), renderer_header, TRUE,
446 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
448 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
451 case MODEST_HEADER_VIEW_COLUMN_STATUS:
452 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
453 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
455 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
460 g_return_val_if_reached(FALSE);
463 /* we keep the column id around */
464 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
465 GINT_TO_POINTER(col));
467 /* we need this ptr when sorting the rows */
468 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
470 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
474 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
475 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
476 (GtkTreeIterCompareFunc) cmp_rows,
477 compact_column, NULL);
478 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
479 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
480 (GtkTreeIterCompareFunc) cmp_subject_rows,
481 compact_column, NULL);
489 modest_header_view_init (ModestHeaderView *obj)
491 ModestHeaderViewPrivate *priv;
494 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
498 priv->monitor = NULL;
499 priv->observers_lock = g_mutex_new ();
501 priv->clipboard = modest_runtime_get_email_clipboard ();
502 priv->hidding_ids = NULL;
503 priv->n_selected = 0;
504 priv->selection_changed_handler = 0;
506 /* Sort parameters */
507 for (j=0; j < 2; j++) {
508 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
509 priv->sort_colid[j][i] = -1;
510 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
514 setup_drag_and_drop (GTK_TREE_VIEW (obj));
518 modest_header_view_dispose (GObject *obj)
520 ModestHeaderView *self;
521 ModestHeaderViewPrivate *priv;
523 self = MODEST_HEADER_VIEW(obj);
524 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
527 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
528 g_object_unref (G_OBJECT (priv->folder));
532 G_OBJECT_CLASS(parent_class)->dispose (obj);
536 modest_header_view_finalize (GObject *obj)
538 ModestHeaderView *self;
539 ModestHeaderViewPrivate *priv;
541 self = MODEST_HEADER_VIEW(obj);
542 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
544 if (priv->selection_changed_handler) {
545 g_signal_handler_disconnect (self, priv->selection_changed_handler);
546 priv->selection_changed_handler = 0;
549 g_mutex_lock (priv->observers_lock);
551 tny_folder_monitor_stop (priv->monitor);
552 g_object_unref (G_OBJECT (priv->monitor));
554 g_mutex_unlock (priv->observers_lock);
555 g_mutex_free (priv->observers_lock);
557 /* Clear hidding array created by cut operation */
558 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
560 G_OBJECT_CLASS(parent_class)->finalize (obj);
565 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
568 GtkTreeSelection *sel;
569 ModestHeaderView *self;
570 ModestHeaderViewPrivate *priv;
572 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
575 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
576 self = MODEST_HEADER_VIEW(obj);
577 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
579 modest_header_view_set_style (self, style);
580 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
582 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
583 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
584 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
586 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
587 TRUE); /* alternating row colors */
588 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
590 priv->selection_changed_handler =
591 g_signal_connect_after (sel, "changed",
592 G_CALLBACK(on_selection_changed), self);
594 g_signal_connect (self, "row-activated",
595 G_CALLBACK (on_header_row_activated), NULL);
597 g_signal_connect (self, "focus-in-event",
598 G_CALLBACK(on_focus_in), NULL);
600 return GTK_WIDGET(self);
605 modest_header_view_count_selected_headers (ModestHeaderView *self)
607 GtkTreeSelection *sel;
610 g_return_val_if_fail (self, 0);
612 /* Get selection object and check selected rows count */
613 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
614 selected_rows = gtk_tree_selection_count_selected_rows (sel);
616 return selected_rows;
620 modest_header_view_has_selected_headers (ModestHeaderView *self)
622 GtkTreeSelection *sel;
625 g_return_val_if_fail (self, FALSE);
627 /* Get selection object and check selected rows count */
628 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
629 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
636 modest_header_view_get_selected_headers (ModestHeaderView *self)
638 GtkTreeSelection *sel;
639 ModestHeaderViewPrivate *priv;
640 TnyList *header_list = NULL;
642 GList *list, *tmp = NULL;
643 GtkTreeModel *tree_model = NULL;
646 g_return_val_if_fail (self, NULL);
648 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
650 /* Get selected rows */
651 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
652 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
655 header_list = tny_simple_list_new();
657 list = g_list_reverse (list);
660 /* get header from selection */
661 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
662 gtk_tree_model_get (tree_model, &iter,
663 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
665 /* Prepend to list */
666 tny_list_prepend (header_list, G_OBJECT (header));
667 g_object_unref (G_OBJECT (header));
669 tmp = g_list_next (tmp);
672 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
679 /* scroll our list view so the selected item is visible */
681 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
683 #ifdef MODEST_PLATFORM_GNOME
685 GtkTreePath *selected_path;
686 GtkTreePath *start, *end;
690 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
691 selected_path = gtk_tree_model_get_path (model, iter);
693 start = gtk_tree_path_new ();
694 end = gtk_tree_path_new ();
696 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
698 if (gtk_tree_path_compare (selected_path, start) < 0 ||
699 gtk_tree_path_compare (end, selected_path) < 0)
700 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
701 selected_path, NULL, TRUE,
704 gtk_tree_path_free (selected_path);
705 gtk_tree_path_free (start);
706 gtk_tree_path_free (end);
708 #endif /* MODEST_PLATFORM_GNOME */
713 modest_header_view_select_next (ModestHeaderView *self)
715 GtkTreeSelection *sel;
720 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
721 path = get_selected_row (GTK_TREE_VIEW(self), &model);
722 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
723 /* Unselect previous path */
724 gtk_tree_selection_unselect_path (sel, path);
726 /* Move path down and selects new one */
727 if (gtk_tree_model_iter_next (model, &iter)) {
728 gtk_tree_selection_select_iter (sel, &iter);
729 scroll_to_selected (self, &iter, FALSE);
731 gtk_tree_path_free(path);
737 modest_header_view_select_prev (ModestHeaderView *self)
739 GtkTreeSelection *sel;
744 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
745 path = get_selected_row (GTK_TREE_VIEW(self), &model);
746 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
747 /* Unselect previous path */
748 gtk_tree_selection_unselect_path (sel, path);
751 if (gtk_tree_path_prev (path)) {
752 gtk_tree_model_get_iter (model, &iter, path);
754 /* Select the new one */
755 gtk_tree_selection_select_iter (sel, &iter);
756 scroll_to_selected (self, &iter, TRUE);
759 gtk_tree_path_free (path);
764 modest_header_view_get_columns (ModestHeaderView *self)
766 g_return_val_if_fail (self, FALSE);
767 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
772 modest_header_view_is_empty (ModestHeaderView *self)
774 g_return_val_if_fail (self, FALSE);
775 return FALSE; /* FIXME */
780 modest_header_view_set_style (ModestHeaderView *self,
781 ModestHeaderViewStyle style)
783 ModestHeaderViewPrivate *priv;
784 gboolean show_col_headers = FALSE;
785 ModestHeaderViewStyle old_style;
787 g_return_val_if_fail (self, FALSE);
788 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
791 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
792 if (priv->style == style)
793 return TRUE; /* nothing to do */
796 case MODEST_HEADER_VIEW_STYLE_DETAILS:
797 show_col_headers = TRUE;
799 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
802 g_return_val_if_reached (FALSE);
804 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
805 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
807 old_style = priv->style;
814 ModestHeaderViewStyle
815 modest_header_view_get_style (ModestHeaderView *self)
817 g_return_val_if_fail (self, FALSE);
818 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
822 * This function sets a sortable model in the header view. It's just
823 * used for developing purposes, because it only does a
824 * gtk_tree_view_set_model
827 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
829 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
830 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
831 /* GtkTreeModel *old_model; */
832 /* ModestHeaderViewPrivate *priv; */
833 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
834 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
836 /* /\* Set new model *\/ */
837 /* gtk_tree_view_set_model (header_view, model); */
839 gtk_tree_view_set_model (header_view, model);
843 modest_header_view_get_folder (ModestHeaderView *self)
845 ModestHeaderViewPrivate *priv;
846 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
849 g_object_ref (priv->folder);
855 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
859 ModestHeaderViewPrivate *priv;
860 GList *cols, *cursor;
861 GtkTreeModel *filter_model, *sortable;
863 GtkSortType sort_type;
865 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
867 headers = TNY_LIST (tny_gtk_header_list_model_new ());
869 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
872 /* Add IDLE observer (monitor) and another folder observer for
873 new messages (self) */
874 g_mutex_lock (priv->observers_lock);
876 tny_folder_monitor_stop (priv->monitor);
877 g_object_unref (G_OBJECT (priv->monitor));
879 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
880 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
881 tny_folder_monitor_start (priv->monitor);
882 g_mutex_unlock (priv->observers_lock);
884 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
885 g_object_unref (G_OBJECT (headers));
887 /* Create a tree model filter to hide and show rows for cut operations */
888 filter_model = gtk_tree_model_filter_new (sortable, NULL);
889 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
893 g_object_unref (G_OBJECT (sortable));
895 /* install our special sorting functions */
896 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
898 /* Restore sort column id */
900 type = modest_tny_folder_guess_folder_type (folder);
901 sort_colid = modest_header_view_get_sort_column_id (self, type);
902 sort_type = modest_header_view_get_sort_type (self, type);
903 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
906 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
907 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
908 (GtkTreeIterCompareFunc) cmp_rows,
910 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
911 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
912 (GtkTreeIterCompareFunc) cmp_subject_rows,
917 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
918 g_object_unref (G_OBJECT (filter_model));
919 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
920 /* g_object_unref (G_OBJECT (sortable)); */
927 modest_header_view_sort_by_column_id (ModestHeaderView *self,
929 GtkSortType sort_type)
931 ModestHeaderViewPrivate *priv = NULL;
932 GtkTreeModel *tree_filter, *sortable = NULL;
935 /* Get model and private data */
936 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
937 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
938 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
939 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
941 /* Sort tree model */
942 type = modest_tny_folder_guess_folder_type (priv->folder);
943 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
946 /* Store new sort parameters */
947 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
949 /* Save GConf parameters */
950 /* modest_widget_memory_save (modest_runtime_get_conf(), */
951 /* G_OBJECT(self), "header-view"); */
956 modest_header_view_set_sort_params (ModestHeaderView *self,
958 GtkSortType sort_type,
961 ModestHeaderViewPrivate *priv;
962 ModestHeaderViewStyle style;
964 style = modest_header_view_get_style (self);
965 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
967 priv->sort_colid[style][type] = sort_colid;
968 priv->sort_type[style][type] = sort_type;
972 modest_header_view_get_sort_column_id (ModestHeaderView *self,
975 ModestHeaderViewPrivate *priv;
976 ModestHeaderViewStyle style;
978 style = modest_header_view_get_style (self);
979 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
981 return priv->sort_colid[style][type];
985 modest_header_view_get_sort_type (ModestHeaderView *self,
988 ModestHeaderViewPrivate *priv;
989 ModestHeaderViewStyle style;
991 style = modest_header_view_get_style (self);
992 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
994 return priv->sort_type[style][type];
998 ModestHeaderView *header_view;
999 RefreshAsyncUserCallback cb;
1004 folder_refreshed_cb (ModestMailOperation *mail_op,
1008 ModestHeaderViewPrivate *priv;
1009 SetFolderHelper *info;
1011 info = (SetFolderHelper*) user_data;
1013 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1017 info->cb (mail_op, folder, info->user_data);
1019 /* Start the folder count changes observer. We do not need it
1020 before the refresh. Note that the monitor could still be
1021 called for this refresh but now we know that the callback
1022 was previously called */
1023 g_mutex_lock (priv->observers_lock);
1024 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1025 g_mutex_unlock (priv->observers_lock);
1032 modest_header_view_set_folder (ModestHeaderView *self,
1034 RefreshAsyncUserCallback callback,
1037 ModestHeaderViewPrivate *priv;
1038 ModestWindowMgr *mgr = NULL;
1039 GObject *source = NULL;
1040 SetFolderHelper *info;
1042 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1045 g_mutex_lock (priv->observers_lock);
1046 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1047 g_object_unref (priv->folder);
1048 priv->folder = NULL;
1049 g_mutex_unlock (priv->observers_lock);
1053 ModestMailOperation *mail_op = NULL;
1054 GtkTreeSelection *selection;
1056 /* Get main window to use it as source of mail operation */
1057 mgr = modest_runtime_get_window_mgr ();
1058 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1060 /* Set folder in the model */
1061 modest_header_view_set_folder_intern (self, folder);
1063 /* Pick my reference. Nothing to do with the mail operation */
1064 priv->folder = g_object_ref (folder);
1066 /* no message selected */
1067 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1069 info = g_malloc0 (sizeof(SetFolderHelper));
1070 info->header_view = self;
1071 info->cb = callback;
1072 info->user_data = user_data;
1074 /* bug 57631: Clear the selection if exists */
1075 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1076 gtk_tree_selection_unselect_all(selection);
1078 /* Create the mail operation (source will be the parent widget) */
1079 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1080 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1083 /* Refresh the folder asynchronously */
1084 modest_mail_operation_refresh_folder (mail_op,
1086 folder_refreshed_cb,
1090 g_object_unref (mail_op);
1092 g_mutex_lock (priv->observers_lock);
1094 if (priv->monitor) {
1095 tny_folder_monitor_stop (priv->monitor);
1096 g_object_unref (G_OBJECT (priv->monitor));
1097 priv->monitor = NULL;
1099 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1101 g_mutex_unlock (priv->observers_lock);
1106 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1107 GtkTreeViewColumn *column, gpointer userdata)
1109 ModestHeaderView *self = NULL;
1110 ModestHeaderViewPrivate *priv = NULL;
1112 GtkTreeModel *model = NULL;
1113 TnyHeader *header = NULL;
1114 TnyHeaderFlags flags;
1116 self = MODEST_HEADER_VIEW (treeview);
1117 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1119 model = gtk_tree_view_get_model (treeview);
1120 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1123 /* get the first selected item */
1124 gtk_tree_model_get (model, &iter,
1125 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1126 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1129 /* Dont open DELETED messages */
1130 if (flags & TNY_HEADER_FLAG_DELETED) {
1131 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1136 g_signal_emit (G_OBJECT(self),
1137 signals[HEADER_ACTIVATED_SIGNAL],
1143 g_object_unref (G_OBJECT (header));
1148 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1150 GtkTreeModel *model;
1152 GtkTreePath *path = NULL;
1154 ModestHeaderView *self;
1155 ModestHeaderViewPrivate *priv;
1156 GList *selected = NULL;
1158 g_return_if_fail (sel);
1159 g_return_if_fail (user_data);
1161 self = MODEST_HEADER_VIEW (user_data);
1162 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1164 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1165 if (selected != NULL)
1166 path = (GtkTreePath *) selected->data;
1167 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1168 return; /* msg was _un_selected */
1170 gtk_tree_model_get (model, &iter,
1171 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1175 g_signal_emit (G_OBJECT(self),
1176 signals[HEADER_SELECTED_SIGNAL],
1179 g_object_unref (G_OBJECT (header));
1181 /* free all items in 'selected' */
1182 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1183 g_list_free (selected);
1187 /* PROTECTED method. It's useful when we want to force a given
1188 selection to reload a msg. For example if we have selected a header
1189 in offline mode, when Modest become online, we want to reload the
1190 message automatically without an user click over the header */
1192 _modest_header_view_change_selection (GtkTreeSelection *selection,
1195 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1196 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1198 on_selection_changed (selection, user_data);
1201 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1203 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1204 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1206 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1208 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1213 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1220 /* static int counter = 0; */
1222 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1223 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1224 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1228 case TNY_HEADER_FLAG_ATTACHMENTS:
1230 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1231 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1232 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1233 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1235 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1236 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1238 return cmp ? cmp : t1 - t2;
1240 case TNY_HEADER_FLAG_PRIORITY:
1241 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1242 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1243 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1244 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1246 /* This is for making priority values respect the intuitive sort relationship
1247 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1248 cmp = compare_priorities (val1, val2);
1250 return cmp ? cmp : t1 - t2;
1253 return &iter1 - &iter2; /* oughhhh */
1258 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1264 /* static int counter = 0; */
1266 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1268 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1269 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1270 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1271 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1273 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1274 val2 + modest_text_utils_get_subject_prefix_len(val2),
1281 /* Drag and drop stuff */
1283 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1284 GtkSelectionData *selection_data,
1285 guint info, guint time, gpointer data)
1287 GtkTreeModel *model = NULL;
1289 GtkTreePath *source_row = NULL;
1290 GtkTreeSelection *sel = NULL;
1292 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1293 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1296 case MODEST_HEADER_ROW:
1297 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1301 gtk_tree_model_get (model, &iter,
1302 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1305 g_object_unref (G_OBJECT(hdr));
1310 g_message ("%s: default switch case.", __FUNCTION__);
1313 /* Set focus on next header */
1314 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1315 gtk_tree_path_next (source_row);
1316 gtk_tree_selection_select_path (sel, source_row);
1318 gtk_tree_path_free (source_row);
1321 /* Header view drag types */
1322 const GtkTargetEntry header_view_drag_types[] = {
1323 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1324 { "text/uri-list", 0, MODEST_MSG },
1328 setup_drag_and_drop (GtkTreeView *self)
1330 gtk_drag_source_set (GTK_WIDGET (self),
1332 header_view_drag_types,
1333 G_N_ELEMENTS (header_view_drag_types),
1334 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1336 g_signal_connect(G_OBJECT (self), "drag_data_get",
1337 G_CALLBACK(drag_data_get_cb), NULL);
1340 static GtkTreePath *
1341 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1343 GtkTreePath *path = NULL;
1344 GtkTreeSelection *sel = NULL;
1347 sel = gtk_tree_view_get_selection(self);
1348 rows = gtk_tree_selection_get_selected_rows (sel, model);
1350 if ((rows == NULL) || (g_list_length(rows) != 1))
1353 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1358 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1365 * This function moves the tree view scroll to the current selected
1366 * row when the widget grabs the focus
1369 on_focus_in (GtkWidget *self,
1370 GdkEventFocus *event,
1373 GtkTreeSelection *selection;
1374 GtkTreeModel *model;
1375 GList *selected = NULL;
1376 GtkTreePath *selected_path = NULL;
1378 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1382 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1383 /* If none selected yet, pick the first one */
1384 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1388 /* Return if the model is empty */
1389 if (!gtk_tree_model_get_iter_first (model, &iter))
1392 path = gtk_tree_model_get_path (model, &iter);
1393 gtk_tree_selection_select_path (selection, path);
1394 gtk_tree_path_free (path);
1397 /* Need to get the all the rows because is selection multiple */
1398 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1399 if (selected == NULL) return FALSE;
1400 selected_path = (GtkTreePath *) selected->data;
1402 /* Check if we need to scroll */
1403 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1404 GtkTreePath *start_path = NULL;
1405 GtkTreePath *end_path = NULL;
1406 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1410 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1411 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1413 /* Scroll to first path */
1414 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1423 gtk_tree_path_free (start_path);
1425 gtk_tree_path_free (end_path);
1427 #endif /* GTK_CHECK_VERSION */
1430 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1431 g_list_free (selected);
1437 idle_notify_headers_count_changed_destroy (gpointer data)
1439 HeadersCountChangedHelper *helper = NULL;
1441 g_return_if_fail (data != NULL);
1442 helper = (HeadersCountChangedHelper *) data;
1444 g_object_unref (helper->change);
1445 g_slice_free (HeadersCountChangedHelper, helper);
1449 idle_notify_headers_count_changed (gpointer data)
1451 TnyFolder *folder = NULL;
1452 ModestHeaderViewPrivate *priv = NULL;
1453 HeadersCountChangedHelper *helper = NULL;
1455 g_return_val_if_fail (data != NULL, FALSE);
1456 helper = (HeadersCountChangedHelper *) data;
1457 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1458 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1460 folder = tny_folder_change_get_folder (helper->change);
1462 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1464 g_mutex_lock (priv->observers_lock);
1466 /* Emit signal to evaluate how headers changes affects to the window view */
1467 g_signal_emit (G_OBJECT(helper->self),
1468 signals[MSG_COUNT_CHANGED_SIGNAL],
1469 0, folder, helper->change);
1471 /* Added or removed headers, so data stored on cliboard are invalid */
1472 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1473 modest_email_clipboard_clear (priv->clipboard);
1475 g_mutex_unlock (priv->observers_lock);
1481 folder_monitor_update (TnyFolderObserver *self,
1482 TnyFolderChange *change)
1484 ModestHeaderViewPrivate *priv;
1485 TnyFolderChangeChanged changed;
1486 HeadersCountChangedHelper *helper = NULL;
1488 changed = tny_folder_change_get_changed (change);
1490 /* Do not notify the observers if the folder of the header
1491 view has changed before this call to the observer
1493 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1494 if (tny_folder_change_get_folder (change) != priv->folder)
1497 /* Check folder count */
1498 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1499 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1500 helper = g_slice_new0 (HeadersCountChangedHelper);
1501 helper->self = MODEST_HEADER_VIEW(self);
1502 helper->change = g_object_ref(change);
1504 g_idle_add_full (G_PRIORITY_DEFAULT,
1505 idle_notify_headers_count_changed,
1507 idle_notify_headers_count_changed_destroy);
1512 modest_header_view_clear (ModestHeaderView *self)
1514 modest_header_view_set_folder (self, NULL, NULL, NULL);
1518 modest_header_view_copy_selection (ModestHeaderView *header_view)
1520 /* Copy selection */
1521 _clipboard_set_selected_data (header_view, FALSE);
1525 modest_header_view_cut_selection (ModestHeaderView *header_view)
1527 ModestHeaderViewPrivate *priv = NULL;
1528 const gchar **hidding = NULL;
1529 guint i, n_selected;
1531 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1532 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1534 /* Copy selection */
1535 _clipboard_set_selected_data (header_view, TRUE);
1537 /* Get hidding ids */
1538 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1540 /* Clear hidding array created by previous cut operation */
1541 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1543 /* Copy hidding array */
1544 priv->n_selected = n_selected;
1545 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1546 for (i=0; i < n_selected; i++)
1547 priv->hidding_ids[i] = g_strdup(hidding[i]);
1549 /* Hide cut headers */
1550 modest_header_view_refilter (header_view);
1557 _clipboard_set_selected_data (ModestHeaderView *header_view,
1560 ModestHeaderViewPrivate *priv = NULL;
1561 TnyList *headers = NULL;
1563 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1564 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1566 /* Set selected data on clipboard */
1567 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1568 headers = modest_header_view_get_selected_headers (header_view);
1569 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1572 g_object_unref (headers);
1578 filter_row (GtkTreeModel *model,
1582 ModestHeaderViewPrivate *priv = NULL;
1583 TnyHeaderFlags flags;
1584 TnyHeader *header = NULL;
1587 gboolean visible = TRUE;
1588 gboolean found = FALSE;
1590 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1591 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1593 /* Get header from model */
1594 gtk_tree_model_get (model, iter,
1595 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1596 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1599 /* Hide mark as deleted heders */
1600 if (flags & TNY_HEADER_FLAG_DELETED) {
1605 /* If no data on clipboard, return always TRUE */
1606 if (modest_email_clipboard_cleared(priv->clipboard)) {
1611 /* Get message id from header (ensure is a valid id) */
1612 if (!header) return FALSE;
1613 id = g_strdup(tny_header_get_message_id (header));
1616 if (priv->hidding_ids != NULL) {
1617 for (i=0; i < priv->n_selected && !found; i++)
1618 if (priv->hidding_ids[i] != NULL && id != NULL)
1619 found = (!strcmp (priv->hidding_ids[i], id));
1627 g_object_unref (header);
1634 _clear_hidding_filter (ModestHeaderView *header_view)
1636 ModestHeaderViewPrivate *priv;
1639 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1640 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1642 if (priv->hidding_ids != NULL) {
1643 for (i=0; i < priv->n_selected; i++)
1644 g_free (priv->hidding_ids[i]);
1645 g_free(priv->hidding_ids);
1650 modest_header_view_refilter (ModestHeaderView *header_view)
1652 GtkTreeModel *model;
1654 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1656 /* Hide cut headers */
1657 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1658 if (GTK_IS_TREE_MODEL_FILTER (model))
1659 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1663 * Protected method, selects a row pointed by path
1666 _modest_header_view_select_from_path (ModestHeaderView *self,
1669 GtkTreeSelection *selection = NULL;
1670 ModestHeaderViewPrivate *priv;
1672 g_return_if_fail (MODEST_HEADER_VIEW (self));
1673 g_return_if_fail (path != NULL);
1675 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1676 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1678 /* Unselect previous selection */
1679 gtk_tree_selection_unselect_all (selection);
1681 /* Select new path*/
1682 gtk_tree_selection_select_path (selection, path);