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