From a1c07af56c5769a6aa0683c998f5f1f258b72183 Mon Sep 17 00:00:00 2001 From: Alban Crequy Date: Wed, 5 May 2010 17:49:07 +0300 Subject: Use a GtkTreeRowReference in HildonLiveSearch instead of GtkTreePath This fixes a bug when the underlying model changes and the selection becomes corrupt. Also, this patch uses a more efficient approach when creating and using the mapping, avoiding iterating the whole model but the selected items only instead. Fixes: NB#165952 (HildonLiveSearch selection is not consistent when the model changes) Signed-off-by: Claudio Saavedra --- hildon/hildon-live-search.c | 247 ++++++++++++++++++++++++++------------------ 1 file changed, 145 insertions(+), 102 deletions(-) diff --git a/hildon/hildon-live-search.c b/hildon/hildon-live-search.c index 933afec..3344fc1 100644 --- a/hildon/hildon-live-search.c +++ b/hildon/hildon-live-search.c @@ -103,10 +103,11 @@ hash_func (gconstpointer key) gchar *path_str; guint val; - path = (GtkTreePath *) key; + path = gtk_tree_row_reference_get_path ((GtkTreeRowReference *) key); path_str = gtk_tree_path_to_string (path); val = g_str_hash (path_str); g_free (path_str); + gtk_tree_path_free (path); return val; } @@ -115,28 +116,18 @@ static gboolean key_equal_func (gconstpointer v1, gconstpointer v2) { - return gtk_tree_path_compare ((GtkTreePath *)v1, - (GtkTreePath *)v2) == 0; -} + gboolean ret; + GtkTreePath *path1; + GtkTreePath *path2; -static gboolean -model_get_root (GtkTreeModelFilter *filter, - GtkTreeModel *base_model, - GtkTreeIter *iter_child) -{ - GtkTreeIter virtual_root; - GtkTreePath *virtual_root_path; - gboolean walking; + path1 = gtk_tree_row_reference_get_path ((GtkTreeRowReference *) v1); + path2 = gtk_tree_row_reference_get_path ((GtkTreeRowReference *) v2); + ret = gtk_tree_path_compare (path1, path2) == 0; - g_object_get (filter, "virtual-root", &virtual_root_path, NULL); - if (virtual_root_path) { - gtk_tree_model_get_iter (base_model, &virtual_root, virtual_root_path); - } - walking = gtk_tree_model_iter_children (base_model, iter_child, - virtual_root_path ? - &virtual_root : NULL); + gtk_tree_path_free (path1); + gtk_tree_path_free (path2); - return walking; + return ret; } /** @@ -149,30 +140,15 @@ model_get_root (GtkTreeModelFilter *filter, static void selection_map_create (HildonLiveSearchPrivate *priv) { - gboolean walking; - GtkTreeModel *base_model; - GtkTreeIter iter_child; - GtkTreePath *path; - if (!GTK_IS_TREE_VIEW (priv->kb_focus_widget)) return; g_assert (priv->selection_map == NULL); - base_model = gtk_tree_model_filter_get_model (priv->filter); - priv->selection_map = g_hash_table_new_full - (hash_func, key_equal_func, (GDestroyNotify) gtk_tree_path_free, NULL); - - walking = model_get_root (priv->filter, base_model, &iter_child); - - while (walking) { - path = gtk_tree_model_get_path (base_model, &iter_child); - g_hash_table_insert (priv->selection_map, - path, GINT_TO_POINTER (FALSE)); + (hash_func, key_equal_func, + (GDestroyNotify) gtk_tree_row_reference_free, NULL); - walking = gtk_tree_model_iter_next (base_model, &iter_child); - } } /** @@ -202,14 +178,65 @@ convert_child_path_to_path (GtkTreeModel *model, if (model == base_model) return gtk_tree_path_copy (path); - g_assert (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(model)) == - base_model); g_assert (GTK_IS_TREE_MODEL_SORT (model)); + g_assert (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(model)) == base_model); return gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (model), path); } +static GtkTreePath * +convert_path_to_child_path (GtkTreeModel *model, + GtkTreeModel *base_model, + GtkTreePath *path) +{ + g_return_val_if_fail (model != NULL, NULL); + g_return_val_if_fail (base_model != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + if (model == base_model) + return gtk_tree_path_copy (path); + + g_assert (GTK_IS_TREE_MODEL_SORT (model)); + g_assert (gtk_tree_model_sort_get_model (model) == base_model); + + return gtk_tree_model_sort_convert_path_to_child_path + (GTK_TREE_MODEL_SORT (model), path); +} + +static gboolean +row_reference_is_not_selected (GtkTreeRowReference *row_ref, + gpointer value, + HildonLiveSearchPrivate *priv) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (priv->kb_focus_widget)); + GtkTreePath *base_path = gtk_tree_row_reference_get_path (row_ref); + GtkTreePath *filter_path = gtk_tree_model_filter_convert_child_path_to_path + (priv->filter, base_path); + GtkTreePath *view_path; + gboolean ret; + + if (filter_path == NULL) + { + gtk_tree_path_free (base_path); + return FALSE; + } + + view_path = convert_child_path_to_path ( + gtk_tree_view_get_model (GTK_TREE_VIEW (priv->kb_focus_widget)), + GTK_TREE_MODEL (priv->filter), filter_path); + + ret = ! gtk_tree_selection_path_is_selected (selection, view_path); + + gtk_tree_path_free (base_path); + gtk_tree_path_free (filter_path); + gtk_tree_path_free (view_path); + + return ret; +} + + /** * selection_map_update_map_from_selection: * @priv: The private pimpl @@ -220,10 +247,8 @@ convert_child_path_to_path (GtkTreeModel *model, static void selection_map_update_map_from_selection (HildonLiveSearchPrivate *priv) { - gboolean walking; GtkTreeModel *base_model; GtkTreeSelection *selection; - GtkTreeIter iter_child; if (!GTK_IS_TREE_VIEW (priv->kb_focus_widget)) return; @@ -231,40 +256,45 @@ selection_map_update_map_from_selection (HildonLiveSearchPrivate *priv) base_model = gtk_tree_model_filter_get_model (priv->filter); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->kb_focus_widget)); - walking = model_get_root (priv->filter, base_model, &iter_child); - - while (walking) { - GtkTreePath *base_path, *filter_path, *view_path = NULL; - base_path = gtk_tree_model_get_path (base_model, &iter_child); - filter_path = gtk_tree_model_filter_convert_child_path_to_path - (priv->filter, base_path); - if (filter_path) { - view_path = convert_child_path_to_path ( - gtk_tree_view_get_model (GTK_TREE_VIEW (priv->kb_focus_widget)), - GTK_TREE_MODEL (priv->filter), filter_path); - gtk_tree_path_free (filter_path); - } - if (view_path) { - if (gtk_tree_selection_path_is_selected - (selection, view_path)) { - g_hash_table_replace - (priv->selection_map, - base_path, - GINT_TO_POINTER (TRUE)); - } else { - g_hash_table_replace - (priv->selection_map, - base_path, - GINT_TO_POINTER (FALSE)); - } - gtk_tree_path_free (view_path); - } else { - gtk_tree_path_free (base_path); - } - walking = gtk_tree_model_iter_next (base_model, &iter_child); + /* Remove all items from priv->selection_map which are not selected */ + g_hash_table_foreach_remove (priv->selection_map, + (GHRFunc) row_reference_is_not_selected, priv); + + /* fill priv->selection_map from gtk_tree_selection_get_selected_rows()*/ + GList *selected_list = gtk_tree_selection_get_selected_rows (selection, + NULL); + while (selected_list) { + GtkTreePath *view_path = selected_list->data; + GtkTreePath *base_path, *filter_path; + GtkTreeRowReference *row_ref; + + filter_path = convert_path_to_child_path ( + gtk_tree_view_get_model (GTK_TREE_VIEW (priv->kb_focus_widget)), + GTK_TREE_MODEL (priv->filter), view_path); + base_path = gtk_tree_model_filter_convert_path_to_child_path + (priv->filter, filter_path); + + row_ref = gtk_tree_row_reference_new (base_model, base_path); + g_hash_table_replace (priv->selection_map, + row_ref, NULL); + gtk_tree_path_free (view_path); + gtk_tree_path_free (filter_path); + gtk_tree_path_free (base_path); + selected_list->data = NULL; + selected_list = g_list_delete_link (selected_list, selected_list); } } +gboolean +reference_row_has_path (GtkTreeRowReference *row_ref, gpointer value, GtkTreePath *path) +{ + gboolean ret; + GtkTreePath *path2 = gtk_tree_row_reference_get_path (row_ref); + ret = gtk_tree_path_compare (path, path2) == 0; + gtk_tree_path_free (path2); + return ret; +} + /** * selection_map_update_selection_from_map: * @priv: The private pimpl @@ -275,10 +305,8 @@ selection_map_update_map_from_selection (HildonLiveSearchPrivate *priv) static void selection_map_update_selection_from_map (HildonLiveSearchPrivate *priv) { - gboolean walking; GtkTreeModel *base_model; GtkTreeSelection *selection; - GtkTreeIter iter_child; if (!GTK_IS_TREE_VIEW (priv->kb_focus_widget)) return; @@ -286,36 +314,51 @@ selection_map_update_selection_from_map (HildonLiveSearchPrivate *priv) base_model = gtk_tree_model_filter_get_model (priv->filter); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->kb_focus_widget)); - walking = model_get_root (priv->filter, base_model, &iter_child); + /* unselect things which are not in priv->selection_map */ + GList *selected_list = gtk_tree_selection_get_selected_rows (selection, + NULL); + while (selected_list) { + GtkTreePath *view_path = selected_list->data; + GtkTreePath *base_path, *filter_path; + filter_path = convert_path_to_child_path ( + gtk_tree_view_get_model (GTK_TREE_VIEW (priv->kb_focus_widget)), + GTK_TREE_MODEL (priv->filter), view_path); + base_path = gtk_tree_model_filter_convert_path_to_child_path + (priv->filter, filter_path); + + if (g_hash_table_find (priv->selection_map, + (GHRFunc) reference_row_has_path, base_path) == NULL) + gtk_tree_selection_unselect_path + (selection, view_path); + + gtk_tree_path_free (view_path); + gtk_tree_path_free (filter_path); + gtk_tree_path_free (base_path); + selected_list->data = NULL; + + selected_list = g_list_delete_link (selected_list, selected_list); + } + + /* going though priv->selection_map to select items */ + GHashTableIter iter; + gpointer key, value; - while (walking) { - GtkTreePath *base_path, *filter_path, *view_path = NULL; - base_path = gtk_tree_model_get_path (base_model, &iter_child); - filter_path = gtk_tree_model_filter_convert_child_path_to_path + g_hash_table_iter_init (&iter, priv->selection_map); + while (g_hash_table_iter_next (&iter, &key, &value)) { + GtkTreeRowReference *row_ref = key; + GtkTreePath *base_path = gtk_tree_row_reference_get_path (row_ref); + GtkTreePath *filter_path = gtk_tree_model_filter_convert_child_path_to_path (priv->filter, base_path); - if (filter_path) { - view_path = convert_child_path_to_path ( - gtk_tree_view_get_model (GTK_TREE_VIEW (priv->kb_focus_widget)), - GTK_TREE_MODEL (priv->filter), filter_path); - gtk_tree_path_free (filter_path); - } - if (view_path) { - gboolean selected; - selected = GPOINTER_TO_INT - (g_hash_table_lookup - (priv->selection_map, base_path)); - - if (selected) { - gtk_tree_selection_select_path - (selection, view_path); - } else { - gtk_tree_selection_unselect_path - (selection, view_path); - } - gtk_tree_path_free (view_path); - } - gtk_tree_path_free (base_path); - walking = gtk_tree_model_iter_next (base_model, &iter_child); + GtkTreePath *view_path; + + if (filter_path == NULL) + continue; + + view_path = convert_child_path_to_path ( + gtk_tree_view_get_model (GTK_TREE_VIEW (priv->kb_focus_widget)), + GTK_TREE_MODEL (priv->filter), filter_path); + + gtk_tree_selection_select_path (selection, view_path); } } -- cgit v1.2.3-18-g5258