[glibmm] Gio::ListStore: Add find()



commit d0d08f9d826517fd2e02ae6300732faefe072f9d
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Mon Aug 29 16:13:32 2022 +0200

    Gio::ListStore: Add find()
    
    * gio/src/liststore.[ccg|hg]: Add two ListStoreBase::find() and
    two ListStore::find().
    * tests/giomm_listmodel/main.cc: Test ListStore::find().

 gio/src/liststore.ccg         |  34 +++++++++++
 gio/src/liststore.hg          | 128 ++++++++++++++++++++++++++++++++++++++++++
 tests/giomm_listmodel/main.cc |  72 ++++++++++++++++++++++++
 3 files changed, 234 insertions(+)
---
diff --git a/gio/src/liststore.ccg b/gio/src/liststore.ccg
index 3b2191d9..9c95e7ef 100644
--- a/gio/src/liststore.ccg
+++ b/gio/src/liststore.ccg
@@ -32,6 +32,19 @@ int ListStoreBase_CompareDataFunc(gconstpointer a, gconstpointer b, gpointer use
 
   return (*slot)(item_a, item_b);
 }
+
+// gboolean is int
+gboolean ListStoreBase_EqualFuncFull(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+  auto slot = static_cast<Gio::ListStoreBase::SlotEqual*>(user_data);
+
+  const Glib::RefPtr<const Glib::ObjectBase> item_a =
+    Glib::wrap(static_cast<Glib::Object::BaseObjectType*>(const_cast<gpointer>(a)), true);
+  const Glib::RefPtr<const Glib::ObjectBase> item_b =
+    Glib::wrap(static_cast<Glib::Object::BaseObjectType*>(const_cast<gpointer>(b)), true);
+
+  return (*slot)(item_a, item_b);
+}
 }
 } // anonymous namespace
 
@@ -49,4 +62,25 @@ void ListStoreBase::splice(guint position, guint n_removals,
   g_list_store_splice(gobj(), position, n_removals, g_additions.get(), n_additions);
 }
 
+std::pair<bool, unsigned int> ListStoreBase::find(const Glib::RefPtr<const Glib::ObjectBase>& item) const
+{
+  unsigned int position = std::numeric_limits<unsigned int>::max();
+  bool result = g_list_store_find(const_cast<GListStore*>(gobj()),
+    const_cast<GObject*>(item->gobj()), &position);
+  return {result, position};
+}
+
+std::pair<bool, unsigned int> ListStoreBase::find(
+  const Glib::RefPtr<const Glib::ObjectBase>& item, const SlotEqual& slot) const
+{
+  // Use the original slot (not a copy).
+  auto slot_ptr = const_cast<SlotEqual*>(&slot);
+
+  unsigned int position = std::numeric_limits<unsigned int>::max();
+  bool result = g_list_store_find_with_equal_func_full(
+    const_cast<GListStore*>(gobj()), const_cast<GObject*>(item->gobj()),
+    &ListStoreBase_EqualFuncFull, slot_ptr, &position);
+  return {result, position};
+}
+
 } // namespace Gio
diff --git a/gio/src/liststore.hg b/gio/src/liststore.hg
index 3bef97ec..3a28b944 100644
--- a/gio/src/liststore.hg
+++ b/gio/src/liststore.hg
@@ -18,6 +18,8 @@
 #include <giomm/listmodel.h>
 #include <vector>
 #include <type_traits>
+#include <limits>
+#include <utility>
 
 _DEFS(giomm,gio)
 _PINCLUDE(glibmm/private/object_p.h)
@@ -98,6 +100,51 @@ public:
     const std::vector<Glib::RefPtr<Glib::ObjectBase>>& additions);
   _IGNORE(g_list_store_splice)
 
+  /** Looks up the given @a item in the list store by looping over the items until
+   * the first occurrence of @a item.
+   *
+   * If you need to compare the two items with a custom comparison function, use
+   * find(const Glib::RefPtr<const Glib::ObjectBase>& item, const SlotEqual& slot) const instead.
+   *
+   * @newin{2,74}
+   *
+   * @param item An item.
+   * @return std::pair{item_found, position} Whether the %ListStoreBase contains @a item.
+   * If it was found, @a position will be set to the position where @a item
+   * occurred for the first time, else @a position = std::numeric_limits<unsigned int>::max().
+   */
+  std::pair<bool, unsigned int> find(const Glib::RefPtr<const Glib::ObjectBase>& item) const;
+  _IGNORE(g_list_store_find)
+
+  /** A slot that will be called to compare two items.
+   * The slot should return <tt>true</tt> if the items are equal,
+   * <tt>false</tt> if they are not equal.
+   * For instance,
+   * @code
+   * bool on_equal_item(const Glib::RefPtr<const Glib::ObjectBase>& item1, const Glib::RefPtr<const 
Glib::ObjectBase>& item2);
+   * @endcode
+   *
+   * @newin{2,74}
+   */
+  using SlotEqual = sigc::slot<bool(const Glib::RefPtr<const Glib::ObjectBase>&, const Glib::RefPtr<const 
Glib::ObjectBase>&)>;
+
+  /** Looks up the given @a item in the list store by looping over the items until
+   * the first occurrence of @a item.
+   *
+   * If you don't need to compare the two items with a custom comparison function,
+   * use find(const Glib::RefPtr<const Glib::ObjectBase>& item) const instead.
+   *
+   * @newin{2,74}
+   *
+   * @param item An item.
+   * @param slot A comparison function.
+   * @return std::pair{item_found, position} Whether the %ListStoreBase contains @a item.
+   * If it was found, @a position will be set to the position where @a item
+   * occurred for the first time, else @a position = std::numeric_limits<unsigned int>::max().
+   */
+  std::pair<bool, unsigned int> find(const Glib::RefPtr<const Glib::ObjectBase>& item, const SlotEqual& 
slot) const;
+  _IGNORE(g_list_store_find_with_equal_func, g_list_store_find_with_equal_func_full)
+
   _WRAP_PROPERTY("item-type", GType, newin "2,50")
   _WRAP_PROPERTY("n-items", unsigned int)
 
@@ -232,8 +279,53 @@ public:
   void splice(guint position, guint n_removals,
     const std::vector<Glib::RefPtr<T_item>>& additions);
 
+  /** Looks up the given @a item in the list store by looping over the items until
+   * the first occurrence of @a item.
+   *
+   * If you need to compare the two items with a custom comparison function, use
+   * find(const Glib::RefPtr<const T_item>& item, const SlotEqual& slot) const instead.
+   *
+   * @newin{2,74}
+   *
+   * @param item An item.
+   * @return std::pair{item_found, position} Whether the %ListStore contains @a item.
+   * If it was found, @a position will be set to the position where @a item
+   * occurred for the first time, else @a position = std::numeric_limits<unsigned int>::max().
+   */
+  std::pair<bool, unsigned int> find(const Glib::RefPtr<const T_item>& item) const;
+
+  /** A slot that will be called to compare two items.
+   * The slot should return <tt>true</tt> if the items are equal,
+   * <tt>false</tt> if they are not equal.
+   * For instance,
+   * @code
+   * bool on_equal_item(const Glib::RefPtr<const T_item>& item1, const Glib::RefPtr<const T_item>& item2);
+   * @endcode
+   *
+   * @newin{2,74}
+   */
+  using SlotEqual = sigc::slot<bool(const Glib::RefPtr<const T_item>&, const Glib::RefPtr<const T_item>&)>;
+
+  /** Looks up the given @a item in the list store by looping over the items until
+   * the first occurrence of @a item.
+   *
+   * If you don't need to compare the two items with a custom comparison function,
+   * use find(const Glib::RefPtr<const T_item>& item) const instead.
+   *
+   * @newin{2,74}
+   *
+   * @param item An item.
+   * @param slot A comparison function.
+   * @return std::pair{item_found, position} Whether the %ListStore contains @a item.
+   * If it was found, @a position will be set to the position where @a item
+   * occurred for the first time, else @a position = std::numeric_limits<unsigned int>::max().
+   */
+  std::pair<bool, unsigned int> find(const Glib::RefPtr<const T_item>& item, const SlotEqual& slot) const;
+
 private:
   static int compare_data_func(gconstpointer a, gconstpointer b, gpointer user_data);
+  // gboolean is int
+  static gboolean equal_func_full(gconstpointer a, gconstpointer b, gpointer user_data);
 }; // end class ListStore
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
@@ -305,6 +397,27 @@ void ListStore<T_item>::splice(guint position, guint n_removals,
   g_list_store_splice(gobj(), position, n_removals, g_additions.get(), n_additions);
 }
 
+template <typename T_item>
+std::pair<bool, unsigned int> ListStore<T_item>::find(
+  const Glib::RefPtr<const T_item>& item) const
+{
+  return ListStoreBase::find(item);
+}
+
+template <typename T_item>
+std::pair<bool, unsigned int> ListStore<T_item>::find(
+  const Glib::RefPtr<const T_item>& item, const SlotEqual& slot) const
+{
+  // Use the original slot (not a copy).
+  auto slot_ptr = const_cast<SlotEqual*>(&slot);
+
+  unsigned int position = std::numeric_limits<unsigned int>::max();
+  bool result = g_list_store_find_with_equal_func_full(
+    const_cast<GListStore*>(gobj()), const_cast<typename T_item::BaseObjectType*>(item->gobj()),
+    &equal_func_full, slot_ptr, &position);
+  return {result, position};
+}
+
 template <typename T_item>
 int ListStore<T_item>::compare_data_func(gconstpointer a, gconstpointer b, gpointer user_data)
 {
@@ -320,6 +433,21 @@ int ListStore<T_item>::compare_data_func(gconstpointer a, gconstpointer b, gpoin
   return (*slot)(item_a, item_b);
 }
 
+template <typename T_item>
+gboolean ListStore<T_item>::equal_func_full(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+  auto slot = static_cast<SlotEqual*>(user_data);
+
+  // cast_dynamic is necessary if T_item is a user-derived class, such as
+  // class MyObject : public Glib::Object
+  const Glib::RefPtr<const T_item> item_a = std::dynamic_pointer_cast<T_item>(
+    Glib::wrap(static_cast<typename T_item::BaseObjectType*>(const_cast<gpointer>(a)), true));
+  const Glib::RefPtr<const T_item> item_b = std::dynamic_pointer_cast<T_item>(
+    Glib::wrap(static_cast<typename T_item::BaseObjectType*>(const_cast<gpointer>(b)), true));
+
+  return (*slot)(item_a, item_b);
+}
+
 #endif // DOXYGEN_SHOULD_SKIP_THIS
 
 } // namespace Gio
diff --git a/tests/giomm_listmodel/main.cc b/tests/giomm_listmodel/main.cc
index 1024a05e..f92fc5c7 100644
--- a/tests/giomm_listmodel/main.cc
+++ b/tests/giomm_listmodel/main.cc
@@ -19,6 +19,7 @@
 #include <giomm.h>
 #include <cstdlib>
 #include <iostream>
+#include <tuple>
 
 namespace
 {
@@ -324,6 +325,76 @@ void test_store_sorted2()
   }
 } // end test_store_sorted2()
 
+void check_found_item_position(int icall, bool found_item, unsigned int position,
+  bool expected_found_item, unsigned int expected_position)
+{
+  if (found_item != expected_found_item || position != expected_position)
+  {
+    result = EXIT_FAILURE;
+    std::cerr << "test_store_find(), " << icall << ": found_item="
+      << found_item << ", position=" << position << std::endl;
+  }
+}
+
+bool casecmp_action_by_name(const Glib::RefPtr<const Gio::SimpleAction>& a,
+  const Glib::RefPtr<const Gio::SimpleAction>& b, const Glib::ustring& suffix)
+{
+  const auto a_name = a->get_name();
+  const auto b_name = b->get_name() + suffix;
+  if (suffix.empty())
+    return a_name.casefold() == b_name.casefold();
+  else
+    return a_name == b_name;
+}
+
+void test_store_find()
+{
+  const std::vector<Glib::ustring> item_strings{ "aaa", "bbb", "xxx", "ccc" };
+  std::vector<Glib::RefPtr<Gio::SimpleAction>> items;
+
+  for (auto& item_string : item_strings)
+    items.push_back(Gio::SimpleAction::create(item_string));
+  auto store = Gio::ListStore<Gio::SimpleAction>::create();
+
+  // Shouldn't crash on an empty list, or change the position pointer.
+  auto [found_item, position] = store->find(items[0]);
+  check_found_item_position(1, found_item, position,
+    false, std::numeric_limits<unsigned int>::max());
+
+  for (auto& item : items)
+    store->append(item);
+
+  // Check whether it could still find the the elements.
+  for (unsigned int i = 0; i < item_strings.size(); ++i)
+  {
+    std::tie(found_item, position) = store->find(items[i]);
+    check_found_item_position(1+i, found_item, position, true, i);
+  }
+
+  // Try to find element not part of the list.
+  auto other_item = Gio::SimpleAction::create("111");
+  std::tie(found_item, position) = store->find(other_item);
+  check_found_item_position(6, found_item, position,
+    false, std::numeric_limits<unsigned int>::max());
+
+  // Re-add item; find() should only return the first position.
+  store->append(items[0]);
+  std::tie(found_item, position) = store->find(items[0]);
+  check_found_item_position(7, found_item, position, true, 0);
+
+  // Try to find element which should only work with custom equality check.
+  other_item = Gio::SimpleAction::create("XXX");
+  std::tie(found_item, position) = store->find(other_item,
+    sigc::bind(sigc::ptr_fun(casecmp_action_by_name), ""));
+  check_found_item_position(8, found_item, position, true, 2);
+
+  other_item = Gio::SimpleAction::create("c");
+  std::tie(found_item, position) = store->find(other_item,
+    sigc::bind(sigc::ptr_fun(casecmp_action_by_name), "cc"));
+  check_found_item_position(8, found_item, position, true, 3);
+
+} // end test_store_find()
+
 } // anonymous namespace
 
 int main(int, char**)
@@ -334,6 +405,7 @@ int main(int, char**)
   test_store_refcounts();
   test_store_sorted1();
   test_store_sorted2();
+  test_store_find();
 
   return result;
 }


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