diff options
author | Kjell Ahlstedt <kjellahlstedt@gmail.com> | 2022-08-29 16:13:32 +0200 |
---|---|---|
committer | Kjell Ahlstedt <kjellahlstedt@gmail.com> | 2022-08-29 16:13:32 +0200 |
commit | d0d08f9d826517fd2e02ae6300732faefe072f9d (patch) | |
tree | 11db1249dec5afec6c362a8d7a1a967b0139754e | |
parent | 818d7a22708ddc225b6363cd845e6cd72481028d (diff) | |
download | glibmm-d0d08f9d826517fd2e02ae6300732faefe072f9d.tar.gz |
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().
-rw-r--r-- | gio/src/liststore.ccg | 34 | ||||
-rw-r--r-- | gio/src/liststore.hg | 128 | ||||
-rw-r--r-- | tests/giomm_listmodel/main.cc | 72 |
3 files changed, 234 insertions, 0 deletions
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 @@ -306,6 +398,27 @@ void ListStore<T_item>::splice(guint position, guint n_removals, } 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) { auto slot = static_cast<SlotCompare*>(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; } |