diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt index cc980e73b..f2e19293d 100644 --- a/docs/reference/gio/gio-sections-common.txt +++ b/docs/reference/gio/gio-sections-common.txt @@ -4654,6 +4654,8 @@ g_list_store_remove g_list_store_remove_all g_list_store_splice g_list_store_sort +g_list_store_find +g_list_store_find_with_equal_func G_TYPE_LIST_STORE diff --git a/gio/gliststore.c b/gio/gliststore.c index 7b2a453d6..3c2361e1b 100644 --- a/gio/gliststore.c +++ b/gio/gliststore.c @@ -494,3 +494,86 @@ g_list_store_splice (GListStore *store, g_list_store_items_changed (store, position, n_removals, n_additions); } + +/** + * g_list_store_find_with_equal_func: + * @store: a #GListStore + * @item: (type GObject): an item + * @equal_func: (scope call): A custom equality check function + * @position: (out) (optional): the first position of @item, if it was found. + * + * Looks up the given @item in the list store by looping over the items and + * comparing them with @compare_func until the first occurrence of @item which + * matches. If @item was not found, then @position will not be set, and this + * method will return %FALSE. + * + * Returns: Whether @store contains @item. If it was found, @position will be + * set to the position where @item occurred for the first time. + * + * Since: 2.64 + */ +gboolean +g_list_store_find_with_equal_func (GListStore *store, + gpointer item, + GEqualFunc equal_func, + guint *position) +{ + GSequenceIter *iter, *begin, *end; + + g_return_val_if_fail (G_IS_LIST_STORE (store), FALSE); + g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (item), store->item_type), + FALSE); + g_return_val_if_fail (equal_func != NULL, FALSE); + + /* NOTE: We can't use g_sequence_lookup() or g_sequence_search(), because we + * can't assume the sequence is sorted. */ + begin = g_sequence_get_begin_iter (store->items); + end = g_sequence_get_end_iter (store->items); + + iter = begin; + while (iter != end) + { + gpointer iter_item; + + iter_item = g_sequence_get (iter); + if (equal_func (iter_item, item)) + { + if (position) + *position = g_sequence_iter_get_position (iter); + return TRUE; + } + + iter = g_sequence_iter_next (iter); + } + + return FALSE; +} + +/** + * g_list_store_find: + * @store: a #GListStore + * @item: (type GObject): an item + * @position: (out) (optional): the first position of @item, if it was found. + * + * Looks up the given @item in the list store by looping over the items until + * the first occurrence of @item. If @item was not found, then @position will + * not be set, and this method will return %FALSE. + * + * If you need to compare the two items with a custom comparison function, use + * g_list_store_find_with_equal_func() with a custom #GEqualFunc instead. + * + * Returns: Whether @store contains @item. If it was found, @position will be + * set to the position where @item occurred for the first time. + * + * Since: 2.64 + */ +gboolean +g_list_store_find (GListStore *store, + gpointer item, + guint *position) +{ + return g_list_store_find_with_equal_func (store, + item, + g_direct_equal, + position); +} diff --git a/gio/gliststore.h b/gio/gliststore.h index 407d542fb..ef3b83951 100644 --- a/gio/gliststore.h +++ b/gio/gliststore.h @@ -72,6 +72,17 @@ void g_list_store_splice (GListSt gpointer *additions, guint n_additions); +GLIB_AVAILABLE_IN_2_64 +gboolean g_list_store_find (GListStore *store, + gpointer item, + guint *position); + +GLIB_AVAILABLE_IN_2_64 +gboolean g_list_store_find_with_equal_func (GListStore *store, + gpointer item, + GEqualFunc equal_func, + guint *position); + G_END_DECLS #endif /* __G_LIST_STORE_H__ */ diff --git a/gio/tests/glistmodel.c b/gio/tests/glistmodel.c index 562037f62..5ecf45f79 100644 --- a/gio/tests/glistmodel.c +++ b/gio/tests/glistmodel.c @@ -805,6 +805,72 @@ test_store_past_end (void) g_object_unref (store); } +static gboolean +list_model_casecmp_action_by_name (gconstpointer a, + gconstpointer b) +{ + return g_ascii_strcasecmp (g_action_get_name (G_ACTION (a)), + g_action_get_name (G_ACTION (b))) == 0; +} + +/* Test if find() and find_with_equal_func() works */ +static void +test_store_find (void) +{ + GListStore *store; + guint position = 100; + const gchar *item_strs[4] = { "aaa", "bbb", "xxx", "ccc" }; + GSimpleAction *items[4] = { NULL, }; + GSimpleAction *other_item; + guint i; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + + for (i = 0; i < G_N_ELEMENTS (item_strs); i++) + items[i] = g_simple_action_new (item_strs[i], NULL); + + /* Shouldn't crash on an empty list, or change the position pointer */ + g_assert_false (g_list_store_find (store, items[0], NULL)); + g_assert_false (g_list_store_find (store, items[0], &position)); + g_assert_cmpint (position, ==, 100); + + for (i = 0; i < G_N_ELEMENTS (item_strs); i++) + g_list_store_append (store, items[i]); + + /* Check whether it could still find the the elements */ + for (i = 0; i < G_N_ELEMENTS (item_strs); i++) + { + g_assert_true (g_list_store_find (store, items[i], &position)); + g_assert_cmpint (position, ==, i); + /* Shouldn't try to write to position pointer if NULL given */ + g_assert_true (g_list_store_find (store, items[i], NULL)); + } + + /* try to find element not part of the list */ + other_item = g_simple_action_new ("111", NULL); + g_assert_false (g_list_store_find (store, other_item, NULL)); + g_clear_object (&other_item); + + /* Re-add item; find() should only return the first position */ + g_list_store_append (store, items[0]); + g_assert_true (g_list_store_find (store, items[0], &position)); + g_assert_cmpint (position, ==, 0); + + /* try to find element which should only work with custom equality check */ + other_item = g_simple_action_new ("XXX", NULL); + g_assert_false (g_list_store_find (store, other_item, NULL)); + g_assert_true (g_list_store_find_with_equal_func (store, + other_item, + list_model_casecmp_action_by_name, + &position)); + g_assert_cmpint (position, ==, 2); + g_clear_object (&other_item); + + for (i = 0; i < G_N_ELEMENTS (item_strs); i++) + g_clear_object(&items[i]); + g_clear_object (&store); +} + int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); @@ -837,6 +903,7 @@ int main (int argc, char *argv[]) g_test_add_func ("/glistmodel/store/items-changed", test_store_signal_items_changed); g_test_add_func ("/glistmodel/store/past-end", test_store_past_end); + g_test_add_func ("/glistmodel/store/find", test_store_find); return g_test_run (); }