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>
36 #include <modest-header-view.h>
37 #include <modest-header-view-priv.h>
38 #include <modest-dnd.h>
39 #include <modest-tny-folder.h>
41 #include <modest-main-window.h>
43 #include <modest-marshal.h>
44 #include <modest-text-utils.h>
45 #include <modest-icon-names.h>
46 #include <modest-runtime.h>
47 #include "modest-platform.h"
48 #include <modest-hbox-cell-renderer.h>
49 #include <modest-vbox-cell-renderer.h>
51 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
52 static void modest_header_view_init (ModestHeaderView *obj);
53 static void modest_header_view_finalize (GObject *obj);
54 static void modest_header_view_dispose (GObject *obj);
56 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
57 GtkTreeViewColumn *column, gpointer userdata);
59 static gint cmp_rows (GtkTreeModel *tree_model,
64 static gint cmp_subject_rows (GtkTreeModel *tree_model,
69 static gboolean filter_row (GtkTreeModel *model,
73 static void on_selection_changed (GtkTreeSelection *sel,
76 static void setup_drag_and_drop (GtkTreeView *self);
78 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
80 static gboolean on_focus_in (GtkWidget *sef,
84 static void folder_monitor_update (TnyFolderObserver *self,
85 TnyFolderChange *change);
87 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
89 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
91 static void _clear_hidding_filter (ModestHeaderView *header_view);
94 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
95 struct _ModestHeaderViewPrivate {
97 ModestHeaderViewStyle style;
99 TnyFolderMonitor *monitor;
100 GMutex *observers_lock;
102 /* not unref this object, its a singlenton */
103 ModestEmailClipboard *clipboard;
105 /* Filter tree model */
109 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
110 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
115 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
116 struct _HeadersCountChangedHelper {
117 ModestHeaderView *self;
118 TnyFolderChange *change;
122 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
123 MODEST_TYPE_HEADER_VIEW, \
124 ModestHeaderViewPrivate))
128 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
131 HEADER_SELECTED_SIGNAL,
132 HEADER_ACTIVATED_SIGNAL,
133 ITEM_NOT_FOUND_SIGNAL,
134 MSG_COUNT_CHANGED_SIGNAL,
139 static GObjectClass *parent_class = NULL;
141 /* uncomment the following if you have defined any signals */
142 static guint signals[LAST_SIGNAL] = {0};
145 modest_header_view_get_type (void)
147 static GType my_type = 0;
149 static const GTypeInfo my_info = {
150 sizeof(ModestHeaderViewClass),
151 NULL, /* base init */
152 NULL, /* base finalize */
153 (GClassInitFunc) modest_header_view_class_init,
154 NULL, /* class finalize */
155 NULL, /* class data */
156 sizeof(ModestHeaderView),
158 (GInstanceInitFunc) modest_header_view_init,
162 static const GInterfaceInfo tny_folder_observer_info =
164 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
165 NULL, /* interface_finalize */
166 NULL /* interface_data */
168 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
172 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
173 &tny_folder_observer_info);
181 modest_header_view_class_init (ModestHeaderViewClass *klass)
183 GObjectClass *gobject_class;
184 gobject_class = (GObjectClass*) klass;
186 parent_class = g_type_class_peek_parent (klass);
187 gobject_class->finalize = modest_header_view_finalize;
188 gobject_class->dispose = modest_header_view_dispose;
190 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
192 signals[HEADER_SELECTED_SIGNAL] =
193 g_signal_new ("header_selected",
194 G_TYPE_FROM_CLASS (gobject_class),
196 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
198 g_cclosure_marshal_VOID__POINTER,
199 G_TYPE_NONE, 1, G_TYPE_POINTER);
201 signals[HEADER_ACTIVATED_SIGNAL] =
202 g_signal_new ("header_activated",
203 G_TYPE_FROM_CLASS (gobject_class),
205 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
207 g_cclosure_marshal_VOID__POINTER,
208 G_TYPE_NONE, 1, G_TYPE_POINTER);
211 signals[ITEM_NOT_FOUND_SIGNAL] =
212 g_signal_new ("item_not_found",
213 G_TYPE_FROM_CLASS (gobject_class),
215 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
217 g_cclosure_marshal_VOID__INT,
218 G_TYPE_NONE, 1, G_TYPE_INT);
220 signals[MSG_COUNT_CHANGED_SIGNAL] =
221 g_signal_new ("msg_count_changed",
222 G_TYPE_FROM_CLASS (gobject_class),
224 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
226 modest_marshal_VOID__POINTER_POINTER,
227 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
231 tny_folder_observer_init (TnyFolderObserverIface *klass)
233 klass->update_func = folder_monitor_update;
236 static GtkTreeViewColumn*
237 get_new_column (const gchar *name, GtkCellRenderer *renderer,
238 gboolean resizable, gint sort_col_id, gboolean show_as_text,
239 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
241 GtkTreeViewColumn *column;
243 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
244 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
246 gtk_tree_view_column_set_resizable (column, resizable);
248 gtk_tree_view_column_set_expand (column, TRUE);
251 gtk_tree_view_column_add_attribute (column, renderer, "text",
253 if (sort_col_id >= 0)
254 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
256 gtk_tree_view_column_set_sort_indicator (column, FALSE);
257 gtk_tree_view_column_set_reorderable (column, TRUE);
260 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
267 remove_all_columns (ModestHeaderView *obj)
269 GList *columns, *cursor;
271 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
273 for (cursor = columns; cursor; cursor = cursor->next)
274 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
275 GTK_TREE_VIEW_COLUMN(cursor->data));
276 g_list_free (columns);
280 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
282 GtkTreeModel *tree_filter, *sortable;
283 GtkTreeViewColumn *column=NULL;
284 GtkTreeSelection *selection = NULL;
285 GtkCellRenderer *renderer_msgtype,*renderer_header,
286 *renderer_attach, *renderer_compact_date_or_status;
287 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
288 *renderer_subject, *renderer_subject_box, *renderer_recpt,
290 ModestHeaderViewPrivate *priv;
291 GtkTreeViewColumn *compact_column = NULL;
294 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
296 /* FIXME: check whether these renderers need to be freed */
297 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
298 renderer_attach = gtk_cell_renderer_pixbuf_new ();
299 renderer_priority = gtk_cell_renderer_pixbuf_new ();
300 renderer_header = gtk_cell_renderer_text_new ();
302 renderer_compact_header = modest_vbox_cell_renderer_new ();
303 renderer_recpt_box = modest_hbox_cell_renderer_new ();
304 renderer_subject_box = modest_hbox_cell_renderer_new ();
305 renderer_recpt = gtk_cell_renderer_text_new ();
306 renderer_subject = gtk_cell_renderer_text_new ();
307 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
309 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
310 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
311 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
312 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
313 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
314 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
315 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
316 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
317 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
318 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
319 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
320 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
321 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
322 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
324 g_object_set(G_OBJECT(renderer_header),
325 "ellipsize", PANGO_ELLIPSIZE_END,
327 g_object_set (G_OBJECT (renderer_subject),
328 "ellipsize", PANGO_ELLIPSIZE_END,
330 g_object_set (G_OBJECT (renderer_recpt),
331 "ellipsize", PANGO_ELLIPSIZE_END,
333 g_object_set(G_OBJECT(renderer_compact_date_or_status),
337 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 32);
338 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 32);
340 remove_all_columns (self);
342 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
343 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
344 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
345 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
346 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
348 /* Add new columns */
349 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
350 ModestHeaderViewColumn col =
351 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
353 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
354 g_printerr ("modest: invalid column %d in column list\n", col);
360 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
361 column = get_new_column (_("M"), renderer_msgtype, FALSE,
362 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
364 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
366 gtk_tree_view_column_set_fixed_width (column, 45);
369 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
370 column = get_new_column (_("A"), renderer_attach, FALSE,
371 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
373 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
375 gtk_tree_view_column_set_fixed_width (column, 45);
379 case MODEST_HEADER_VIEW_COLUMN_FROM:
380 column = get_new_column (_("From"), renderer_header, TRUE,
381 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
383 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
384 GINT_TO_POINTER(TRUE));
387 case MODEST_HEADER_VIEW_COLUMN_TO:
388 column = get_new_column (_("To"), renderer_header, TRUE,
389 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
391 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
392 GINT_TO_POINTER(FALSE));
395 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
396 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
397 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
399 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
400 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
401 compact_column = column;
404 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
405 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
406 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
408 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
409 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
410 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
411 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
412 compact_column = column;
416 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
417 column = get_new_column (_("Subject"), renderer_header, TRUE,
418 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
420 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
424 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
425 column = get_new_column (_("Received"), renderer_header, TRUE,
426 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
428 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
429 GINT_TO_POINTER(TRUE));
432 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
433 column = get_new_column (_("Sent"), renderer_header, TRUE,
434 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
436 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
437 GINT_TO_POINTER(FALSE));
440 case MODEST_HEADER_VIEW_COLUMN_SIZE:
441 column = get_new_column (_("Size"), renderer_header, TRUE,
442 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
444 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
447 case MODEST_HEADER_VIEW_COLUMN_STATUS:
448 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
449 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
451 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
456 g_return_val_if_reached(FALSE);
459 /* we keep the column id around */
460 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
461 GINT_TO_POINTER(col));
463 /* we need this ptr when sorting the rows */
464 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
466 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
470 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
471 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
472 (GtkTreeIterCompareFunc) cmp_rows,
473 compact_column, NULL);
474 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
475 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
476 (GtkTreeIterCompareFunc) cmp_subject_rows,
477 compact_column, NULL);
485 modest_header_view_init (ModestHeaderView *obj)
487 ModestHeaderViewPrivate *priv;
490 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
494 priv->monitor = NULL;
495 priv->observers_lock = g_mutex_new ();
497 priv->clipboard = modest_runtime_get_email_clipboard ();
498 priv->hidding_ids = NULL;
499 priv->n_selected = 0;
501 /* Sort parameters */
502 for (j=0; j < 2; j++) {
503 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
504 priv->sort_colid[j][i] = -1;
505 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
509 setup_drag_and_drop (GTK_TREE_VIEW (obj));
513 modest_header_view_dispose (GObject *obj)
515 ModestHeaderView *self;
516 ModestHeaderViewPrivate *priv;
518 self = MODEST_HEADER_VIEW(obj);
519 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
522 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
523 g_object_unref (G_OBJECT (priv->folder));
527 G_OBJECT_CLASS(parent_class)->dispose (obj);
531 modest_header_view_finalize (GObject *obj)
533 ModestHeaderView *self;
534 ModestHeaderViewPrivate *priv;
536 self = MODEST_HEADER_VIEW(obj);
537 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
539 g_mutex_lock (priv->observers_lock);
541 tny_folder_monitor_stop (priv->monitor);
542 g_object_unref (G_OBJECT (priv->monitor));
544 g_mutex_unlock (priv->observers_lock);
545 g_mutex_free (priv->observers_lock);
547 /* Clear hidding array created by cut operation */
548 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
550 G_OBJECT_CLASS(parent_class)->finalize (obj);
555 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
558 GtkTreeSelection *sel;
559 ModestHeaderView *self;
560 ModestHeaderViewPrivate *priv;
562 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
565 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
566 self = MODEST_HEADER_VIEW(obj);
567 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
569 modest_header_view_set_style (self, style);
570 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
572 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
573 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
574 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
576 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
577 TRUE); /* alternating row colors */
578 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
580 g_signal_connect (sel, "changed",
581 G_CALLBACK(on_selection_changed), self);
583 g_signal_connect (self, "row-activated",
584 G_CALLBACK (on_header_row_activated), NULL);
586 g_signal_connect (self, "focus-in-event",
587 G_CALLBACK(on_focus_in), NULL);
589 return GTK_WIDGET(self);
594 modest_header_view_count_selected_headers (ModestHeaderView *self)
596 GtkTreeSelection *sel;
599 g_return_val_if_fail (self, 0);
601 /* Get selection object and check selected rows count */
602 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
603 selected_rows = gtk_tree_selection_count_selected_rows (sel);
605 return selected_rows;
609 modest_header_view_has_selected_headers (ModestHeaderView *self)
611 GtkTreeSelection *sel;
614 g_return_val_if_fail (self, FALSE);
616 /* Get selection object and check selected rows count */
617 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
618 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
625 modest_header_view_get_selected_headers (ModestHeaderView *self)
627 GtkTreeSelection *sel;
628 ModestHeaderViewPrivate *priv;
629 TnyList *header_list = NULL;
631 GList *list, *tmp = NULL;
632 GtkTreeModel *tree_model = NULL;
635 g_return_val_if_fail (self, NULL);
637 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
639 /* Get selected rows */
640 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
641 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
644 header_list = tny_simple_list_new();
646 list = g_list_reverse (list);
649 /* get header from selection */
650 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
651 gtk_tree_model_get (tree_model, &iter,
652 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
654 /* Prepend to list */
655 tny_list_prepend (header_list, G_OBJECT (header));
656 g_object_unref (G_OBJECT (header));
658 tmp = g_list_next (tmp);
661 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
668 /* scroll our list view so the selected item is visible */
670 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
672 #ifdef MODEST_PLATFORM_GNOME
674 GtkTreePath *selected_path;
675 GtkTreePath *start, *end;
679 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
680 selected_path = gtk_tree_model_get_path (model, iter);
682 start = gtk_tree_path_new ();
683 end = gtk_tree_path_new ();
685 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
687 if (gtk_tree_path_compare (selected_path, start) < 0 ||
688 gtk_tree_path_compare (end, selected_path) < 0)
689 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
690 selected_path, NULL, TRUE,
693 gtk_tree_path_free (selected_path);
694 gtk_tree_path_free (start);
695 gtk_tree_path_free (end);
697 #endif /* MODEST_PLATFORM_GNOME */
702 modest_header_view_select_next (ModestHeaderView *self)
704 GtkTreeSelection *sel;
709 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
710 path = get_selected_row (GTK_TREE_VIEW(self), &model);
711 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
712 /* Unselect previous path */
713 gtk_tree_selection_unselect_path (sel, path);
715 /* Move path down and selects new one */
716 if (gtk_tree_model_iter_next (model, &iter)) {
717 gtk_tree_selection_select_iter (sel, &iter);
718 scroll_to_selected (self, &iter, FALSE);
720 gtk_tree_path_free(path);
726 modest_header_view_select_prev (ModestHeaderView *self)
728 GtkTreeSelection *sel;
733 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
734 path = get_selected_row (GTK_TREE_VIEW(self), &model);
735 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
736 /* Unselect previous path */
737 gtk_tree_selection_unselect_path (sel, path);
740 if (gtk_tree_path_prev (path)) {
741 gtk_tree_model_get_iter (model, &iter, path);
743 /* Select the new one */
744 gtk_tree_selection_select_iter (sel, &iter);
745 scroll_to_selected (self, &iter, TRUE);
748 gtk_tree_path_free (path);
753 modest_header_view_get_columns (ModestHeaderView *self)
755 g_return_val_if_fail (self, FALSE);
756 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
761 modest_header_view_is_empty (ModestHeaderView *self)
763 g_return_val_if_fail (self, FALSE);
764 return FALSE; /* FIXME */
769 modest_header_view_set_style (ModestHeaderView *self,
770 ModestHeaderViewStyle style)
772 ModestHeaderViewPrivate *priv;
773 gboolean show_col_headers = FALSE;
774 ModestHeaderViewStyle old_style;
776 g_return_val_if_fail (self, FALSE);
777 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
780 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
781 if (priv->style == style)
782 return TRUE; /* nothing to do */
785 case MODEST_HEADER_VIEW_STYLE_DETAILS:
786 show_col_headers = TRUE;
788 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
791 g_return_val_if_reached (FALSE);
793 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
794 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
796 old_style = priv->style;
803 ModestHeaderViewStyle
804 modest_header_view_get_style (ModestHeaderView *self)
806 g_return_val_if_fail (self, FALSE);
807 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
811 * This function sets a sortable model in the header view. It's just
812 * used for developing purposes, because it only does a
813 * gtk_tree_view_set_model
816 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
818 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
819 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
820 /* GtkTreeModel *old_model; */
821 /* ModestHeaderViewPrivate *priv; */
822 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
823 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
825 /* /\* Set new model *\/ */
826 /* gtk_tree_view_set_model (header_view, model); */
828 gtk_tree_view_set_model (header_view, model);
832 modest_header_view_get_folder (ModestHeaderView *self)
834 ModestHeaderViewPrivate *priv;
835 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
838 g_object_ref (priv->folder);
844 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
848 ModestHeaderViewPrivate *priv;
849 GList *cols, *cursor;
850 GtkTreeModel *filter_model, *sortable;
852 GtkSortType sort_type;
854 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
856 headers = TNY_LIST (tny_gtk_header_list_model_new ());
858 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
861 /* Add IDLE observer (monitor) and another folder observer for
862 new messages (self) */
863 g_mutex_lock (priv->observers_lock);
865 tny_folder_monitor_stop (priv->monitor);
866 g_object_unref (G_OBJECT (priv->monitor));
868 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
869 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
870 tny_folder_monitor_start (priv->monitor);
871 g_mutex_unlock (priv->observers_lock);
873 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
874 g_object_unref (G_OBJECT (headers));
876 /* Create a tree model filter to hide and show rows for cut operations */
877 filter_model = gtk_tree_model_filter_new (sortable, NULL);
878 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
882 g_object_unref (G_OBJECT (sortable));
884 /* install our special sorting functions */
885 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
887 /* Restore sort column id */
889 type = modest_tny_folder_guess_folder_type (folder);
890 sort_colid = modest_header_view_get_sort_column_id (self, type);
891 sort_type = modest_header_view_get_sort_type (self, type);
892 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
895 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
896 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
897 (GtkTreeIterCompareFunc) cmp_rows,
899 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
900 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
901 (GtkTreeIterCompareFunc) cmp_subject_rows,
906 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
907 g_object_unref (G_OBJECT (filter_model));
908 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
909 /* g_object_unref (G_OBJECT (sortable)); */
916 modest_header_view_sort_by_column_id (ModestHeaderView *self,
918 GtkSortType sort_type)
920 ModestHeaderViewPrivate *priv = NULL;
921 GtkTreeModel *tree_filter, *sortable = NULL;
924 /* Get model and private data */
925 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
926 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
927 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
928 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
930 /* Sort tree model */
931 type = modest_tny_folder_guess_folder_type (priv->folder);
932 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
935 /* Store new sort parameters */
936 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
938 /* Save GConf parameters */
939 /* modest_widget_memory_save (modest_runtime_get_conf(), */
940 /* G_OBJECT(self), "header-view"); */
945 modest_header_view_set_sort_params (ModestHeaderView *self,
947 GtkSortType sort_type,
950 ModestHeaderViewPrivate *priv;
951 ModestHeaderViewStyle style;
953 style = modest_header_view_get_style (self);
954 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
956 priv->sort_colid[style][type] = sort_colid;
957 priv->sort_type[style][type] = sort_type;
961 modest_header_view_get_sort_column_id (ModestHeaderView *self,
964 ModestHeaderViewPrivate *priv;
965 ModestHeaderViewStyle style;
967 style = modest_header_view_get_style (self);
968 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
970 return priv->sort_colid[style][type];
974 modest_header_view_get_sort_type (ModestHeaderView *self,
977 ModestHeaderViewPrivate *priv;
978 ModestHeaderViewStyle style;
980 style = modest_header_view_get_style (self);
981 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
983 return priv->sort_type[style][type];
987 ModestHeaderView *header_view;
988 RefreshAsyncUserCallback cb;
993 folder_refreshed_cb (const GObject *obj,
997 ModestHeaderViewPrivate *priv;
998 SetFolderHelper *info;
1000 info = (SetFolderHelper*) user_data;
1002 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1006 info->cb (obj, folder, info->user_data);
1008 /* Start the folder count changes observer. We do not need it
1009 before the refresh. Note that the monitor could still be
1010 called for this refresh but now we know that the callback
1011 was previously called */
1012 g_mutex_lock (priv->observers_lock);
1013 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1014 g_mutex_unlock (priv->observers_lock);
1021 modest_header_view_set_folder (ModestHeaderView *self,
1023 RefreshAsyncUserCallback callback,
1026 ModestHeaderViewPrivate *priv;
1027 ModestWindowMgr *mgr = NULL;
1028 GObject *source = NULL;
1029 SetFolderHelper *info;
1031 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1034 g_mutex_lock (priv->observers_lock);
1035 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1036 g_object_unref (priv->folder);
1037 priv->folder = NULL;
1038 g_mutex_unlock (priv->observers_lock);
1042 ModestMailOperation *mail_op = NULL;
1044 /* Get main window to use it as source of mail operation */
1045 mgr = modest_runtime_get_window_mgr ();
1046 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1048 /* Set folder in the model */
1049 modest_header_view_set_folder_intern (self, folder);
1051 /* Pick my reference. Nothing to do with the mail operation */
1052 priv->folder = g_object_ref (folder);
1054 /* no message selected */
1055 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1057 info = g_malloc0 (sizeof(SetFolderHelper));
1058 info->header_view = self;
1059 info->cb = callback;
1060 info->user_data = user_data;
1062 /* Create the mail operation (source will be the parent widget) */
1063 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1064 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1067 /* Refresh the folder asynchronously */
1068 modest_mail_operation_refresh_folder (mail_op,
1070 folder_refreshed_cb,
1074 g_object_unref (mail_op);
1076 g_mutex_lock (priv->observers_lock);
1078 if (priv->monitor) {
1079 tny_folder_monitor_stop (priv->monitor);
1080 g_object_unref (G_OBJECT (priv->monitor));
1081 priv->monitor = NULL;
1083 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1085 g_mutex_unlock (priv->observers_lock);
1090 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1091 GtkTreeViewColumn *column, gpointer userdata)
1093 ModestHeaderView *self = NULL;
1094 ModestHeaderViewPrivate *priv = NULL;
1096 GtkTreeModel *model = NULL;
1097 TnyHeader *header = NULL;
1098 TnyHeaderFlags flags;
1100 self = MODEST_HEADER_VIEW (treeview);
1101 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1103 model = gtk_tree_view_get_model (treeview);
1104 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1107 /* get the first selected item */
1108 gtk_tree_model_get (model, &iter,
1109 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1110 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1113 /* Dont open DELETED messages */
1114 if (flags & TNY_HEADER_FLAG_DELETED) {
1115 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1120 g_signal_emit (G_OBJECT(self),
1121 signals[HEADER_ACTIVATED_SIGNAL],
1127 g_object_unref (G_OBJECT (header));
1132 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1134 GtkTreeModel *model;
1136 GtkTreePath *path = NULL;
1138 ModestHeaderView *self;
1139 ModestHeaderViewPrivate *priv;
1140 GList *selected = NULL;
1142 g_return_if_fail (sel);
1143 g_return_if_fail (user_data);
1145 self = MODEST_HEADER_VIEW (user_data);
1146 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1148 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1149 if (selected != NULL)
1150 path = (GtkTreePath *) selected->data;
1151 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1152 return; /* msg was _un_selected */
1154 gtk_tree_model_get (model, &iter,
1155 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1159 g_signal_emit (G_OBJECT(self),
1160 signals[HEADER_SELECTED_SIGNAL],
1163 g_object_unref (G_OBJECT (header));
1165 /* free all items in 'selected' */
1166 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1167 g_list_free (selected);
1171 /* PROTECTED method. It's useful when we want to force a given
1172 selection to reload a msg. For example if we have selected a header
1173 in offline mode, when Modest become online, we want to reload the
1174 message automatically without an user click over the header */
1176 _modest_header_view_change_selection (GtkTreeSelection *selection,
1179 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1180 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1182 on_selection_changed (selection, user_data);
1185 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1187 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1188 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1190 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1192 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1197 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1204 /* static int counter = 0; */
1206 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1207 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1208 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1212 case TNY_HEADER_FLAG_ATTACHMENTS:
1214 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1215 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1216 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1217 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1219 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1220 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1222 return cmp ? cmp : t1 - t2;
1224 case TNY_HEADER_FLAG_PRIORITY:
1225 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1226 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1227 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1228 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1230 /* This is for making priority values respect the intuitive sort relationship
1231 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1232 cmp = compare_priorities (val1, val2);
1234 return cmp ? cmp : t1 - t2;
1237 return &iter1 - &iter2; /* oughhhh */
1242 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1248 /* static int counter = 0; */
1250 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1252 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1253 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1254 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1255 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1257 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1258 val2 + modest_text_utils_get_subject_prefix_len(val2),
1265 /* Drag and drop stuff */
1267 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1268 GtkSelectionData *selection_data,
1269 guint info, guint time, gpointer data)
1271 GtkTreeModel *model = NULL;
1273 GtkTreePath *source_row = NULL;
1275 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1276 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1279 case MODEST_HEADER_ROW:
1280 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1284 gtk_tree_model_get (model, &iter,
1285 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1288 g_object_unref (G_OBJECT(hdr));
1293 g_message ("%s: default switch case.", __FUNCTION__);
1296 gtk_tree_path_free (source_row);
1299 /* Header view drag types */
1300 const GtkTargetEntry header_view_drag_types[] = {
1301 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1302 { "text/uri-list", 0, MODEST_MSG },
1306 setup_drag_and_drop (GtkTreeView *self)
1308 gtk_drag_source_set (GTK_WIDGET (self),
1310 header_view_drag_types,
1311 G_N_ELEMENTS (header_view_drag_types),
1312 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1314 g_signal_connect(G_OBJECT (self), "drag_data_get",
1315 G_CALLBACK(drag_data_get_cb), NULL);
1318 static GtkTreePath *
1319 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1321 GtkTreePath *path = NULL;
1322 GtkTreeSelection *sel = NULL;
1325 sel = gtk_tree_view_get_selection(self);
1326 rows = gtk_tree_selection_get_selected_rows (sel, model);
1328 if ((rows == NULL) || (g_list_length(rows) != 1))
1331 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1336 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1343 * This function moves the tree view scroll to the current selected
1344 * row when the widget grabs the focus
1347 on_focus_in (GtkWidget *self,
1348 GdkEventFocus *event,
1351 GtkTreeSelection *selection;
1352 GtkTreeModel *model;
1353 GList *selected = NULL;
1354 GtkTreePath *selected_path = NULL;
1356 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1360 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1361 /* If none selected yet, pick the first one */
1362 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1366 /* Return if the model is empty */
1367 if (!gtk_tree_model_get_iter_first (model, &iter))
1370 path = gtk_tree_model_get_path (model, &iter);
1371 gtk_tree_selection_select_path (selection, path);
1372 gtk_tree_path_free (path);
1375 /* Need to get the all the rows because is selection multiple */
1376 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1377 if (selected == NULL) return FALSE;
1378 selected_path = (GtkTreePath *) selected->data;
1380 /* Check if we need to scroll */
1381 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1382 GtkTreePath *start_path = NULL;
1383 GtkTreePath *end_path = NULL;
1384 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1388 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1389 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1391 /* Scroll to first path */
1392 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1401 gtk_tree_path_free (start_path);
1403 gtk_tree_path_free (end_path);
1405 #endif /* GTK_CHECK_VERSION */
1408 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1409 g_list_free (selected);
1415 idle_notify_headers_count_changed_destroy (gpointer data)
1417 HeadersCountChangedHelper *helper = NULL;
1419 g_return_if_fail (data != NULL);
1420 helper = (HeadersCountChangedHelper *) data;
1422 g_object_unref (helper->change);
1423 g_slice_free (HeadersCountChangedHelper, helper);
1427 idle_notify_headers_count_changed (gpointer data)
1429 TnyFolder *folder = NULL;
1430 ModestHeaderViewPrivate *priv = NULL;
1431 HeadersCountChangedHelper *helper = NULL;
1433 g_return_val_if_fail (data != NULL, FALSE);
1434 helper = (HeadersCountChangedHelper *) data;
1435 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1436 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1438 folder = tny_folder_change_get_folder (helper->change);
1440 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1442 g_mutex_lock (priv->observers_lock);
1444 /* Emit signal to evaluate how headers changes affects to the window view */
1445 g_signal_emit (G_OBJECT(helper->self),
1446 signals[MSG_COUNT_CHANGED_SIGNAL],
1447 0, folder, helper->change);
1449 /* Added or removed headers, so data stored on cliboard are invalid */
1450 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1451 modest_email_clipboard_clear (priv->clipboard);
1453 g_mutex_unlock (priv->observers_lock);
1459 folder_monitor_update (TnyFolderObserver *self,
1460 TnyFolderChange *change)
1462 ModestHeaderViewPrivate *priv;
1463 TnyFolderChangeChanged changed;
1464 HeadersCountChangedHelper *helper = NULL;
1466 changed = tny_folder_change_get_changed (change);
1468 /* Do not notify the observers if the folder of the header
1469 view has changed before this call to the observer
1471 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1472 if (tny_folder_change_get_folder (change) != priv->folder)
1475 /* Check folder count */
1476 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1477 (changed & TNY_FOLDER_CHANGE_CHANGED_REMOVED_HEADERS)) {
1478 helper = g_slice_new0 (HeadersCountChangedHelper);
1479 helper->self = MODEST_HEADER_VIEW(self);
1480 helper->change = g_object_ref(change);
1482 g_idle_add_full (G_PRIORITY_DEFAULT,
1483 idle_notify_headers_count_changed,
1485 idle_notify_headers_count_changed_destroy);
1490 modest_header_view_clear (ModestHeaderView *self)
1492 modest_header_view_set_folder (self, NULL, NULL, NULL);
1496 modest_header_view_copy_selection (ModestHeaderView *header_view)
1498 /* Copy selection */
1499 _clipboard_set_selected_data (header_view, FALSE);
1503 modest_header_view_cut_selection (ModestHeaderView *header_view)
1505 ModestHeaderViewPrivate *priv = NULL;
1506 const gchar **hidding = NULL;
1507 guint i, n_selected;
1509 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1510 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1512 /* Copy selection */
1513 _clipboard_set_selected_data (header_view, TRUE);
1515 /* Get hidding ids */
1516 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1518 /* Clear hidding array created by previous cut operation */
1519 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1521 /* Copy hidding array */
1522 priv->n_selected = n_selected;
1523 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1524 for (i=0; i < n_selected; i++)
1525 priv->hidding_ids[i] = g_strdup(hidding[i]);
1527 /* Hide cut headers */
1528 modest_header_view_refilter (header_view);
1535 _clipboard_set_selected_data (ModestHeaderView *header_view,
1538 ModestHeaderViewPrivate *priv = NULL;
1539 TnyList *headers = NULL;
1541 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1542 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1544 /* Set selected data on clipboard */
1545 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1546 headers = modest_header_view_get_selected_headers (header_view);
1547 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1550 g_object_unref (headers);
1556 filter_row (GtkTreeModel *model,
1560 ModestHeaderViewPrivate *priv = NULL;
1561 TnyHeaderFlags flags;
1562 TnyHeader *header = NULL;
1565 gboolean visible = TRUE;
1566 gboolean found = FALSE;
1568 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1569 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1571 /* Get header from model */
1572 gtk_tree_model_get (model, iter,
1573 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1574 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1577 /* Hide mark as deleted heders */
1578 if (flags & TNY_HEADER_FLAG_DELETED) {
1583 /* If no data on clipboard, return always TRUE */
1584 if (modest_email_clipboard_cleared(priv->clipboard)) {
1589 /* Get message id from header (ensure is a valid id) */
1590 if (!header) return FALSE;
1591 id = g_strdup(tny_header_get_message_id (header));
1594 if (priv->hidding_ids != NULL) {
1595 for (i=0; i < priv->n_selected && !found; i++)
1596 if (priv->hidding_ids[i] != NULL && id != NULL)
1597 found = (!strcmp (priv->hidding_ids[i], id));
1605 g_object_unref (header);
1612 _clear_hidding_filter (ModestHeaderView *header_view)
1614 ModestHeaderViewPrivate *priv;
1617 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1618 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1620 if (priv->hidding_ids != NULL) {
1621 for (i=0; i < priv->n_selected; i++)
1622 g_free (priv->hidding_ids[i]);
1623 g_free(priv->hidding_ids);
1628 modest_header_view_refilter (ModestHeaderView *header_view)
1630 GtkTreeModel *model;
1632 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1634 /* Hide cut headers */
1635 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1636 if (GTK_IS_TREE_MODEL_FILTER (model))
1637 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));