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;
1045 /* Get main window to use it as source of mail operation */
1046 mgr = modest_runtime_get_window_mgr ();
1047 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1049 /* Set folder in the model */
1050 modest_header_view_set_folder_intern (self, folder);
1052 /* Pick my reference. Nothing to do with the mail operation */
1053 priv->folder = g_object_ref (folder);
1055 /* no message selected */
1056 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1058 info = g_malloc0 (sizeof(SetFolderHelper));
1059 info->header_view = self;
1060 info->cb = callback;
1061 info->user_data = user_data;
1063 /* Create the mail operation (source will be the parent widget) */
1064 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1065 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1068 /* Refresh the folder asynchronously */
1069 modest_mail_operation_refresh_folder (mail_op,
1071 folder_refreshed_cb,
1075 g_object_unref (mail_op);
1077 g_mutex_lock (priv->observers_lock);
1079 if (priv->monitor) {
1080 tny_folder_monitor_stop (priv->monitor);
1081 g_object_unref (G_OBJECT (priv->monitor));
1082 priv->monitor = NULL;
1084 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1086 g_mutex_unlock (priv->observers_lock);
1091 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1092 GtkTreeViewColumn *column, gpointer userdata)
1094 ModestHeaderView *self = NULL;
1095 ModestHeaderViewPrivate *priv = NULL;
1097 GtkTreeModel *model = NULL;
1098 TnyHeader *header = NULL;
1099 TnyHeaderFlags flags;
1101 self = MODEST_HEADER_VIEW (treeview);
1102 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1104 model = gtk_tree_view_get_model (treeview);
1105 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1108 /* get the first selected item */
1109 gtk_tree_model_get (model, &iter,
1110 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1111 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1114 /* Dont open DELETED messages */
1115 if (flags & TNY_HEADER_FLAG_DELETED) {
1116 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1121 g_signal_emit (G_OBJECT(self),
1122 signals[HEADER_ACTIVATED_SIGNAL],
1128 g_object_unref (G_OBJECT (header));
1133 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1135 GtkTreeModel *model;
1137 GtkTreePath *path = NULL;
1139 ModestHeaderView *self;
1140 ModestHeaderViewPrivate *priv;
1141 GList *selected = NULL;
1143 g_return_if_fail (sel);
1144 g_return_if_fail (user_data);
1146 self = MODEST_HEADER_VIEW (user_data);
1147 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1149 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1150 if (selected != NULL)
1151 path = (GtkTreePath *) selected->data;
1152 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1153 return; /* msg was _un_selected */
1155 gtk_tree_model_get (model, &iter,
1156 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1160 g_signal_emit (G_OBJECT(self),
1161 signals[HEADER_SELECTED_SIGNAL],
1164 g_object_unref (G_OBJECT (header));
1166 /* free all items in 'selected' */
1167 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1168 g_list_free (selected);
1172 /* PROTECTED method. It's useful when we want to force a given
1173 selection to reload a msg. For example if we have selected a header
1174 in offline mode, when Modest become online, we want to reload the
1175 message automatically without an user click over the header */
1177 _modest_header_view_change_selection (GtkTreeSelection *selection,
1180 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1181 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1183 on_selection_changed (selection, user_data);
1186 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1188 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1189 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1191 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1193 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1198 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1205 /* static int counter = 0; */
1207 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1208 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1209 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1213 case TNY_HEADER_FLAG_ATTACHMENTS:
1215 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1216 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1217 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1218 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1220 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1221 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1223 return cmp ? cmp : t1 - t2;
1225 case TNY_HEADER_FLAG_PRIORITY:
1226 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1227 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1228 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1229 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1231 /* This is for making priority values respect the intuitive sort relationship
1232 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1233 cmp = compare_priorities (val1, val2);
1235 return cmp ? cmp : t1 - t2;
1238 return &iter1 - &iter2; /* oughhhh */
1243 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1249 /* static int counter = 0; */
1251 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1253 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1254 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1255 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1256 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1258 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1259 val2 + modest_text_utils_get_subject_prefix_len(val2),
1266 /* Drag and drop stuff */
1268 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1269 GtkSelectionData *selection_data,
1270 guint info, guint time, gpointer data)
1272 GtkTreeModel *model = NULL;
1274 GtkTreePath *source_row = NULL;
1275 GtkTreeSelection *sel = NULL;
1277 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1278 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1281 case MODEST_HEADER_ROW:
1282 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1286 gtk_tree_model_get (model, &iter,
1287 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1290 g_object_unref (G_OBJECT(hdr));
1295 g_message ("%s: default switch case.", __FUNCTION__);
1298 /* Set focus on next header */
1299 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1300 gtk_tree_path_next (source_row);
1301 gtk_tree_selection_select_path (sel, source_row);
1303 gtk_tree_path_free (source_row);
1306 /* Header view drag types */
1307 const GtkTargetEntry header_view_drag_types[] = {
1308 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1309 { "text/uri-list", 0, MODEST_MSG },
1313 setup_drag_and_drop (GtkTreeView *self)
1315 gtk_drag_source_set (GTK_WIDGET (self),
1317 header_view_drag_types,
1318 G_N_ELEMENTS (header_view_drag_types),
1319 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1321 g_signal_connect(G_OBJECT (self), "drag_data_get",
1322 G_CALLBACK(drag_data_get_cb), NULL);
1325 static GtkTreePath *
1326 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1328 GtkTreePath *path = NULL;
1329 GtkTreeSelection *sel = NULL;
1332 sel = gtk_tree_view_get_selection(self);
1333 rows = gtk_tree_selection_get_selected_rows (sel, model);
1335 if ((rows == NULL) || (g_list_length(rows) != 1))
1338 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1343 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1350 * This function moves the tree view scroll to the current selected
1351 * row when the widget grabs the focus
1354 on_focus_in (GtkWidget *self,
1355 GdkEventFocus *event,
1358 GtkTreeSelection *selection;
1359 GtkTreeModel *model;
1360 GList *selected = NULL;
1361 GtkTreePath *selected_path = NULL;
1363 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1367 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1368 /* If none selected yet, pick the first one */
1369 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1373 /* Return if the model is empty */
1374 if (!gtk_tree_model_get_iter_first (model, &iter))
1377 path = gtk_tree_model_get_path (model, &iter);
1378 gtk_tree_selection_select_path (selection, path);
1379 gtk_tree_path_free (path);
1382 /* Need to get the all the rows because is selection multiple */
1383 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1384 if (selected == NULL) return FALSE;
1385 selected_path = (GtkTreePath *) selected->data;
1387 /* Check if we need to scroll */
1388 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1389 GtkTreePath *start_path = NULL;
1390 GtkTreePath *end_path = NULL;
1391 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1395 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1396 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1398 /* Scroll to first path */
1399 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1408 gtk_tree_path_free (start_path);
1410 gtk_tree_path_free (end_path);
1412 #endif /* GTK_CHECK_VERSION */
1415 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1416 g_list_free (selected);
1422 idle_notify_headers_count_changed_destroy (gpointer data)
1424 HeadersCountChangedHelper *helper = NULL;
1426 g_return_if_fail (data != NULL);
1427 helper = (HeadersCountChangedHelper *) data;
1429 g_object_unref (helper->change);
1430 g_slice_free (HeadersCountChangedHelper, helper);
1434 idle_notify_headers_count_changed (gpointer data)
1436 TnyFolder *folder = NULL;
1437 ModestHeaderViewPrivate *priv = NULL;
1438 HeadersCountChangedHelper *helper = NULL;
1440 g_return_val_if_fail (data != NULL, FALSE);
1441 helper = (HeadersCountChangedHelper *) data;
1442 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1443 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1445 folder = tny_folder_change_get_folder (helper->change);
1447 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1449 g_mutex_lock (priv->observers_lock);
1451 /* Emit signal to evaluate how headers changes affects to the window view */
1452 g_signal_emit (G_OBJECT(helper->self),
1453 signals[MSG_COUNT_CHANGED_SIGNAL],
1454 0, folder, helper->change);
1456 /* Added or removed headers, so data stored on cliboard are invalid */
1457 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1458 modest_email_clipboard_clear (priv->clipboard);
1460 g_mutex_unlock (priv->observers_lock);
1466 folder_monitor_update (TnyFolderObserver *self,
1467 TnyFolderChange *change)
1469 ModestHeaderViewPrivate *priv;
1470 TnyFolderChangeChanged changed;
1471 HeadersCountChangedHelper *helper = NULL;
1473 changed = tny_folder_change_get_changed (change);
1475 /* Do not notify the observers if the folder of the header
1476 view has changed before this call to the observer
1478 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1479 if (tny_folder_change_get_folder (change) != priv->folder)
1482 /* Check folder count */
1483 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1484 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1485 helper = g_slice_new0 (HeadersCountChangedHelper);
1486 helper->self = MODEST_HEADER_VIEW(self);
1487 helper->change = g_object_ref(change);
1489 g_idle_add_full (G_PRIORITY_DEFAULT,
1490 idle_notify_headers_count_changed,
1492 idle_notify_headers_count_changed_destroy);
1497 modest_header_view_clear (ModestHeaderView *self)
1499 modest_header_view_set_folder (self, NULL, NULL, NULL);
1503 modest_header_view_copy_selection (ModestHeaderView *header_view)
1505 /* Copy selection */
1506 _clipboard_set_selected_data (header_view, FALSE);
1510 modest_header_view_cut_selection (ModestHeaderView *header_view)
1512 ModestHeaderViewPrivate *priv = NULL;
1513 const gchar **hidding = NULL;
1514 guint i, n_selected;
1516 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1517 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1519 /* Copy selection */
1520 _clipboard_set_selected_data (header_view, TRUE);
1522 /* Get hidding ids */
1523 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1525 /* Clear hidding array created by previous cut operation */
1526 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1528 /* Copy hidding array */
1529 priv->n_selected = n_selected;
1530 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1531 for (i=0; i < n_selected; i++)
1532 priv->hidding_ids[i] = g_strdup(hidding[i]);
1534 /* Hide cut headers */
1535 modest_header_view_refilter (header_view);
1542 _clipboard_set_selected_data (ModestHeaderView *header_view,
1545 ModestHeaderViewPrivate *priv = NULL;
1546 TnyList *headers = NULL;
1548 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1549 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1551 /* Set selected data on clipboard */
1552 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1553 headers = modest_header_view_get_selected_headers (header_view);
1554 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1557 g_object_unref (headers);
1563 filter_row (GtkTreeModel *model,
1567 ModestHeaderViewPrivate *priv = NULL;
1568 TnyHeaderFlags flags;
1569 TnyHeader *header = NULL;
1572 gboolean visible = TRUE;
1573 gboolean found = FALSE;
1575 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1576 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1578 /* Get header from model */
1579 gtk_tree_model_get (model, iter,
1580 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1581 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1584 /* Hide mark as deleted heders */
1585 if (flags & TNY_HEADER_FLAG_DELETED) {
1590 /* If no data on clipboard, return always TRUE */
1591 if (modest_email_clipboard_cleared(priv->clipboard)) {
1596 /* Get message id from header (ensure is a valid id) */
1597 if (!header) return FALSE;
1598 id = g_strdup(tny_header_get_message_id (header));
1601 if (priv->hidding_ids != NULL) {
1602 for (i=0; i < priv->n_selected && !found; i++)
1603 if (priv->hidding_ids[i] != NULL && id != NULL)
1604 found = (!strcmp (priv->hidding_ids[i], id));
1612 g_object_unref (header);
1619 _clear_hidding_filter (ModestHeaderView *header_view)
1621 ModestHeaderViewPrivate *priv;
1624 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1625 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1627 if (priv->hidding_ids != NULL) {
1628 for (i=0; i < priv->n_selected; i++)
1629 g_free (priv->hidding_ids[i]);
1630 g_free(priv->hidding_ids);
1635 modest_header_view_refilter (ModestHeaderView *header_view)
1637 GtkTreeModel *model;
1639 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1641 /* Hide cut headers */
1642 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1643 if (GTK_IS_TREE_MODEL_FILTER (model))
1644 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));