1/* Copyright (C) 2016 The giomm Development Team 2 * 3 * This library is free software; you can redistribute it and/or 4 * modify it under the terms of the GNU Lesser General Public 5 * License as published by the Free Software Foundation; either 6 * version 2.1 of the License, or (at your option) any later version. 7 * 8 * This library is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public 14 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17#include <glibmm/object.h> 18#include <giomm/listmodel.h> 19#include <vector> 20#include <type_traits> 21 22_DEFS(giomm,gio) 23_PINCLUDE(glibmm/private/object_p.h) 24 25namespace Gio 26{ 27 28/** A simple implementation of Gio::ListModel that stores all items in memory. 29 * 30 * The templated subclass ListStore<> provides better compile-time type safety. 31 * 32 * It provides insertions, deletions, and lookups in logarithmic time 33 * with a fast path for the common case of iterating the list linearly. 34 * 35 * @newin{2,50} 36 */ 37class GIOMM_API ListStoreBase 38: public Glib::Object, 39 public ListModel 40{ 41 _CLASS_GOBJECT(ListStoreBase, GListStore, G_LIST_STORE, Glib::Object, GObject, , , GIOMM_API) 42 _IMPLEMENTS_INTERFACE(ListModel) 43 _STRUCT_NOT_HIDDEN 44 45protected: 46 _WRAP_CTOR(ListStoreBase(GType item_type), g_list_store_new) 47 48public: 49 _WRAP_CREATE(GType item_type) 50 51#m4 _CONVERSION(`const Glib::RefPtr<Glib::ObjectBase>&',`gpointer',`($3)->gobj()') 52 53 _WRAP_METHOD(void insert(guint position, const Glib::RefPtr<Glib::ObjectBase>& item), g_list_store_insert, newin "2,50") 54 55 /** A slot that will be called to compare two items. 56 * The slot should return a negative integer if the first item comes before the second, 57 * 0 if they are equal, or a positive integer if the first value comes after the second. 58 * For instance, 59 * @code 60 * int on_compare_item(const Glib::RefPtr<const Glib::ObjectBase>& item1, const Glib::RefPtr<const Glib::ObjectBase>& item2); 61 * @endcode 62 * 63 * @newin{2,50} 64 */ 65 using SlotCompare = sigc::slot<int, const Glib::RefPtr<const Glib::ObjectBase>&, const Glib::RefPtr<const Glib::ObjectBase>&>; 66 67 _WRAP_METHOD(guint insert_sorted(const Glib::RefPtr<Glib::ObjectBase>& item, 68 const SlotCompare& slot{compare_func}), g_list_store_insert_sorted, 69 slot_name slot, slot_callback ListStoreBase_CompareDataFunc, no_slot_copy, newin "2,50") 70 71 _WRAP_METHOD(void sort(const SlotCompare& slot{compare_func}), g_list_store_sort, 72 slot_name slot, slot_callback ListStoreBase_CompareDataFunc, no_slot_copy, newin "2,50") 73 74 _WRAP_METHOD(void append(const Glib::RefPtr<Glib::ObjectBase>& item), g_list_store_append, newin "2,50") 75 _WRAP_METHOD(void remove(guint position), g_list_store_remove, newin "2,50") 76 _WRAP_METHOD(void remove_all(), g_list_store_remove_all, newin "2,50") 77 78 /** Removes @a n_removals items and adds @a additions.size() items. 79 * @a additions must contain items of type property_item_type() or derived from it. 80 * Empty RefPtr is not permitted. 81 * 82 * This function is more efficient than insert() and remove(), because it only emits 83 * ListModel::signal_items_changed() once for the change. 84 * 85 * The parameters @a position and @a n_removals must be correct (i.e. 86 * @a position + @a n_removals must be less than or equal to the length of 87 * the list at the time this function is called). 88 * 89 * @newin{2,50} 90 * 91 * @param position The position at which to make the change. 92 * @param n_removals The number of items to remove. 93 * @param additions The items to add. 94 */ 95 void splice(guint position, guint n_removals, 96 const std::vector<Glib::RefPtr<Glib::ObjectBase>>& additions); 97 _IGNORE(g_list_store_splice) 98 99 _WRAP_PROPERTY("item-type", GType, newin "2,50") 100 101}; // end class ListStoreBase 102 103/** A simple implementation of Gio::ListModel that stores all items in memory. 104 * 105 * It provides insertions, deletions, and lookups in logarithmic time 106 * with a fast path for the common case of iterating the list linearly. 107 * 108 * @newin{2,50} 109 * 110 * @tparam T_item Base class of the items in the ListStore. All items must 111 * be of type T_item or a type derived from T_item. 112 * T_item must be Glib::Object or a type derived from Glib::Object. 113 */ 114template <typename T_item> 115class ListStore : public ListStoreBase 116{ 117 static_assert(std::is_base_of<Glib::Object, T_item>::value, 118 "T_item must be Glib::Object or derived from Glib::Object."); 119 120protected: 121 ListStore(); 122 123public: 124 static Glib::RefPtr<ListStore> create(); 125 126 /** Get the item at @a position. 127 * If @a position is greater than or equal to the number of 128 * items in @a list, an empty Glib::RefPtr is returned. 129 * 130 * An empty Glib::RefPtr is never returned for an index that is less than the length 131 * of the list. See ListModel::get_n_items(). 132 * 133 * @newin{2,50} 134 * 135 * @param position The position of the item to fetch. 136 * @return The object at @a position. 137 */ 138 Glib::RefPtr<T_item> get_item(guint position); 139 140 /** Get the item at @a position. 141 * If @a position is greater than or equal to the number of 142 * items in @a list, an empty Glib::RefPtr is returned. 143 * 144 * An empty Glib::RefPtr is never returned for an index that is less than the length 145 * of the list. See ListModel::get_n_items(). 146 * 147 * @newin{2,50} 148 * 149 * @param position The position of the item to fetch. 150 * @return The object at @a position. 151 */ 152 Glib::RefPtr<const T_item> get_item(guint position) const; 153 154 /** Inserts @a item at @a position. 155 * @a item must be of type ListStoreBase::property_item_type() or derived from it. 156 * @a position must be smaller than the length of the list, or equal to it to append. 157 * 158 * Use splice() to insert multiple items at the same time efficiently. 159 * 160 * @newin{2,50} 161 * 162 * @param position The position at which to insert the new item. 163 * @param item The new item. 164 */ 165 void insert(guint position, const Glib::RefPtr<T_item>& item); 166 167 /** A slot that will be called to compare two items. 168 * The slot should return a negative integer if the first item comes before the second, 169 * 0 if they are equal, or a positive integer if the first value comes after the second. 170 * For instance, 171 * @code 172 * int on_compare_item(const Glib::RefPtr<const T_item>& item1, const Glib::RefPtr<const T_item>& item2); 173 * @endcode 174 * 175 * @newin{2,50} 176 */ 177 using SlotCompare = sigc::slot<int, const Glib::RefPtr<const T_item>&, const Glib::RefPtr<const T_item>&>; 178 179 /** Inserts @a item at a position to be determined by the @a slot. 180 * 181 * The list must already be sorted before calling this function or the 182 * result is undefined. Usually you would approach this by only ever 183 * inserting items by way of this function. 184 * 185 * @newin{2,50} 186 * 187 * @param item The new item. 188 * @param slot Pairwise comparison function for sorting. 189 * @return The position at which @a item was inserted. 190 */ 191 guint insert_sorted(const Glib::RefPtr<T_item>& item, const SlotCompare& slot); 192 193 /** Sorts the items according to @a slot. 194 * 195 * @newin{2,50} 196 * 197 * @param slot Pairwise comparison function for sorting. 198 */ 199 void sort(const SlotCompare& slot); 200 201 /** Appends @a item. 202 * @a item must be of type ListStoreBase::property_item_type() or derived from it. 203 * 204 * Use splice() to append multiple items at the same time efficiently. 205 * 206 * @newin{2,50} 207 * 208 * @param item The new item. 209 */ 210 void append(const Glib::RefPtr<T_item>& item); 211 212 /** Removes @a n_removals items and adds @a additions.size() items. 213 * @a additions must contain items of type ListStoreBase::property_item_type() 214 * or derived from it. Empty RefPtr is not permitted. 215 * 216 * This function is more efficient than insert() and remove(), because it only emits 217 * ListModel::signal_items_changed() once for the change. 218 * 219 * The parameters @a position and @a n_removals must be correct (i.e. 220 * @a position + @a n_removals must be less than or equal to the length of 221 * the list at the time this function is called). 222 * 223 * @newin{2,50} 224 * 225 * @param position The position at which to make the change. 226 * @param n_removals The number of items to remove. 227 * @param additions The items to add. 228 */ 229 void splice(guint position, guint n_removals, 230 const std::vector<Glib::RefPtr<T_item>>& additions); 231 232private: 233 static int compare_data_func(gconstpointer a, gconstpointer b, gpointer user_data); 234}; // end class ListStore 235 236#ifndef DOXYGEN_SHOULD_SKIP_THIS 237 238template <typename T_item> 239ListStore<T_item>::ListStore() 240: ListStoreBase(T_item::get_base_type()) 241{ } 242 243template <typename T_item> 244Glib::RefPtr<ListStore<T_item>> ListStore<T_item>::create() 245{ 246 return Glib::RefPtr<ListStore<T_item>>(new ListStore<T_item>()); 247} 248 249template <typename T_item> 250Glib::RefPtr<T_item> ListStore<T_item>::get_item(guint position) 251{ 252 return Glib::RefPtr<T_item>::cast_dynamic(ListModel::get_object(position)); 253} 254 255template <typename T_item> 256Glib::RefPtr<const T_item> ListStore<T_item>::get_item(guint position) const 257{ 258 return const_cast<ListStore<T_item>*>(this)->get_item(position); 259} 260 261template <typename T_item> 262void ListStore<T_item>::insert(guint position, const Glib::RefPtr<T_item>& item) 263{ 264 ListStoreBase::insert(position, item); 265} 266 267template <typename T_item> 268guint ListStore<T_item>::insert_sorted( 269 const Glib::RefPtr<T_item>& item, const SlotCompare& slot) 270{ 271 // Use the original slot (not a copy). 272 auto slot_copy = const_cast<SlotCompare*>(&slot); 273 274 return g_list_store_insert_sorted(gobj(), item->gobj(), &compare_data_func, slot_copy); 275} 276 277template <typename T_item> 278void ListStore<T_item>::sort(const SlotCompare& slot) 279{ 280 // Use the original slot (not a copy). 281 auto slot_copy = const_cast<SlotCompare*>(&slot); 282 283 g_list_store_sort(gobj(), &compare_data_func, slot_copy); 284} 285 286template <typename T_item> 287void ListStore<T_item>::append(const Glib::RefPtr<T_item>& item) 288{ 289 ListStoreBase::append(item); 290} 291 292template <typename T_item> 293void ListStore<T_item>::splice(guint position, guint n_removals, 294 const std::vector<Glib::RefPtr<T_item>>& additions) 295{ 296 const std::size_t n_additions = additions.size(); 297 std::unique_ptr<gpointer[]> g_additions{new gpointer[n_additions]}; 298 for (std::size_t i = 0; i < n_additions; i++) 299 { 300 g_additions[i] = additions[i]->gobj(); 301 } 302 g_list_store_splice(gobj(), position, n_removals, g_additions.get(), n_additions); 303} 304 305template <typename T_item> 306int ListStore<T_item>::compare_data_func(gconstpointer a, gconstpointer b, gpointer user_data) 307{ 308 auto slot = static_cast<SlotCompare*>(user_data); 309 310 // cast_dynamic is necessary if T_item is a user-derived class, such as 311 // class MyObject : public Glib::Object 312 const Glib::RefPtr<const T_item> item_a = Glib::RefPtr<T_item>::cast_dynamic( 313 Glib::wrap(static_cast<typename T_item::BaseObjectType*>(const_cast<gpointer>(a)), true)); 314 const Glib::RefPtr<const T_item> item_b = Glib::RefPtr<T_item>::cast_dynamic( 315 Glib::wrap(static_cast<typename T_item::BaseObjectType*>(const_cast<gpointer>(b)), true)); 316 317 return (*slot)(item_a, item_b); 318} 319 320#endif // DOXYGEN_SHOULD_SKIP_THIS 321 322} // namespace Gio 323