/* Copyright (C) 2016 The giomm Development Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include #include #include #include _DEFS(giomm,gio) _PINCLUDE(glibmm/private/object_p.h) namespace Gio { /** A simple implementation of Gio::ListModel that stores all items in memory. * * The templated subclass ListStore<> provides better compile-time type safety. * * It provides insertions, deletions, and lookups in logarithmic time * with a fast path for the common case of iterating the list linearly. * * @newin{2,50} */ class GIOMM_API ListStoreBase : public Glib::Object, public ListModel { _CLASS_GOBJECT(ListStoreBase, GListStore, G_LIST_STORE, Glib::Object, GObject, , , GIOMM_API) _IMPLEMENTS_INTERFACE(ListModel) _DO_NOT_DERIVE_GTYPE dnl// GListStore is a final type _ABI_AS_WITH_DERIVED_GTYPE dnl// Remove when we can break ABI _STRUCT_NOT_HIDDEN protected: _WRAP_CTOR(ListStoreBase(GType item_type), g_list_store_new) public: _WRAP_CREATE(GType item_type) #m4 _CONVERSION(`const Glib::RefPtr&',`gpointer',`($3)->gobj()') _WRAP_METHOD(void insert(guint position, const Glib::RefPtr& item), g_list_store_insert, newin "2,50") /** A slot that will be called to compare two items. * The slot should return a negative integer if the first item comes before the second, * 0 if they are equal, or a positive integer if the first value comes after the second. * For instance, * @code * int on_compare_item(const Glib::RefPtr& item1, const Glib::RefPtr& item2); * @endcode * * @newin{2,50} */ using SlotCompare = sigc::slot&, const Glib::RefPtr&)>; _WRAP_METHOD(guint insert_sorted(const Glib::RefPtr& item, const SlotCompare& slot{compare_func}), g_list_store_insert_sorted, slot_name slot, slot_callback ListStoreBase_CompareDataFunc, no_slot_copy, newin "2,50") _WRAP_METHOD(void sort(const SlotCompare& slot{compare_func}), g_list_store_sort, slot_name slot, slot_callback ListStoreBase_CompareDataFunc, no_slot_copy, newin "2,50") _WRAP_METHOD(void append(const Glib::RefPtr& item), g_list_store_append, newin "2,50") _WRAP_METHOD(void remove(guint position), g_list_store_remove, newin "2,50") _WRAP_METHOD(void remove_all(), g_list_store_remove_all, newin "2,50") /** Removes @a n_removals items and adds @a additions.size() items. * @a additions must contain items of type property_item_type() or derived from it. * Empty RefPtr is not permitted. * * This function is more efficient than insert() and remove(), because it only emits * ListModel::signal_items_changed() once for the change. * * The parameters @a position and @a n_removals must be correct (i.e. * @a position + @a n_removals must be less than or equal to the length of * the list at the time this function is called). * * @newin{2,50} * * @param position The position at which to make the change. * @param n_removals The number of items to remove. * @param additions The items to add. */ void splice(guint position, guint n_removals, const std::vector>& 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& item, const SlotEqual& slot) const instead. * * @newin{2,74} * * @param item An item. * @return {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::max(). */ std::pair find(const Glib::RefPtr& item) const; _IGNORE(g_list_store_find) /** A slot that will be called to compare two items. * The slot should return true if the items are equal, * false if they are not equal. * For instance, * @code * bool on_equal_item(const Glib::RefPtr& item1, const Glib::RefPtr& item2); * @endcode * * @newin{2,74} */ using SlotEqual = sigc::slot&, const Glib::RefPtr&)>; /** 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& item) const instead. * * @newin{2,74} * * @param item An item. * @param slot A comparison function. * @return {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::max(). */ std::pair find(const Glib::RefPtr& 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) }; // end class ListStoreBase /** A simple implementation of Gio::ListModel that stores all items in memory. * * It provides insertions, deletions, and lookups in logarithmic time * with a fast path for the common case of iterating the list linearly. * * @newin{2,50} * * @tparam T_item Base class of the items in the ListStore. All items must * be of type T_item or a type derived from T_item. * T_item must be Glib::ObjectBase or a type derived from Glib::ObjectBase. */ template class ListStore : public ListStoreBase { static_assert(std::is_base_of::value, "T_item must be Glib::ObjectBase or derived from Glib::ObjectBase."); protected: ListStore(); public: static Glib::RefPtr create(); /** Get the item at @a position. * If @a position is greater than or equal to the number of * items in @a list, an empty Glib::RefPtr is returned. * * An empty Glib::RefPtr is never returned for an index that is less than the length * of the list. See ListModel::get_n_items(). * * @newin{2,50} * * @param position The position of the item to fetch. * @return The object at @a position. */ Glib::RefPtr get_item(guint position); /** Get the item at @a position. * If @a position is greater than or equal to the number of * items in @a list, an empty Glib::RefPtr is returned. * * An empty Glib::RefPtr is never returned for an index that is less than the length * of the list. See ListModel::get_n_items(). * * @newin{2,50} * * @param position The position of the item to fetch. * @return The object at @a position. */ Glib::RefPtr get_item(guint position) const; /** Inserts @a item at @a position. * @a item must be of type ListStoreBase::property_item_type() or derived from it. * @a position must be smaller than the length of the list, or equal to it to append. * * Use splice() to insert multiple items at the same time efficiently. * * @newin{2,50} * * @param position The position at which to insert the new item. * @param item The new item. */ void insert(guint position, const Glib::RefPtr& item); /** A slot that will be called to compare two items. * The slot should return a negative integer if the first item comes before the second, * 0 if they are equal, or a positive integer if the first value comes after the second. * For instance, * @code * int on_compare_item(const Glib::RefPtr& item1, const Glib::RefPtr& item2); * @endcode * * @newin{2,50} */ using SlotCompare = sigc::slot&, const Glib::RefPtr&)>; /** Inserts @a item at a position to be determined by the @a slot. * * The list must already be sorted before calling this function or the * result is undefined. Usually you would approach this by only ever * inserting items by way of this function. * * @newin{2,50} * * @param item The new item. * @param slot Pairwise comparison function for sorting. * @return The position at which @a item was inserted. */ guint insert_sorted(const Glib::RefPtr& item, const SlotCompare& slot); /** Sorts the items according to @a slot. * * @newin{2,50} * * @param slot Pairwise comparison function for sorting. */ void sort(const SlotCompare& slot); /** Appends @a item. * @a item must be of type ListStoreBase::property_item_type() or derived from it. * * Use splice() to append multiple items at the same time efficiently. * * @newin{2,50} * * @param item The new item. */ void append(const Glib::RefPtr& item); /** Removes @a n_removals items and adds @a additions.size() items. * @a additions must contain items of type ListStoreBase::property_item_type() * or derived from it. Empty RefPtr is not permitted. * * This function is more efficient than insert() and remove(), because it only emits * ListModel::signal_items_changed() once for the change. * * The parameters @a position and @a n_removals must be correct (i.e. * @a position + @a n_removals must be less than or equal to the length of * the list at the time this function is called). * * @newin{2,50} * * @param position The position at which to make the change. * @param n_removals The number of items to remove. * @param additions The items to add. */ void splice(guint position, guint n_removals, const std::vector>& 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& item, const SlotEqual& slot) const instead. * * @newin{2,74} * * @param item An item. * @return {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::max(). */ std::pair find(const Glib::RefPtr& item) const; /** A slot that will be called to compare two items. * The slot should return true if the items are equal, * false if they are not equal. * For instance, * @code * bool on_equal_item(const Glib::RefPtr& item1, const Glib::RefPtr& item2); * @endcode * * @newin{2,74} */ using SlotEqual = sigc::slot&, const Glib::RefPtr&)>; /** 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& item) const instead. * * @newin{2,74} * * @param item An item. * @param slot A comparison function. * @return {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::max(). */ std::pair find(const Glib::RefPtr& 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 template ListStore::ListStore() : ListStoreBase(T_item::get_base_type()) { } template Glib::RefPtr> ListStore::create() { return Glib::make_refptr_for_instance>(new ListStore()); } template Glib::RefPtr ListStore::get_item(guint position) { return std::dynamic_pointer_cast(ListModel::get_object(position)); } template Glib::RefPtr ListStore::get_item(guint position) const { return const_cast*>(this)->get_item(position); } template void ListStore::insert(guint position, const Glib::RefPtr& item) { ListStoreBase::insert(position, item); } template guint ListStore::insert_sorted( const Glib::RefPtr& item, const SlotCompare& slot) { // Use the original slot (not a copy). auto slot_copy = const_cast(&slot); return g_list_store_insert_sorted(gobj(), item->gobj(), &compare_data_func, slot_copy); } template void ListStore::sort(const SlotCompare& slot) { // Use the original slot (not a copy). auto slot_copy = const_cast(&slot); g_list_store_sort(gobj(), &compare_data_func, slot_copy); } template void ListStore::append(const Glib::RefPtr& item) { ListStoreBase::append(item); } template void ListStore::splice(guint position, guint n_removals, const std::vector>& additions) { const std::size_t n_additions = additions.size(); std::unique_ptr g_additions{new gpointer[n_additions]}; for (std::size_t i = 0; i < n_additions; i++) { g_additions[i] = additions[i]->gobj(); } g_list_store_splice(gobj(), position, n_removals, g_additions.get(), n_additions); } template std::pair ListStore::find( const Glib::RefPtr& item) const { return ListStoreBase::find(item); } template std::pair ListStore::find( const Glib::RefPtr& item, const SlotEqual& slot) const { // Use the original slot (not a copy). auto slot_ptr = const_cast(&slot); unsigned int position = std::numeric_limits::max(); bool find_result = g_list_store_find_with_equal_func_full( const_cast(gobj()), const_cast(item->gobj()), &equal_func_full, slot_ptr, &position); return {find_result, position}; } template int ListStore::compare_data_func(gconstpointer a, gconstpointer b, gpointer user_data) { auto slot = static_cast(user_data); // cast_dynamic is necessary if T_item is a user-derived class, such as // class MyObject : public Glib::Object const Glib::RefPtr item_a = std::dynamic_pointer_cast( Glib::wrap(static_cast(const_cast(a)), true)); const Glib::RefPtr item_b = std::dynamic_pointer_cast( Glib::wrap(static_cast(const_cast(b)), true)); return (*slot)(item_a, item_b); } template gboolean ListStore::equal_func_full(gconstpointer a, gconstpointer b, gpointer user_data) { auto slot = static_cast(user_data); // cast_dynamic is necessary if T_item is a user-derived class, such as // class MyObject : public Glib::Object const Glib::RefPtr item_a = std::dynamic_pointer_cast( Glib::wrap(static_cast(const_cast(a)), true)); const Glib::RefPtr item_b = std::dynamic_pointer_cast( Glib::wrap(static_cast(const_cast(b)), true)); return (*slot)(item_a, item_b); } #endif // DOXYGEN_SHOULD_SKIP_THIS } // namespace Gio