/* 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
_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)
_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)
_WRAP_PROPERTY("item-type", GType, newin "2,50")
}; // 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::Object or a type derived from Glib::Object.
*/
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);
private:
static int compare_data_func(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
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);
}
#endif // DOXYGEN_SHOULD_SKIP_THIS
} // namespace Gio