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];
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 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
346 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
347 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
349 /* Add new columns */
350 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
351 ModestHeaderViewColumn col =
352 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
354 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
355 g_printerr ("modest: invalid column %d in column list\n", col);
361 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
362 column = get_new_column (_("M"), renderer_msgtype, FALSE,
363 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
365 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
367 gtk_tree_view_column_set_fixed_width (column, 45);
370 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
371 column = get_new_column (_("A"), renderer_attach, FALSE,
372 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
374 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
376 gtk_tree_view_column_set_fixed_width (column, 45);
380 case MODEST_HEADER_VIEW_COLUMN_FROM:
381 column = get_new_column (_("From"), renderer_header, TRUE,
382 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
384 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
385 GINT_TO_POINTER(TRUE));
388 case MODEST_HEADER_VIEW_COLUMN_TO:
389 column = get_new_column (_("To"), renderer_header, TRUE,
390 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
392 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
393 GINT_TO_POINTER(FALSE));
396 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
397 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
398 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
400 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
401 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
402 compact_column = column;
405 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
406 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
407 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
409 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
410 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
411 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
412 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
413 compact_column = column;
417 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
418 column = get_new_column (_("Subject"), renderer_header, TRUE,
419 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
421 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
425 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
426 column = get_new_column (_("Received"), renderer_header, TRUE,
427 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
429 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
430 GINT_TO_POINTER(TRUE));
433 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
434 column = get_new_column (_("Sent"), renderer_header, TRUE,
435 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
437 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
438 GINT_TO_POINTER(FALSE));
441 case MODEST_HEADER_VIEW_COLUMN_SIZE:
442 column = get_new_column (_("Size"), renderer_header, TRUE,
443 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
445 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
448 case MODEST_HEADER_VIEW_COLUMN_STATUS:
449 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
450 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
452 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
457 g_return_val_if_reached(FALSE);
460 /* we keep the column id around */
461 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
462 GINT_TO_POINTER(col));
464 /* we need this ptr when sorting the rows */
465 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
467 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
471 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
472 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
473 (GtkTreeIterCompareFunc) cmp_rows,
474 compact_column, NULL);
475 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
476 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
477 (GtkTreeIterCompareFunc) cmp_subject_rows,
478 compact_column, NULL);
486 modest_header_view_init (ModestHeaderView *obj)
488 ModestHeaderViewPrivate *priv;
491 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
495 priv->monitor = NULL;
496 priv->observers_lock = g_mutex_new ();
498 priv->clipboard = modest_runtime_get_email_clipboard ();
499 priv->hidding_ids = NULL;
500 priv->n_selected = 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 g_mutex_lock (priv->observers_lock);
542 tny_folder_monitor_stop (priv->monitor);
543 g_object_unref (G_OBJECT (priv->monitor));
545 g_mutex_unlock (priv->observers_lock);
546 g_mutex_free (priv->observers_lock);
548 /* Clear hidding array created by cut operation */
549 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
551 G_OBJECT_CLASS(parent_class)->finalize (obj);
556 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
559 GtkTreeSelection *sel;
560 ModestHeaderView *self;
561 ModestHeaderViewPrivate *priv;
563 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
566 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
567 self = MODEST_HEADER_VIEW(obj);
568 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
570 modest_header_view_set_style (self, style);
571 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
573 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
574 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
575 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
577 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
578 TRUE); /* alternating row colors */
579 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
581 g_signal_connect (sel, "changed",
582 G_CALLBACK(on_selection_changed), self);
584 g_signal_connect (self, "row-activated",
585 G_CALLBACK (on_header_row_activated), NULL);
587 g_signal_connect (self, "focus-in-event",
588 G_CALLBACK(on_focus_in), NULL);
590 return GTK_WIDGET(self);
595 modest_header_view_count_selected_headers (ModestHeaderView *self)
597 GtkTreeSelection *sel;
600 g_return_val_if_fail (self, 0);
602 /* Get selection object and check selected rows count */
603 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
604 selected_rows = gtk_tree_selection_count_selected_rows (sel);
606 return selected_rows;
610 modest_header_view_has_selected_headers (ModestHeaderView *self)
612 GtkTreeSelection *sel;
615 g_return_val_if_fail (self, FALSE);
617 /* Get selection object and check selected rows count */
618 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
619 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
626 modest_header_view_get_selected_headers (ModestHeaderView *self)
628 GtkTreeSelection *sel;
629 ModestHeaderViewPrivate *priv;
630 TnyList *header_list = NULL;
632 GList *list, *tmp = NULL;
633 GtkTreeModel *tree_model = NULL;
636 g_return_val_if_fail (self, NULL);
638 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
640 /* Get selected rows */
641 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
642 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
645 header_list = tny_simple_list_new();
647 list = g_list_reverse (list);
650 /* get header from selection */
651 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
652 gtk_tree_model_get (tree_model, &iter,
653 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
655 /* Prepend to list */
656 tny_list_prepend (header_list, G_OBJECT (header));
657 g_object_unref (G_OBJECT (header));
659 tmp = g_list_next (tmp);
662 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
669 /* scroll our list view so the selected item is visible */
671 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
673 #ifdef MODEST_PLATFORM_GNOME
675 GtkTreePath *selected_path;
676 GtkTreePath *start, *end;
680 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
681 selected_path = gtk_tree_model_get_path (model, iter);
683 start = gtk_tree_path_new ();
684 end = gtk_tree_path_new ();
686 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
688 if (gtk_tree_path_compare (selected_path, start) < 0 ||
689 gtk_tree_path_compare (end, selected_path) < 0)
690 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
691 selected_path, NULL, TRUE,
694 gtk_tree_path_free (selected_path);
695 gtk_tree_path_free (start);
696 gtk_tree_path_free (end);
698 #endif /* MODEST_PLATFORM_GNOME */
703 modest_header_view_select_next (ModestHeaderView *self)
705 GtkTreeSelection *sel;
710 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
711 path = get_selected_row (GTK_TREE_VIEW(self), &model);
712 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
713 /* Unselect previous path */
714 gtk_tree_selection_unselect_path (sel, path);
716 /* Move path down and selects new one */
717 if (gtk_tree_model_iter_next (model, &iter)) {
718 gtk_tree_selection_select_iter (sel, &iter);
719 scroll_to_selected (self, &iter, FALSE);
721 gtk_tree_path_free(path);
727 modest_header_view_select_prev (ModestHeaderView *self)
729 GtkTreeSelection *sel;
734 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
735 path = get_selected_row (GTK_TREE_VIEW(self), &model);
736 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
737 /* Unselect previous path */
738 gtk_tree_selection_unselect_path (sel, path);
741 if (gtk_tree_path_prev (path)) {
742 gtk_tree_model_get_iter (model, &iter, path);
744 /* Select the new one */
745 gtk_tree_selection_select_iter (sel, &iter);
746 scroll_to_selected (self, &iter, TRUE);
749 gtk_tree_path_free (path);
754 modest_header_view_get_columns (ModestHeaderView *self)
756 g_return_val_if_fail (self, FALSE);
757 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
762 modest_header_view_is_empty (ModestHeaderView *self)
764 g_return_val_if_fail (self, FALSE);
765 return FALSE; /* FIXME */
770 modest_header_view_set_style (ModestHeaderView *self,
771 ModestHeaderViewStyle style)
773 ModestHeaderViewPrivate *priv;
774 gboolean show_col_headers = FALSE;
775 ModestHeaderViewStyle old_style;
777 g_return_val_if_fail (self, FALSE);
778 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
781 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
782 if (priv->style == style)
783 return TRUE; /* nothing to do */
786 case MODEST_HEADER_VIEW_STYLE_DETAILS:
787 show_col_headers = TRUE;
789 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
792 g_return_val_if_reached (FALSE);
794 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
795 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
797 old_style = priv->style;
804 ModestHeaderViewStyle
805 modest_header_view_get_style (ModestHeaderView *self)
807 g_return_val_if_fail (self, FALSE);
808 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
812 * This function sets a sortable model in the header view. It's just
813 * used for developing purposes, because it only does a
814 * gtk_tree_view_set_model
817 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
819 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
820 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
821 /* GtkTreeModel *old_model; */
822 /* ModestHeaderViewPrivate *priv; */
823 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
824 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
826 /* /\* Set new model *\/ */
827 /* gtk_tree_view_set_model (header_view, model); */
829 gtk_tree_view_set_model (header_view, model);
833 modest_header_view_get_folder (ModestHeaderView *self)
835 ModestHeaderViewPrivate *priv;
836 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
839 g_object_ref (priv->folder);
845 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
849 ModestHeaderViewPrivate *priv;
850 GList *cols, *cursor;
851 GtkTreeModel *filter_model, *sortable;
853 GtkSortType sort_type;
855 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
857 headers = TNY_LIST (tny_gtk_header_list_model_new ());
859 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
862 /* Add IDLE observer (monitor) and another folder observer for
863 new messages (self) */
864 g_mutex_lock (priv->observers_lock);
866 tny_folder_monitor_stop (priv->monitor);
867 g_object_unref (G_OBJECT (priv->monitor));
869 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
870 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
871 tny_folder_monitor_start (priv->monitor);
872 g_mutex_unlock (priv->observers_lock);
874 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
875 g_object_unref (G_OBJECT (headers));
877 /* Create a tree model filter to hide and show rows for cut operations */
878 filter_model = gtk_tree_model_filter_new (sortable, NULL);
879 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
883 g_object_unref (G_OBJECT (sortable));
885 /* install our special sorting functions */
886 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
888 /* Restore sort column id */
890 type = modest_tny_folder_guess_folder_type (folder);
891 sort_colid = modest_header_view_get_sort_column_id (self, type);
892 sort_type = modest_header_view_get_sort_type (self, type);
893 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
896 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
897 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
898 (GtkTreeIterCompareFunc) cmp_rows,
900 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
901 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
902 (GtkTreeIterCompareFunc) cmp_subject_rows,
907 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
908 g_object_unref (G_OBJECT (filter_model));
909 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
910 /* g_object_unref (G_OBJECT (sortable)); */
917 modest_header_view_sort_by_column_id (ModestHeaderView *self,
919 GtkSortType sort_type)
921 ModestHeaderViewPrivate *priv = NULL;
922 GtkTreeModel *tree_filter, *sortable = NULL;
925 /* Get model and private data */
926 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
927 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
928 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
929 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
931 /* Sort tree model */
932 type = modest_tny_folder_guess_folder_type (priv->folder);
933 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
936 /* Store new sort parameters */
937 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
939 /* Save GConf parameters */
940 /* modest_widget_memory_save (modest_runtime_get_conf(), */
941 /* G_OBJECT(self), "header-view"); */
946 modest_header_view_set_sort_params (ModestHeaderView *self,
948 GtkSortType sort_type,
951 ModestHeaderViewPrivate *priv;
952 ModestHeaderViewStyle style;
954 style = modest_header_view_get_style (self);
955 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
957 priv->sort_colid[style][type] = sort_colid;
958 priv->sort_type[style][type] = sort_type;
962 modest_header_view_get_sort_column_id (ModestHeaderView *self,
965 ModestHeaderViewPrivate *priv;
966 ModestHeaderViewStyle style;
968 style = modest_header_view_get_style (self);
969 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
971 return priv->sort_colid[style][type];
975 modest_header_view_get_sort_type (ModestHeaderView *self,
978 ModestHeaderViewPrivate *priv;
979 ModestHeaderViewStyle style;
981 style = modest_header_view_get_style (self);
982 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
984 return priv->sort_type[style][type];
988 ModestHeaderView *header_view;
989 RefreshAsyncUserCallback cb;
994 folder_refreshed_cb (ModestMailOperation *mail_op,
998 ModestHeaderViewPrivate *priv;
999 SetFolderHelper *info;
1001 info = (SetFolderHelper*) user_data;
1003 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1007 info->cb (mail_op, folder, info->user_data);
1009 /* Start the folder count changes observer. We do not need it
1010 before the refresh. Note that the monitor could still be
1011 called for this refresh but now we know that the callback
1012 was previously called */
1013 g_mutex_lock (priv->observers_lock);
1014 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1015 g_mutex_unlock (priv->observers_lock);
1022 modest_header_view_set_folder (ModestHeaderView *self,
1024 RefreshAsyncUserCallback callback,
1027 ModestHeaderViewPrivate *priv;
1028 ModestWindowMgr *mgr = NULL;
1029 GObject *source = NULL;
1030 SetFolderHelper *info;
1032 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1035 g_mutex_lock (priv->observers_lock);
1036 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1037 g_object_unref (priv->folder);
1038 priv->folder = NULL;
1039 g_mutex_unlock (priv->observers_lock);
1043 ModestMailOperation *mail_op = NULL;
1044 GtkTreeSelection *selection;
1046 /* Get main window to use it as source of mail operation */
1047 mgr = modest_runtime_get_window_mgr ();
1048 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1050 /* Set folder in the model */
1051 modest_header_view_set_folder_intern (self, folder);
1053 /* Pick my reference. Nothing to do with the mail operation */
1054 priv->folder = g_object_ref (folder);
1056 /* no message selected */
1057 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1059 info = g_malloc0 (sizeof(SetFolderHelper));
1060 info->header_view = self;
1061 info->cb = callback;
1062 info->user_data = user_data;
1064 /* bug 57631: Clear the selection if exists */
1065 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1066 gtk_tree_selection_unselect_all(selection);
1068 /* Create the mail operation (source will be the parent widget) */
1069 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1070 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1073 /* Refresh the folder asynchronously */
1074 modest_mail_operation_refresh_folder (mail_op,
1076 folder_refreshed_cb,
1080 g_object_unref (mail_op);
1082 g_mutex_lock (priv->observers_lock);
1084 if (priv->monitor) {
1085 tny_folder_monitor_stop (priv->monitor);
1086 g_object_unref (G_OBJECT (priv->monitor));
1087 priv->monitor = NULL;
1089 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1091 g_mutex_unlock (priv->observers_lock);
1096 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1097 GtkTreeViewColumn *column, gpointer userdata)
1099 ModestHeaderView *self = NULL;
1100 ModestHeaderViewPrivate *priv = NULL;
1102 GtkTreeModel *model = NULL;
1103 TnyHeader *header = NULL;
1104 TnyHeaderFlags flags;
1106 self = MODEST_HEADER_VIEW (treeview);
1107 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1109 model = gtk_tree_view_get_model (treeview);
1110 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1113 /* get the first selected item */
1114 gtk_tree_model_get (model, &iter,
1115 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1116 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1119 /* Dont open DELETED messages */
1120 if (flags & TNY_HEADER_FLAG_DELETED) {
1121 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1126 g_signal_emit (G_OBJECT(self),
1127 signals[HEADER_ACTIVATED_SIGNAL],
1133 g_object_unref (G_OBJECT (header));
1138 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1140 GtkTreeModel *model;
1142 GtkTreePath *path = NULL;
1144 ModestHeaderView *self;
1145 ModestHeaderViewPrivate *priv;
1146 GList *selected = NULL;
1148 g_return_if_fail (sel);
1149 g_return_if_fail (user_data);
1151 self = MODEST_HEADER_VIEW (user_data);
1152 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1154 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1155 if (selected != NULL)
1156 path = (GtkTreePath *) selected->data;
1157 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1158 return; /* msg was _un_selected */
1160 gtk_tree_model_get (model, &iter,
1161 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1165 g_signal_emit (G_OBJECT(self),
1166 signals[HEADER_SELECTED_SIGNAL],
1169 g_object_unref (G_OBJECT (header));
1171 /* free all items in 'selected' */
1172 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1173 g_list_free (selected);
1177 /* PROTECTED method. It's useful when we want to force a given
1178 selection to reload a msg. For example if we have selected a header
1179 in offline mode, when Modest become online, we want to reload the
1180 message automatically without an user click over the header */
1182 _modest_header_view_change_selection (GtkTreeSelection *selection,
1185 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1186 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1188 on_selection_changed (selection, user_data);
1191 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1193 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1194 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1196 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1198 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1203 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1210 /* static int counter = 0; */
1212 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1213 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1214 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1218 case TNY_HEADER_FLAG_ATTACHMENTS:
1220 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1221 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1222 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1223 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1225 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1226 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1228 return cmp ? cmp : t1 - t2;
1230 case TNY_HEADER_FLAG_PRIORITY:
1231 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1232 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1233 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1234 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1236 /* This is for making priority values respect the intuitive sort relationship
1237 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1238 cmp = compare_priorities (val1, val2);
1240 return cmp ? cmp : t1 - t2;
1243 return &iter1 - &iter2; /* oughhhh */
1248 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1254 /* static int counter = 0; */
1256 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1258 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1259 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1260 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1261 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1263 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1264 val2 + modest_text_utils_get_subject_prefix_len(val2),
1271 /* Drag and drop stuff */
1273 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1274 GtkSelectionData *selection_data,
1275 guint info, guint time, gpointer data)
1277 GtkTreeModel *model = NULL;
1279 GtkTreePath *source_row = NULL;
1280 GtkTreeSelection *sel = NULL;
1282 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1283 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1286 case MODEST_HEADER_ROW:
1287 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1291 gtk_tree_model_get (model, &iter,
1292 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1295 g_object_unref (G_OBJECT(hdr));
1300 g_message ("%s: default switch case.", __FUNCTION__);
1303 /* Set focus on next header */
1304 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1305 gtk_tree_path_next (source_row);
1306 gtk_tree_selection_select_path (sel, source_row);
1308 gtk_tree_path_free (source_row);
1311 /* Header view drag types */
1312 const GtkTargetEntry header_view_drag_types[] = {
1313 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1314 { "text/uri-list", 0, MODEST_MSG },
1318 setup_drag_and_drop (GtkTreeView *self)
1320 gtk_drag_source_set (GTK_WIDGET (self),
1322 header_view_drag_types,
1323 G_N_ELEMENTS (header_view_drag_types),
1324 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1326 g_signal_connect(G_OBJECT (self), "drag_data_get",
1327 G_CALLBACK(drag_data_get_cb), NULL);
1330 static GtkTreePath *
1331 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1333 GtkTreePath *path = NULL;
1334 GtkTreeSelection *sel = NULL;
1337 sel = gtk_tree_view_get_selection(self);
1338 rows = gtk_tree_selection_get_selected_rows (sel, model);
1340 if ((rows == NULL) || (g_list_length(rows) != 1))
1343 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1348 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1355 * This function moves the tree view scroll to the current selected
1356 * row when the widget grabs the focus
1359 on_focus_in (GtkWidget *self,
1360 GdkEventFocus *event,
1363 GtkTreeSelection *selection;
1364 GtkTreeModel *model;
1365 GList *selected = NULL;
1366 GtkTreePath *selected_path = NULL;
1368 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1372 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1373 /* If none selected yet, pick the first one */
1374 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1378 /* Return if the model is empty */
1379 if (!gtk_tree_model_get_iter_first (model, &iter))
1382 path = gtk_tree_model_get_path (model, &iter);
1383 gtk_tree_selection_select_path (selection, path);
1384 gtk_tree_path_free (path);
1387 /* Need to get the all the rows because is selection multiple */
1388 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1389 if (selected == NULL) return FALSE;
1390 selected_path = (GtkTreePath *) selected->data;
1392 /* Check if we need to scroll */
1393 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1394 GtkTreePath *start_path = NULL;
1395 GtkTreePath *end_path = NULL;
1396 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1400 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1401 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1403 /* Scroll to first path */
1404 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1413 gtk_tree_path_free (start_path);
1415 gtk_tree_path_free (end_path);
1417 #endif /* GTK_CHECK_VERSION */
1420 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1421 g_list_free (selected);
1427 idle_notify_headers_count_changed_destroy (gpointer data)
1429 HeadersCountChangedHelper *helper = NULL;
1431 g_return_if_fail (data != NULL);
1432 helper = (HeadersCountChangedHelper *) data;
1434 g_object_unref (helper->change);
1435 g_slice_free (HeadersCountChangedHelper, helper);
1439 idle_notify_headers_count_changed (gpointer data)
1441 TnyFolder *folder = NULL;
1442 ModestHeaderViewPrivate *priv = NULL;
1443 HeadersCountChangedHelper *helper = NULL;
1445 g_return_val_if_fail (data != NULL, FALSE);
1446 helper = (HeadersCountChangedHelper *) data;
1447 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1448 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1450 folder = tny_folder_change_get_folder (helper->change);
1452 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1454 g_mutex_lock (priv->observers_lock);
1456 /* Emit signal to evaluate how headers changes affects to the window view */
1457 g_signal_emit (G_OBJECT(helper->self),
1458 signals[MSG_COUNT_CHANGED_SIGNAL],
1459 0, folder, helper->change);
1461 /* Added or removed headers, so data stored on cliboard are invalid */
1462 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1463 modest_email_clipboard_clear (priv->clipboard);
1465 g_mutex_unlock (priv->observers_lock);
1471 folder_monitor_update (TnyFolderObserver *self,
1472 TnyFolderChange *change)
1474 ModestHeaderViewPrivate *priv;
1475 TnyFolderChangeChanged changed;
1476 HeadersCountChangedHelper *helper = NULL;
1478 changed = tny_folder_change_get_changed (change);
1480 /* Do not notify the observers if the folder of the header
1481 view has changed before this call to the observer
1483 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1484 if (tny_folder_change_get_folder (change) != priv->folder)
1487 /* Check folder count */
1488 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1489 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1490 helper = g_slice_new0 (HeadersCountChangedHelper);
1491 helper->self = MODEST_HEADER_VIEW(self);
1492 helper->change = g_object_ref(change);
1494 g_idle_add_full (G_PRIORITY_DEFAULT,
1495 idle_notify_headers_count_changed,
1497 idle_notify_headers_count_changed_destroy);
1502 modest_header_view_clear (ModestHeaderView *self)
1504 modest_header_view_set_folder (self, NULL, NULL, NULL);
1508 modest_header_view_copy_selection (ModestHeaderView *header_view)
1510 /* Copy selection */
1511 _clipboard_set_selected_data (header_view, FALSE);
1515 modest_header_view_cut_selection (ModestHeaderView *header_view)
1517 ModestHeaderViewPrivate *priv = NULL;
1518 const gchar **hidding = NULL;
1519 guint i, n_selected;
1521 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1522 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1524 /* Copy selection */
1525 _clipboard_set_selected_data (header_view, TRUE);
1527 /* Get hidding ids */
1528 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1530 /* Clear hidding array created by previous cut operation */
1531 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1533 /* Copy hidding array */
1534 priv->n_selected = n_selected;
1535 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1536 for (i=0; i < n_selected; i++)
1537 priv->hidding_ids[i] = g_strdup(hidding[i]);
1539 /* Hide cut headers */
1540 modest_header_view_refilter (header_view);
1547 _clipboard_set_selected_data (ModestHeaderView *header_view,
1550 ModestHeaderViewPrivate *priv = NULL;
1551 TnyList *headers = NULL;
1553 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1554 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1556 /* Set selected data on clipboard */
1557 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1558 headers = modest_header_view_get_selected_headers (header_view);
1559 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1562 g_object_unref (headers);
1568 filter_row (GtkTreeModel *model,
1572 ModestHeaderViewPrivate *priv = NULL;
1573 TnyHeaderFlags flags;
1574 TnyHeader *header = NULL;
1577 gboolean visible = TRUE;
1578 gboolean found = FALSE;
1580 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1581 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1583 /* Get header from model */
1584 gtk_tree_model_get (model, iter,
1585 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1586 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1589 /* Hide mark as deleted heders */
1590 if (flags & TNY_HEADER_FLAG_DELETED) {
1595 /* If no data on clipboard, return always TRUE */
1596 if (modest_email_clipboard_cleared(priv->clipboard)) {
1601 /* Get message id from header (ensure is a valid id) */
1602 if (!header) return FALSE;
1603 id = g_strdup(tny_header_get_message_id (header));
1606 if (priv->hidding_ids != NULL) {
1607 for (i=0; i < priv->n_selected && !found; i++)
1608 if (priv->hidding_ids[i] != NULL && id != NULL)
1609 found = (!strcmp (priv->hidding_ids[i], id));
1617 g_object_unref (header);
1624 _clear_hidding_filter (ModestHeaderView *header_view)
1626 ModestHeaderViewPrivate *priv;
1629 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1630 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1632 if (priv->hidding_ids != NULL) {
1633 for (i=0; i < priv->n_selected; i++)
1634 g_free (priv->hidding_ids[i]);
1635 g_free(priv->hidding_ids);
1640 modest_header_view_refilter (ModestHeaderView *header_view)
1642 GtkTreeModel *model;
1644 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1646 /* Hide cut headers */
1647 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1648 if (GTK_IS_TREE_MODEL_FILTER (model))
1649 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));