[glib/wip/nielsdg/g-list-store-find: 43/43] gliststore: Add item lookup functions



commit d8687278a640a31fb78f3143918197f2e2003732
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Sun Sep 8 15:42:57 2019 +0200

    gliststore: Add item lookup functions
    
    Currently, there is no quick way to find whether and element is already
    part of a list store, except for manually writing a for-loop and calling
    `g_list_model_get_item()` and breaking when you find the item.
    
    This is mostly just a small API addition to support this use case.
    
    Fixes https://gitlab.gnome.org/GNOME/glib/issues/1011

 gio/gliststore.c       | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gliststore.h       | 11 +++++++
 gio/tests/glistmodel.c | 67 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 161 insertions(+)
---
diff --git a/gio/gliststore.c b/gio/gliststore.c
index 7b2a453d6..864b60df5 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, @first_position
+ * will be set to the position where @item occurred for the first time.
+ *
+ * Since: 2.62
+ */
+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, @first_position
+ * will be set to the position where @item occurred for the first time.
+ *
+ * Since: 2.62
+ */
+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 ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]