Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgenericitemmodel_impl.h
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QGENERICITEMMODEL_IMPL_H
5#define QGENERICITEMMODEL_IMPL_H
6
7#ifndef Q_QDOC
8
9#ifndef QGENERICITEMMODEL_H
10#error Do not include qgenericitemmodel_impl.h directly
11#endif
12
13#if 0
14#pragma qt_sync_skip_header_check
15#pragma qt_sync_stop_processing
16#endif
17
18#include <QtCore/qabstractitemmodel.h>
19#include <QtCore/qmetaobject.h>
20#include <QtCore/qvariant.h>
21#include <QtCore/qmap.h>
22
23#include <algorithm>
24#include <functional>
25#include <iterator>
26#include <type_traits>
27#include <QtCore/q20type_traits.h>
28#include <tuple>
29#include <QtCore/q23utility.h>
30
31QT_BEGIN_NAMESPACE
32
34{
35 template <typename T, template <typename...> typename... Templates>
37
38 template <template <typename...> typename Template,
39 typename... Params,
40 template <typename...> typename... Templates>
42
43 template <typename T,
44 template <typename...> typename Template,
45 template <typename...> typename... Templates>
47
48 template <typename T, template <typename...> typename... Templates>
50
51 template <typename T>
52 using is_validatable = std::is_constructible<bool, T>;
53
54 template <typename T, typename = void>
56
57 template <typename T>
60 std::is_pointer<decltype(std::declval<T&>().get())>,
61 std::is_same<decltype(*std::declval<T&>().get()), decltype(*std::declval<T&>())>,
63 >>>
64 : std::true_type
65 {};
66
67 // TODO: shouldn't we check is_smart_ptr && !is_copy_constructible && !is_copy_assignable
68 // to support users-specific ptrs?
69 template <typename T>
71
72 template <typename T>
75
76 template <typename T>
78 std::is_pointer<T>>;
79
80 template <typename T>
81 static auto pointerTo(T&& t) {
82 using Type = q20::remove_cvref_t<T>;
83 if constexpr (is_any_of<Type, std::optional>())
84 return t ? std::addressof(*std::forward<T>(t)) : nullptr;
85 else if constexpr (std::is_pointer<Type>())
86 return t;
87 else if constexpr (is_smart_ptr<Type>())
88 return t.get();
89 else if constexpr (is_any_of<Type, std::reference_wrapper>())
90 return std::addressof(t.get());
91 else
92 return std::addressof(std::forward<T>(t));
93 }
94
95 template <typename T>
96 using wrapped_t = std::remove_pointer_t<decltype(pointerTo(std::declval<T&>()))>;
97
98 template <typename T>
100
101 template <typename T>
102 static constexpr bool isValid(const T &t) noexcept
103 {
104 if constexpr (is_validatable<T>())
105 return bool(t);
106 else
107 return true;
108 }
109
110 template <typename T>
111 static decltype(auto) refTo(T&& t) {
112 Q_ASSERT(isValid(t));
113 // it's allowed to move only if the object holds unique ownership of the wrapped data
114 using Type = q20::remove_cvref_t<T>;
115 if constexpr (is_any_of<T, std::optional>())
116 return *std::forward<T>(t); // let std::optional resolve dereferencing
117 if constexpr (!is_wrapped<Type>() || is_any_unique_ptr<Type>())
118 return q23::forward_like<T>(*pointerTo(t));
119 else
120 return *pointerTo(t);
121 }
122
123 template <typename It>
124 auto key(It&& it) -> decltype(it.key()) { return it.key(); }
125 template <typename It>
126 auto key(It&& it) -> decltype((it->first)) { return it->first; }
127
128 template <typename It>
129 auto value(It&& it) -> decltype(it.value()) { return it.value(); }
130 template <typename It>
131 auto value(It&& it) -> decltype((it->second)) { return it->second; }
132
133 // use our own version of begin/end so that we can overload for pointers
134 template <typename C>
135 static auto begin(C &&c) -> decltype(std::begin(refTo(std::forward<C>(c))))
136 { return std::begin(refTo(std::forward<C>(c))); }
137 template <typename C>
138 static auto end(C &&c) -> decltype(std::end(refTo(std::forward<C>(c))))
139 { return std::end(refTo(std::forward<C>(c))); }
140 template <typename C>
141 static auto cbegin(C &&c) -> decltype(std::cbegin(refTo(std::forward<C>(c))))
142 { return std::cbegin(refTo(std::forward<C>(c))); }
143 template <typename C>
144 static auto cend(C &&c) -> decltype(std::cend(refTo(std::forward<C>(c))))
145 { return std::cend(refTo(std::forward<C>(c))); }
146 template <typename C>
147 static auto pos(C &&c, int i)
148 { return std::next(QGenericItemModelDetails::begin(std::forward<C>(c)), i); }
149 template <typename C>
150 static auto cpos(C &&c, int i)
151 { return std::next(QGenericItemModelDetails::cbegin(std::forward<C>(c)), i); }
152
153 // Test if a type is a range, and whether we can modify it using the
154 // standard C++ container member functions insert, erase, and resize.
155 // For the sake of QAIM, we cannot modify a range if it holds const data
156 // even if the range itself is not const; we'd need to initialize new rows
157 // and columns, and move old row and column data.
158 template <typename C, typename = void>
160
161 template <typename C>
162 struct test_insert<C, std::void_t<decltype(std::declval<C>().insert(
163 std::declval<typename C::const_iterator>(),
164 std::declval<typename C::size_type>(),
165 std::declval<typename C::value_type>()
166 ))>>
167 : std::true_type
168 {};
169
170 // Can we insert from another (identical) range? Required to support
171 // move-only types
172 template <typename C, typename = void>
174
175 template <typename C>
176 struct test_insert_range<C, std::void_t<decltype(std::declval<C&>().insert(
177 std::declval<typename C::const_iterator&>(),
178 std::declval<std::move_iterator<typename C::iterator>&>(),
179 std::declval<std::move_iterator<typename C::iterator>&>()
180 ))>>
181 : std::true_type
182 {};
183
184 template <typename C, typename = void>
186
187 template <typename C>
188 struct test_erase<C, std::void_t<decltype(std::declval<C>().erase(
189 std::declval<typename C::const_iterator>(),
190 std::declval<typename C::const_iterator>()
191 ))>>
192 : std::true_type
193 {};
194
195 template <typename C, typename = void>
197
198 template <typename C>
199 struct test_resize<C, std::void_t<decltype(std::declval<C>().resize(
200 std::declval<typename C::size_type>(),
201 std::declval<typename C::value_type>()
202 ))>>
203 : std::true_type
204 {};
205
206 // Test if a type is an associative container that we can use for multi-role
207 // data, i.e. has a key_type and a mapped_type typedef, and maps from int,
208 // Qt::ItemDataRole, or QString to QVariant. This excludes std::set (and
209 // unordered_set), which are not useful for us anyway even though they are
210 // considered associative containers.
211 template <typename C, typename = void> struct is_multi_role : std::false_type
212 {
213 static constexpr bool int_key = false;
214 };
215 template <typename C> // Qt::ItemDataRole -> QVariant, or QString -> QVariant, int -> QVariant
216 struct is_multi_role<C, std::void_t<typename C::key_type, typename C::mapped_type>>
217 : std::conjunction<std::disjunction<std::is_same<typename C::key_type, int>,
218 std::is_same<typename C::key_type, Qt::ItemDataRole>,
219 std::is_same<typename C::key_type, QString>>,
220 std::is_same<typename C::mapped_type, QVariant>>
221 {
222 static constexpr bool int_key = !std::is_same_v<typename C::key_type, QString>;
223 };
224 template <typename C>
225 [[maybe_unused]]
226 static constexpr bool is_multi_role_v = is_multi_role<C>::value;
227
228 template <typename C, typename = void>
230 template <typename C>
231 struct test_size<C, std::void_t<decltype(std::size(std::declval<C&>()))>> : std::true_type {};
232
233 template <typename C, typename = void>
235 static constexpr bool is_mutable = !std::is_const_v<C>;
236 static constexpr bool has_insert = false;
237 static constexpr bool has_insert_range = false;
238 static constexpr bool has_erase = false;
239 static constexpr bool has_resize = false;
240 };
241 template <typename C>
242 struct range_traits<C, std::void_t<decltype(cbegin(std::declval<C&>())),
243 decltype(cend(std::declval<C&>())),
245 >> : std::true_type
246 {
247 using iterator = decltype(begin(std::declval<C&>()));
249 static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
250 static constexpr bool has_insert = test_insert<C>();
251 static constexpr bool has_insert_range = test_insert_range<C>();
252 static constexpr bool has_erase = test_erase<C>();
253 static constexpr bool has_resize = test_resize<C>();
254 };
255
256 // Specializations for types that look like ranges, but should be
257 // treated as values.
258 enum class Mutable { Yes, No };
259 template <Mutable IsMutable>
261 static constexpr bool is_mutable = IsMutable == Mutable::Yes;
262 static constexpr bool has_insert = false;
263 static constexpr bool has_erase = false;
264 static constexpr bool has_resize = false;
265 };
267 template <> struct range_traits<QString> : iterable_value<Mutable::Yes> {};
268 template <class CharT, class Traits, class Allocator>
271
272 // const T * and views are read-only
273 template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
275
276 template <typename C>
277 [[maybe_unused]] static constexpr bool is_range_v = range_traits<C>();
278
279 // Find out how many fixed elements can be retrieved from a row element.
280 // main template for simple values and ranges. Specializing for ranges
281 // is ambiguous with arrays, as they are also ranges
282 template <typename T, typename = void>
283 struct row_traits {
284 static constexpr bool is_range = is_range_v<q20::remove_cvref_t<T>>;
285 // A static size of -1 indicates dynamically sized range
286 // A static size of 0 indicates that the specified type doesn't
287 // represent static or dynamic range.
288 static constexpr int static_size = is_range ? -1 : 0;
289 static constexpr int fixed_size() { return 1; }
290 };
291
292 // Specialization for tuples, using std::tuple_size
293 template <typename T>
295 static constexpr std::size_t size64 = std::tuple_size_v<T>;
296 static_assert(q20::in_range<int>(size64));
297 static constexpr int static_size = int(size64);
298 static constexpr int fixed_size() { return 0; }
299 };
300
301 // Specialization for C arrays
302 template <typename T, std::size_t N>
303 struct row_traits<T[N]>
304 {
305 static_assert(q20::in_range<int>(N));
306 static constexpr int static_size = int(N);
307 static constexpr int fixed_size() { return 0; }
308 };
309
310 // Specialization for gadgets
311 // clang doesn't accept multiple specializations using std::void_t directly
312 template <class... Ts> struct make_void { using type = void; };
313
314 template <typename T>
315 struct row_traits<T, typename make_void<decltype(T::staticMetaObject)>::type>
316 {
317 static constexpr int static_size = 0;
318 static int fixed_size() {
319 // Interpret a gadget in a list as a multi-column row item. To
320 // disambiguate, stick it into a SingleColumn wrapper.
321 static const int columnCount = []{
322 const QMetaObject &mo = T::staticMetaObject;
323 return mo.propertyCount() - mo.propertyOffset();
324 }();
325 return columnCount;
326 }
327 };
328
329 template <typename T>
330 [[maybe_unused]] static constexpr int static_size_v =
332
333 // we can't add this as a member to row_traits, as we'd end up with
334 // ambiguous specializations for gadgets implementing tuple protocol.
335 template <typename T, typename = void>
337 template <typename T>
339 : std::true_type {};
340
341 template <typename T>
342 [[maybe_unused]] static constexpr bool has_metaobject_v = has_metaobject<T>::value;
343
344 template <typename Range>
346 {
348
349 template <typename R = row_type>
350 auto newRow() -> decltype(R{}) { return R{}; }
351 };
352
353 template <typename Range>
355 {
357
358 template <typename R = row_type,
360 is_owning_or_raw_pointer<R>>, bool> = true>
361 auto newRow() -> decltype(R(new wrapped_t<R>)) {
362 if constexpr (is_any_of<R, std::shared_ptr>())
363 return std::make_shared<wrapped_t<R>>();
364 else
365 return R(new wrapped_t<R>);
366 }
367
368 template <typename R = row_type,
370 auto newRow() -> decltype(R{}) { return R{}; }
371
372 template <typename R = row_type,
374 auto deleteRow(R&& row) -> decltype(delete row) { delete row; }
375 };
376
377 template <typename Range, typename R = typename range_traits<wrapped_t<Range>>::value_type>
380
381 // Default tree traversal protocol implementation for row types that have
382 // the respective member functions. The trailing return type implicitly
383 // removes those functions that are not available.
384 template <typename Range>
386 {
387 template <typename R /*wrapped_row_type*/>
388 auto parentRow(const R& row) const -> decltype(row.parentRow())
389 {
390 return row.parentRow();
391 }
392
393 template <typename R /* = wrapped_row_type*/>
394 auto setParentRow(R &row, R* parent) -> decltype(row.setParentRow(parent))
395 {
396 row.setParentRow(parent);
397 }
398
399 template <typename R /* = wrapped_row_type*/>
400 auto childRows(const R &row) const -> decltype(row.childRows())
401 {
402 return row.childRows();
403 }
404
405 template <typename R /* = wrapped_row_type*/>
406 auto childRows(R &row) -> decltype(row.childRows())
407 {
408 return row.childRows();
409 }
410 };
411
412 template <typename P, typename R, typename = void>
414 template <typename P, typename R>
416 std::void_t<decltype(std::declval<P&>().parentRow(std::declval<wrapped_t<R>&>()))>>
417 : std::true_type {};
418
419 template <typename P, typename R, typename = void>
421 template <typename P, typename R>
423 std::void_t<decltype(std::declval<P&>().childRows(std::declval<wrapped_t<R>&>()))>>
424 : std::true_type {};
425
426 template <typename P, typename R, typename = void>
428 template <typename P, typename R>
430 std::void_t<decltype(std::declval<P&>().setParentRow(std::declval<wrapped_t<R>&>(),
431 std::declval<wrapped_t<R>*>()))>>
432 : std::true_type {};
433
434 template <typename P, typename R, typename = void>
436 template <typename P, typename R>
438 std::void_t<decltype(refTo(std::declval<P&>().childRows(std::declval<wrapped_t<R>&>()))
439 = {}) >>
440 : std::true_type {};
441
442 template <typename P, typename = void>
444 template <typename P>
445 struct protocol_newRow<P, std::void_t<decltype(std::declval<P&>().newRow())>>
446 : std::true_type {};
447
448 template <typename P, typename R, typename = void>
450 template <typename P, typename R>
452 std::void_t<decltype(std::declval<P&>().deleteRow(std::declval<R&&>()))>>
453 : std::true_type {};
454
455 template <typename Range,
456 typename Protocol = DefaultTreeProtocol<Range>,
457 typename R = typename range_traits<Range>::value_type,
458 typename = void>
460
461 template <typename Range, typename Protocol, typename R>
466
467 template <typename Range>
470 bool>;
471
472 template <typename Range, typename Protocol = DefaultTreeProtocol<Range>>
475 bool>;
476
477 template <typename Range, typename Protocol>
490
491 // The storage of the model data. We might store it as a pointer, or as a
492 // (copied- or moved-into) value (or smart pointer). But we always return a
493 // raw pointer.
494 template <typename ModelStorage>
496 {
497 auto model() { return pointerTo(m_model); }
498 auto model() const { return pointerTo(m_model); }
499
500 ModelStorage m_model;
501 };
502}
503
504class QGenericItemModel;
505
507{
509protected:
510 // Helpers for calling a lambda with the tuple element at a runtime index.
511 template <typename Tuple, typename F, size_t ...Is>
513 {
515 ((Is == idx ? static_cast<void>(function(get<Is>(
517 : static_cast<void>(0)), ...);
518 }
519
520 template <typename T, typename F>
522 {
524 constexpr size_t size = std::tuple_size_v<type>;
525 Q_ASSERT(idx < size);
527 std::forward<F>(function));
528 }
529
530 // Get the QMetaType for a tuple-element at a runtime index.
531 // Used in the headerData implementation.
532 template <typename Tuple, std::size_t ...I>
533 static constexpr std::array<QMetaType, sizeof...(I)> makeMetaTypes(std::index_sequence<I...>)
534 {
536 }
537 template <typename T>
539 {
541 constexpr auto size = std::tuple_size_v<type>;
542 Q_ASSERT(idx < size);
544 }
545
546 // Helpers to call a given member function with the correct arguments.
547 template <typename Class, typename T, typename F, size_t...I>
548 static auto apply(std::integer_sequence<size_t, I...>, Class* obj, F&& fn, T&& tuple)
549 {
550 return std::invoke(fn, obj, std::get<I>(tuple)...);
551 }
552 template <typename Ret, typename Class, typename ...Args>
554 void *ret, const void *args)
555 {
556 const auto &tuple = *static_cast<const std::tuple<Args&...> *>(args);
557 *static_cast<Ret *>(ret) = apply(std::make_index_sequence<sizeof...(Args)>{},
558 static_cast<Class *>(obj), fn, tuple);
559 }
560 template <typename Ret, typename Class, typename ...Args>
561 static void makeCall(const QGenericItemModelImplBase *obj, Ret(Class::* &&fn)(Args...) const,
562 void *ret, const void *args)
563 {
564 const auto &tuple = *static_cast<const std::tuple<Args&...> *>(args);
565 *static_cast<Ret *>(ret) = apply(std::make_index_sequence<sizeof...(Args)>{},
566 static_cast<const Class *>(obj), fn, tuple);
567 }
568
569public:
581
594
595 void destroy()
596 {
597 call<bool>(Destroy);
598 }
599
600private:
601 // prototypes
602 static void callConst(ConstOp, const QGenericItemModelImplBase *, void *, const void *);
603 static void call(Op, QGenericItemModelImplBase *, void *, const void *);
604
605 using CallConstFN = decltype(callConst);
606 using CallTupleFN = decltype(call);
607
608 CallConstFN *callConst_fn;
609 CallTupleFN *call_fn;
610 QGenericItemModel *m_itemModel;
611
612protected:
613 template <typename Impl> // type deduction
614 explicit QGenericItemModelImplBase(QGenericItemModel *itemModel, const Impl *)
615 : callConst_fn(&Impl::callConst), call_fn(&Impl::call), m_itemModel(itemModel)
616 {}
618
619 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
620 inline void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to);
621 inline QHash<int, QByteArray> roleNames() const;
622 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
623 const QList<int> &roles);
624 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
625 inline void endInsertColumns();
626 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
627 inline void endRemoveColumns();
628 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
629 const QModelIndex &destParent, int destRow);
630 inline void endMoveColumns();
631 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
632 inline void endInsertRows();
633 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
634 inline void endRemoveRows();
635 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
636 const QModelIndex &destParent, int destRow);
637 inline void endMoveRows();
639 inline const QAbstractItemModel &itemModel() const;
640
641public:
642 template <typename Ret, typename ...Args>
643 Ret callConst(ConstOp op, const Args &...args) const
644 {
645 Ret ret = {};
646 const auto tuple = std::tie(args...);
647 callConst_fn(op, this, &ret, &tuple);
648 return ret;
649 }
650
651 template <typename Ret, typename ...Args>
652 Ret call(Op op, const Args &...args)
653 {
654 Ret ret = {};
655 const auto tuple = std::tie(args...);
656 call_fn(op, this, &ret, &tuple);
657 return ret;
658 }
659};
660
661template <typename Structure, typename Range,
662 typename Protocol = QGenericItemModelDetails::table_protocol_t<Range>>
664{
666public:
672
675 "Currently, std::optional is not supported for ranges and rows, as "
676 "it has range semantics in c++26. Once the required behavior is clarified, "
677 "std::optional for ranges and rows will be supported.");
678
679protected:
680 using Self = QGenericItemModelImpl<Structure, Range, Protocol>;
681 Structure& that() { return static_cast<Structure &>(*this); }
682 const Structure& that() const { return static_cast<const Structure &>(*this); }
683
684 template <typename C>
685 static constexpr int size(const C &c)
686 {
687 if (!QGenericItemModelDetails::isValid(c))
688 return 0;
689
690 if constexpr (QGenericItemModelDetails::test_size<C>()) {
691 return int(std::size(c));
692 } else {
693#if defined(__cpp_lib_ranges)
694 return int(std::ranges::distance(QGenericItemModelDetails::begin(c),
695 QGenericItemModelDetails::end(c)));
696#else
697 return int(std::distance(QGenericItemModelDetails::begin(c),
698 QGenericItemModelDetails::end(c)));
699#endif
700 }
701 }
702
708
709 static constexpr bool isMutable()
710 {
711 return range_features::is_mutable && row_features::is_mutable
712 && std::is_reference_v<row_reference>
713 && Structure::is_mutable_impl;
714 }
715
718 static constexpr bool rows_are_owning_or_raw_pointers =
721 static constexpr bool one_dimensional_range = static_column_count == 0;
722
723 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
724 static constexpr bool dynamicColumns() { return static_column_count < 0; }
725
726 // A row might be a value (or range of values), or a pointer.
727 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
730
731 template <typename T>
734
738 >;
739
740 // A iterator type to use as the input iterator with the
741 // range_type::insert(pos, start, end) overload if available (it is in
742 // std::vector, but not in QList). Generates a prvalue when dereferenced,
743 // which then gets moved into the newly constructed row, which allows us to
744 // implement insertRows() for move-only row types.
746 {
750 using iterator_category = std::input_iterator_tag;
751 using difference_type = int;
752
753 value_type operator*() { return impl->makeEmptyRow(*parent); }
754 EmptyRowGenerator &operator++() { ++n; return *this; }
755 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
756 { return lhs.n == rhs.n; }
757 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
758 { return !(lhs == rhs); }
759
760 difference_type n = 0;
761 Structure *impl = nullptr;
762 const QModelIndex* parent = nullptr;
763 };
764
765 // If we have a move-only row_type and can add/remove rows, then the range
766 // must have an insert-from-range overload.
769 "The range holding a move-only row-type must support insert(pos, start, end)");
770
771public:
772 explicit QGenericItemModelImpl(Range &&model, Protocol&& protocol, QGenericItemModel *itemModel)
773 : QGenericItemModelImplBase(itemModel, static_cast<const Self*>(nullptr))
775 , m_protocol(std::forward<Protocol>(protocol))
776 {
777 }
778
779 // static interface, called by QGenericItemModelImplBase
780 static void callConst(ConstOp op, const QGenericItemModelImplBase *that, void *r, const void *args)
781 {
782 switch (op) {
783 case Index: makeCall(that, &Self::index, r, args);
784 break;
785 case Parent: makeCall(that, &Structure::parent, r, args);
786 break;
787 case Sibling: makeCall(that, &Self::sibling, r, args);
788 break;
789 case RowCount: makeCall(that, &Structure::rowCount, r, args);
790 break;
791 case ColumnCount: makeCall(that, &Structure::columnCount, r, args);
792 break;
793 case Flags: makeCall(that, &Self::flags, r, args);
794 break;
795 case HeaderData: makeCall(that, &Self::headerData, r, args);
796 break;
797 case Data: makeCall(that, &Self::data, r, args);
798 break;
799 case ItemData: makeCall(that, &Self::itemData, r, args);
800 break;
801 }
802 }
803
804 static void call(Op op, QGenericItemModelImplBase *that, void *r, const void *args)
805 {
806 switch (op) {
807 case Destroy: delete static_cast<Structure *>(that);
808 break;
809 case SetData: makeCall(that, &Self::setData, r, args);
810 break;
811 case SetItemData: makeCall(that, &Self::setItemData, r, args);
812 break;
813 case ClearItemData: makeCall(that, &Self::clearItemData, r, args);
814 break;
815 case InsertColumns: makeCall(that, &Self::insertColumns, r, args);
816 break;
817 case RemoveColumns: makeCall(that, &Self::removeColumns, r, args);
818 break;
819 case MoveColumns: makeCall(that, &Self::moveColumns, r, args);
820 break;
821 case InsertRows: makeCall(that, &Self::insertRows, r, args);
822 break;
823 case RemoveRows: makeCall(that, &Self::removeRows, r, args);
824 break;
825 case MoveRows: makeCall(that, &Self::moveRows, r, args);
826 break;
827 }
828 }
829
830 // actual implementations
831 QModelIndex index(int row, int column, const QModelIndex &parent) const
832 {
833 if (row < 0 || column < 0 || column >= that().columnCount(parent)
834 || row >= that().rowCount(parent)) {
835 return {};
836 }
837
838 return that().indexImpl(row, column, parent);
839 }
840
841 QModelIndex sibling(int row, int column, const QModelIndex &index) const
842 {
843 if (row == index.row() && column == index.column())
844 return index;
845
846 if (column < 0 || column >= itemModel().columnCount())
847 return {};
848
849 if (row == index.row())
850 return createIndex(row, column, index.constInternalPointer());
851
852 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
853 const auto siblingCount = size(that().childrenOf(parentRow));
854 if (row < 0 || row >= int(siblingCount))
855 return {};
856 return createIndex(row, column, parentRow);
857 }
858
859 Qt::ItemFlags flags(const QModelIndex &index) const
860 {
861 if (!index.isValid())
862 return Qt::NoItemFlags;
863
864 Qt::ItemFlags f = Structure::defaultFlags();
865
866 if constexpr (has_metaobject<wrapped_row_type>) {
867 if (index.column() < row_traits::fixed_size()) {
868 const QMetaObject mo = wrapped_row_type::staticMetaObject;
869 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
870 if (prop.isWritable())
871 f |= Qt::ItemIsEditable;
872 }
873 } else if constexpr (static_column_count <= 0) {
874 if constexpr (isMutable())
875 f |= Qt::ItemIsEditable;
876 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
877 // we want to know if the elements in the tuple are const; they'd always be, if
878 // we didn't remove the const of the range first.
879 const_row_reference row = rowData(index);
880 row_reference mutableRow = const_cast<row_reference>(row);
881 if (QGenericItemModelDetails::isValid(mutableRow)) {
882 for_element_at(mutableRow, index.column(), [&f](auto &&ref){
883 using target_type = decltype(ref);
884 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
885 f &= ~Qt::ItemIsEditable;
886 else if constexpr (std::is_lvalue_reference_v<target_type>)
887 f |= Qt::ItemIsEditable;
888 });
889 } else {
890 // If there's no usable value stored in the row, then we can't
891 // do anything with this item.
892 f &= ~Qt::ItemIsEditable;
893 }
894 }
895 return f;
896 }
897
898 QVariant headerData(int section, Qt::Orientation orientation, int role) const
899 {
900 QVariant result;
901 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
902 || section < 0 || section >= that().columnCount({})) {
903 return itemModel().QAbstractItemModel::headerData(section, orientation, role);
904 }
905
906 if constexpr (has_metaobject<wrapped_row_type>) {
907 if (row_traits::fixed_size() == 1) {
908 const QMetaType metaType = QMetaType::fromType<wrapped_row_type>();
909 result = QString::fromUtf8(metaType.name());
910 } else if (section <= row_traits::fixed_size()) {
911 const QMetaProperty prop = wrapped_row_type::staticMetaObject.property(
912 section + wrapped_row_type::staticMetaObject.propertyOffset());
913 result = QString::fromUtf8(prop.name());
914 }
915 } else if constexpr (static_column_count >= 1) {
916 const QMetaType metaType = meta_type_at<row_type>(section);
917 if (metaType.isValid())
918 result = QString::fromUtf8(metaType.name());
919 }
920 if (!result.isValid())
921 result = itemModel().QAbstractItemModel::headerData(section, orientation, role);
922 return result;
923 }
924
925 QVariant data(const QModelIndex &index, int role) const
926 {
927 QVariant result;
928 const auto readData = [this, column = index.column(), &result, role](const auto &value) {
929 Q_UNUSED(this);
930 using value_type = q20::remove_cvref_t<decltype(value)>;
931 using multi_role = QGenericItemModelDetails::is_multi_role<value_type>;
932 if constexpr (has_metaobject<value_type>) {
933 if (row_traits::fixed_size() <= 1) {
934 result = readRole(role, QGenericItemModelDetails::pointerTo(value));
935 } else if (column <= row_traits::fixed_size()
936 && (role == Qt::DisplayRole || role == Qt::EditRole)) {
937 result = readProperty(column, QGenericItemModelDetails::pointerTo(value));
938 }
939 } else if constexpr (multi_role::value) {
940 const auto it = [this, &value, role]{
941 Q_UNUSED(this);
942 if constexpr (multi_role::int_key)
943 return std::as_const(value).find(Qt::ItemDataRole(role));
944 else
945 return std::as_const(value).find(roleNames().value(role));
946 }();
947 if (it != value.cend()) {
948 result = QGenericItemModelDetails::value(it);
949 }
950 } else if (role == Qt::DisplayRole || role == Qt::EditRole) {
951 result = read(value);
952 }
953 };
954
955 if (index.isValid())
956 readAt(index, readData);
957
958 return result;
959 }
960
961 QMap<int, QVariant> itemData(const QModelIndex &index) const
962 {
963 QMap<int, QVariant> result;
964 bool tried = false;
965 const auto readItemData = [this, &result, &tried](auto &&value){
966 Q_UNUSED(this);
967 using value_type = q20::remove_cvref_t<decltype(value)>;
968 using multi_role = QGenericItemModelDetails::is_multi_role<value_type>;
969 if constexpr (multi_role()) {
970 tried = true;
971 if constexpr (std::is_convertible_v<value_type, decltype(result)>) {
972 result = value;
973 } else {
974 for (auto it = std::cbegin(value); it != std::cend(value); ++it) {
975 int role = [this, key = QGenericItemModelDetails::key(it)]() {
976 Q_UNUSED(this);
977 if constexpr (multi_role::int_key)
978 return int(key);
979 else
980 return roleNames().key(key.toUtf8(), -1);
981 }();
982
983 if (role != -1)
984 result.insert(role, QGenericItemModelDetails::value(it));
985 }
986 }
987 } else if constexpr (has_metaobject<value_type>) {
988 if (row_traits::fixed_size() <= 1) {
989 tried = true;
990 using meta_type = QGenericItemModelDetails::wrapped_t<value_type>;
991 const QMetaObject &mo = meta_type::staticMetaObject;
992 for (auto &&[role, roleName] : roleNames().asKeyValueRange()) {
993 QVariant data;
994 if constexpr (std::is_base_of_v<QObject, meta_type>) {
995 if (value)
996 data = value->property(roleName);
997 } else {
998 const int pi = mo.indexOfProperty(roleName.constData());
999 if (pi >= 0) {
1000 const QMetaProperty prop = mo.property(pi);
1001 if (prop.isValid())
1002 data = prop.readOnGadget(QGenericItemModelDetails::pointerTo(value));
1003 }
1004 }
1005 if (data.isValid())
1006 result[role] = std::move(data);
1007 }
1008 }
1009 }
1010 };
1011
1012 if (index.isValid()) {
1013 readAt(index, readItemData);
1014
1015 if (!tried) // no multi-role item found
1016 result = itemModel().QAbstractItemModel::itemData(index);
1017 }
1018 return result;
1019 }
1020
1021 bool setData(const QModelIndex &index, const QVariant &data, int role)
1022 {
1023 if (!index.isValid())
1024 return false;
1025
1026 bool success = false;
1027 if constexpr (isMutable()) {
1028 auto emitDataChanged = qScopeGuard([&success, this, &index, &role]{
1029 if (success) {
1030 Q_EMIT dataChanged(index, index, role == Qt::EditRole
1031 ? QList<int>{} : QList{role});
1032 }
1033 });
1034
1035 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1036 using value_type = q20::remove_cvref_t<decltype(target)>;
1037 using multi_role = QGenericItemModelDetails::is_multi_role<value_type>;
1038 if constexpr (has_metaobject<value_type>) {
1039 if (QMetaType::fromType<value_type>() == data.metaType()) {
1040 if constexpr (std::is_copy_assignable_v<value_type>) {
1041 target = data.value<value_type>();
1042 return true;
1043 } else {
1044 qCritical("Cannot assign %s", QMetaType::fromType<value_type>().name());
1045 return false;
1046 }
1047 } else if (row_traits::fixed_size() <= 1) {
1048 return writeRole(role, QGenericItemModelDetails::pointerTo(target), data);
1049 } else if (column <= row_traits::fixed_size()
1050 && (role == Qt::DisplayRole || role == Qt::EditRole)) {
1051 return writeProperty(column, QGenericItemModelDetails::pointerTo(target), data);
1052 }
1053 } else if constexpr (multi_role::value) {
1054 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1055 // If there is an entry for EditRole, overwrite that; otherwise,
1056 // set the entry for DisplayRole.
1057 if (role == Qt::EditRole) {
1058 if constexpr (multi_role::int_key) {
1059 if (target.find(roleToSet) == target.end())
1060 roleToSet = Qt::DisplayRole;
1061 } else {
1062 if (target.find(roleNames().value(roleToSet)) == target.end())
1063 roleToSet = Qt::DisplayRole;
1064 }
1065 }
1066 if constexpr (multi_role::int_key)
1067 return write(target[roleToSet], data);
1068 else
1069 return write(target[roleNames().value(roleToSet)], data);
1070 } else if (role == Qt::DisplayRole || role == Qt::EditRole) {
1071 return write(target, data);
1072 }
1073 return false;
1074 };
1075
1076 success = writeAt(index, writeData);
1077 }
1078 return success;
1079 }
1080
1081 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1082 {
1083 if (!index.isValid() || data.isEmpty())
1084 return false;
1085
1086 bool success = false;
1087 if constexpr (isMutable()) {
1088 auto emitDataChanged = qScopeGuard([&success, this, &index, &data]{
1089 if (success)
1090 Q_EMIT dataChanged(index, index, data.keys());
1091 });
1092
1093 bool tried = false;
1094 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
1095 Q_UNUSED(this);
1096 using value_type = q20::remove_cvref_t<decltype(target)>;
1097 using multi_role = QGenericItemModelDetails::is_multi_role<value_type>;
1098 if constexpr (multi_role()) {
1099 using key_type = typename value_type::key_type;
1100 tried = true;
1101 const auto roleName = [map = roleNames()](int role) { return map.value(role); };
1102
1103 // transactional: only update target if all values from data
1104 // can be stored. Storing never fails with int-keys.
1105 if constexpr (!multi_role::int_key)
1106 {
1107 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
1108 [&roleName](int role) { return roleName(role).isEmpty(); }
1109 );
1110
1111 if (invalid != data.keyEnd()) {
1112 qWarning("No role name set for %d", *invalid);
1113 return false;
1114 }
1115 }
1116
1117 for (auto &&[role, value] : data.asKeyValueRange()) {
1118 if constexpr (multi_role::int_key)
1119 target[static_cast<key_type>(role)] = value;
1120 else
1121 target[QString::fromUtf8(roleName(role))] = value;
1122 }
1123 return true;
1124 } else if constexpr (has_metaobject<value_type>) {
1125 if (row_traits::fixed_size() <= 1) {
1126 tried = true;
1127 using meta_type = QGenericItemModelDetails::wrapped_t<value_type>;
1128 const QMetaObject &mo = meta_type::staticMetaObject;
1129 // transactional: if possible, modify a copy and only
1130 // update target if all values from data could be stored.
1131 auto targetCopy = [](auto &&origin) {
1132 if constexpr (std::is_base_of_v<QObject, meta_type>)
1133 return origin; // can't copy, no transaction support
1134 else if constexpr (std::is_pointer_v<decltype(target)>)
1135 return *origin;
1136 else if constexpr (std::is_copy_assignable_v<value_type>)
1137 return origin;
1138 else // can't copy - targetCopy is now a pointer
1139 return &origin;
1140 }(target);
1141 for (auto &&[role, value] : data.asKeyValueRange()) {
1142 const QByteArray roleName = roleNames().value(role);
1143 bool written = false;
1144 if constexpr (std::is_base_of_v<QObject, meta_type>) {
1145 if (targetCopy)
1146 written = targetCopy->setProperty(roleName, value);
1147 } else {
1148 const int pi = mo.indexOfProperty(roleName.constData());
1149 if (pi >= 0) {
1150 const QMetaProperty prop = mo.property(pi);
1151 if (prop.isValid())
1152 written = prop.writeOnGadget(QGenericItemModelDetails::pointerTo(targetCopy), value);
1153 }
1154 }
1155 if (!written) {
1156 qWarning("Failed to write value for %s", roleName.data());
1157 return false;
1158 }
1159 }
1160 if constexpr (std::is_base_of_v<QObject, meta_type>)
1161 target = targetCopy; // nothing actually copied
1162 else if constexpr (std::is_pointer_v<decltype(target)>)
1163 qSwap(*target, targetCopy);
1164 else if constexpr (std::is_pointer_v<decltype(targetCopy)>)
1165 ; // couldn't copy
1166 else
1167 qSwap(target, targetCopy);
1168 return true;
1169 }
1170 }
1171 return false;
1172 };
1173
1174 success = writeAt(index, writeItemData);
1175
1176 if (!tried) {
1177 // setItemData will emit the dataChanged signal
1178 Q_ASSERT(!success);
1179 emitDataChanged.dismiss();
1180 success = itemModel().QAbstractItemModel::setItemData(index, data);
1181 }
1182 }
1183 return success;
1184 }
1185
1186 bool clearItemData(const QModelIndex &index)
1187 {
1188 if (!index.isValid())
1189 return false;
1190
1191 bool success = false;
1192 if constexpr (isMutable()) {
1193 auto emitDataChanged = qScopeGuard([&success, this, &index]{
1194 if (success)
1195 Q_EMIT dataChanged(index, index, {});
1196 });
1197
1198 auto clearData = [column = index.column()](auto &&target) {
1199 if constexpr (has_metaobject<row_type>) {
1200 if (row_traits::fixed_size() <= 1) {
1201 // multi-role object/gadget: reset all properties
1202 return resetProperty(-1, QGenericItemModelDetails::pointerTo(target));
1203 } else if (column <= row_traits::fixed_size()) {
1204 return resetProperty(column, QGenericItemModelDetails::pointerTo(target));
1205 }
1206 } else { // normal structs, values, associative containers
1207 target = {};
1208 return true;
1209 }
1210 return false;
1211 };
1212
1213 success = writeAt(index, clearData);
1214 }
1215 return success;
1216 }
1217
1218 bool insertColumns(int column, int count, const QModelIndex &parent)
1219 {
1220 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
1221 if (count == 0)
1222 return false;
1223 range_type * const children = childRange(parent);
1224 if (!children)
1225 return false;
1226
1227 beginInsertColumns(parent, column, column + count - 1);
1228 for (auto &child : *children) {
1229 auto it = QGenericItemModelDetails::pos(child, column);
1230 QGenericItemModelDetails::refTo(child).insert(it, count, {});
1231 }
1233 return true;
1234 }
1235 return false;
1236 }
1237
1238 bool removeColumns(int column, int count, const QModelIndex &parent)
1239 {
1240 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
1241 if (column < 0 || column + count > that().columnCount(parent))
1242 return false;
1243
1244 range_type * const children = childRange(parent);
1245 if (!children)
1246 return false;
1247
1248 beginRemoveColumns(parent, column, column + count - 1);
1249 for (auto &child : *children) {
1250 const auto start = QGenericItemModelDetails::pos(child, column);
1251 QGenericItemModelDetails::refTo(child).erase(start, std::next(start, count));
1252 }
1254 return true;
1255 }
1256 return false;
1257 }
1258
1259 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1260 const QModelIndex &destParent, int destColumn)
1261 {
1262 // we only support moving columns within the same parent
1263 if (sourceParent != destParent)
1264 return false;
1265 if constexpr (isMutable()) {
1266 if (!Structure::canMoveColumns(sourceParent, destParent))
1267 return false;
1268
1269 if constexpr (dynamicColumns()) {
1270 // we only support ranges as columns, as other types might
1271 // not have the same data type across all columns
1272 range_type * const children = childRange(sourceParent);
1273 if (!children)
1274 return false;
1275
1276 if (!beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
1277 destParent, destColumn)) {
1278 return false;
1279 }
1280
1281 for (auto &child : *children) {
1282 const auto first = QGenericItemModelDetails::pos(child, sourceColumn);
1283 const auto middle = std::next(first, count);
1284 const auto last = QGenericItemModelDetails::pos(child, destColumn);
1285
1286 if (sourceColumn < destColumn) // moving right
1287 std::rotate(first, middle, last);
1288 else // moving left
1289 std::rotate(last, first, middle);
1290 }
1291
1293 return true;
1294 }
1295 }
1296 return false;
1297 }
1298
1299 bool insertRows(int row, int count, const QModelIndex &parent)
1300 {
1301 if constexpr (canInsertRows()) {
1302 range_type *children = childRange(parent);
1303 if (!children)
1304 return false;
1305
1306 EmptyRowGenerator generator{0, &that(), &parent};
1307
1308 beginInsertRows(parent, row, row + count - 1);
1309
1310 const auto pos = QGenericItemModelDetails::pos(children, row);
1311 if constexpr (range_features::has_insert_range) {
1312 children->insert(pos, generator, EmptyRowGenerator{count});
1313 } else if constexpr (rows_are_owning_or_raw_pointers) {
1314 auto start = children->insert(pos, count, row_type{});
1315 std::copy(generator, EmptyRowGenerator{count}, start);
1316 } else {
1317 children->insert(pos, count, *generator);
1318 }
1319
1320 // fix the parent in all children of the modified row, as the
1321 // references back to the parent might have become invalid.
1322 that().resetParentInChildren(children);
1323
1325 return true;
1326 } else {
1327 return false;
1328 }
1329 }
1330
1331 bool removeRows(int row, int count, const QModelIndex &parent = {})
1332 {
1333 if constexpr (Structure::canRemoveRows()) {
1334 const int prevRowCount = that().rowCount(parent);
1335 if (row < 0 || row + count > prevRowCount)
1336 return false;
1337
1338 range_type *children = childRange(parent);
1339 if (!children)
1340 return false;
1341
1342 beginRemoveRows(parent, row, row + count - 1);
1343 [[maybe_unused]] bool callEndRemoveColumns = false;
1344 if constexpr (dynamicColumns()) {
1345 // if we remove the last row in a dynamic model, then we no longer
1346 // know how many columns we should have, so they will be reported as 0.
1347 if (prevRowCount == count) {
1348 if (const int columns = that().columnCount(parent)) {
1349 callEndRemoveColumns = true;
1350 beginRemoveColumns(parent, 0, columns - 1);
1351 }
1352 }
1353 }
1354 { // erase invalidates iterators
1355 const auto begin = QGenericItemModelDetails::pos(children, row);
1356 const auto end = std::next(begin, count);
1357 that().deleteRemovedRows(begin, end);
1358 children->erase(begin, end);
1359 }
1360 // fix the parent in all children of the modified row, as the
1361 // references back to the parent might have become invalid.
1362 that().resetParentInChildren(children);
1363
1364 if constexpr (dynamicColumns()) {
1365 if (callEndRemoveColumns) {
1366 Q_ASSERT(that().columnCount(parent) == 0);
1368 }
1369 }
1371 return true;
1372 } else {
1373 return false;
1374 }
1375 }
1376
1377 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
1378 const QModelIndex &destParent, int destRow)
1379 {
1380 if constexpr (isMutable()) {
1381 if (!Structure::canMoveRows(sourceParent, destParent))
1382 return false;
1383
1384 if (sourceParent != destParent) {
1385 return that().moveRowsAcross(sourceParent, sourceRow, count,
1386 destParent, destRow);
1387 }
1388
1389 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
1390 || sourceRow < 0 || sourceRow + count - 1 >= itemModel().rowCount(sourceParent)
1391 || destRow < 0 || destRow > itemModel().rowCount(destParent)) {
1392 return false;
1393 }
1394
1395 range_type *source = childRange(sourceParent);
1396 // moving within the same range
1397 if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
1398 return false;
1399
1400 const auto first = QGenericItemModelDetails::pos(source, sourceRow);
1401 const auto middle = std::next(first, count);
1402 const auto last = QGenericItemModelDetails::pos(source, destRow);
1403
1404 if (sourceRow < destRow) // moving down
1405 std::rotate(first, middle, last);
1406 else // moving up
1407 std::rotate(last, first, middle);
1408
1409 that().resetParentInChildren(source);
1410
1412 return true;
1413 } else {
1414 return false;
1415 }
1416 }
1417
1418protected:
1420 {
1421 // We delete row objects if we are not operating on a reference or pointer
1422 // to a range, as in that case, the owner of the referenced/pointed to
1423 // range also owns the row entries.
1424 // ### Problem: if we get a copy of a range (no matter if shared or not),
1425 // then adding rows will create row objects in the model's copy, and the
1426 // client can never delete those. But copied rows will be the same pointer,
1427 // which we must not delete (as we didn't create them).
1428 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
1429 && !QGenericItemModelDetails::is_any_of<Range, std::reference_wrapper>()) {
1430 const auto begin = QGenericItemModelDetails::begin(*m_data.model());
1431 const auto end = QGenericItemModelDetails::end(*m_data.model());
1432 that().deleteRemovedRows(begin, end);
1433 }
1434 }
1435
1436 static constexpr bool canInsertRows()
1437 {
1438 if constexpr (dynamicColumns() && !row_features::has_resize) {
1439 // If we operate on dynamic columns and cannot resize a newly
1440 // constructed row, then we cannot insert.
1441 return false;
1442 } else if constexpr (!protocol_traits::has_newRow) {
1443 // We also cannot insert if we cannot create a new row element
1444 return false;
1445 } else if constexpr (!range_features::has_insert_range
1446 && !std::is_copy_constructible_v<row_type>) {
1447 // And if the row is a move-only type, then the range needs to be
1448 // backed by a container that can move-insert default-constructed
1449 // row elements.
1450 return false;
1451 } else {
1452 return Structure::canInsertRows();
1453 }
1454 }
1455
1456 template <typename F>
1457 bool writeAt(const QModelIndex &index, F&& writer)
1458 {
1459 bool result = false;
1460 row_reference row = rowData(index);
1461
1462 if constexpr (one_dimensional_range) {
1463 result = writer(row);
1464 } else if (QGenericItemModelDetails::isValid(row)) {
1465 if constexpr (dynamicColumns()) {
1466 result = writer(*QGenericItemModelDetails::pos(row, index.column()));
1467 } else {
1468 for_element_at(row, index.column(), [&writer, &result](auto &&target) {
1469 using target_type = decltype(target);
1470 // we can only assign to an lvalue reference
1471 if constexpr (std::is_lvalue_reference_v<target_type>
1472 && !std::is_const_v<std::remove_reference_t<target_type>>) {
1473 result = writer(std::forward<target_type>(target));
1474 }
1475 });
1476 }
1477 }
1478
1479 return result;
1480 }
1481
1482 template <typename F>
1483 void readAt(const QModelIndex &index, F&& reader) const {
1484 const_row_reference row = rowData(index);
1485 if constexpr (one_dimensional_range) {
1486 return reader(row);
1487 } else if (QGenericItemModelDetails::isValid(row)) {
1488 if constexpr (dynamicColumns())
1489 reader(*QGenericItemModelDetails::cpos(row, index.column()));
1490 else
1491 for_element_at(row, index.column(), std::forward<F>(reader));
1492 }
1493 }
1494
1495 template <typename Value>
1496 static QVariant read(const Value &value)
1497 {
1498 if constexpr (std::is_constructible_v<QVariant, Value>)
1499 return QVariant(value);
1500 else
1501 return QVariant::fromValue(value);
1502 }
1503 template <typename Value>
1504 static QVariant read(Value *value)
1505 {
1506 if (value) {
1507 if constexpr (std::is_constructible_v<QVariant, Value *>)
1508 return QVariant(value);
1509 else
1510 return read(*value);
1511 }
1512 return {};
1513 }
1514
1515 template <typename Target>
1516 static bool write(Target &target, const QVariant &value)
1517 {
1518 using Type = std::remove_reference_t<Target>;
1519 if constexpr (std::is_constructible_v<Target, QVariant>) {
1520 target = value;
1521 return true;
1522 } else if (value.canConvert<Type>()) {
1523 target = value.value<Type>();
1524 return true;
1525 }
1526 return false;
1527 }
1528 template <typename Target>
1529 static bool write(Target *target, const QVariant &value)
1530 {
1531 if (target)
1532 return write(*target, value);
1533 return false;
1534 }
1535
1536 template <typename ItemType>
1538 {
1539 const QMetaObject *mo = &ItemType::staticMetaObject;
1540 const QByteArray roleName = roleNames().value(role);
1541 if (const int index = mo->indexOfProperty(roleName.data()); index >= 0)
1542 return mo->property(index);
1543 return {};
1544 }
1545
1546 template <typename ItemType>
1547 QVariant readRole(int role, ItemType *gadget) const
1548 {
1549 using item_type = std::remove_pointer_t<ItemType>;
1550 QVariant result;
1551 QMetaProperty prop = roleProperty<item_type>(role);
1552 if (!prop.isValid() && role == Qt::EditRole)
1553 prop = roleProperty<item_type>(Qt::DisplayRole);
1554
1555 if (prop.isValid())
1556 result = readProperty(prop, gadget);
1557 return result;
1558 }
1559
1560 template <typename ItemType>
1561 QVariant readRole(int role, const ItemType &gadget) const
1562 {
1563 return readRole(role, &gadget);
1564 }
1565
1566 template <typename ItemType>
1567 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
1568 {
1569 if constexpr (std::is_base_of_v<QObject, ItemType>)
1570 return prop.read(gadget);
1571 else
1572 return prop.readOnGadget(gadget);
1573 }
1574 template <typename ItemType>
1575 static QVariant readProperty(int property, ItemType *gadget)
1576 {
1577 using item_type = std::remove_pointer_t<ItemType>;
1578 const QMetaObject &mo = item_type::staticMetaObject;
1579 const QMetaProperty prop = mo.property(property + mo.propertyOffset());
1580 return readProperty(prop, gadget);
1581 }
1582
1583 template <typename ItemType>
1584 static QVariant readProperty(int property, const ItemType &gadget)
1585 {
1586 return readProperty(property, &gadget);
1587 }
1588
1589 template <typename ItemType>
1590 bool writeRole(int role, ItemType *gadget, const QVariant &data)
1591 {
1592 using item_type = std::remove_pointer_t<ItemType>;
1593 auto prop = roleProperty<item_type>(role);
1594 if (!prop.isValid() && role == Qt::EditRole)
1595 prop = roleProperty<item_type>(Qt::DisplayRole);
1596
1597 return writeProperty(prop, gadget, data);
1598 }
1599
1600 template <typename ItemType>
1601 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
1602 {
1603 return writeRole(role, &gadget, data);
1604 }
1605
1606 template <typename ItemType>
1607 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
1608 {
1609 if constexpr (std::is_base_of_v<QObject, ItemType>)
1610 return prop.write(gadget, data);
1611 else
1612 return prop.writeOnGadget(gadget, data);
1613 }
1614 template <typename ItemType>
1615 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
1616 {
1617 using item_type = std::remove_pointer_t<ItemType>;
1618 const QMetaObject &mo = item_type::staticMetaObject;
1619 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
1620 }
1621
1622 template <typename ItemType>
1623 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
1624 {
1625 return writeProperty(property, &gadget, data);
1626 }
1627
1628 template <typename ItemType>
1629 static bool resetProperty(int property, ItemType *object)
1630 {
1631 using item_type = std::remove_pointer_t<ItemType>;
1632 const QMetaObject &mo = item_type::staticMetaObject;
1633 bool success = true;
1634 if (property == -1) {
1635 // reset all properties
1636 if constexpr (std::is_base_of_v<QObject, item_type>) {
1637 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
1638 success = writeProperty(mo.property(p), object, {}) && success;
1639 } else { // reset a gadget by assigning a default-constructed
1640 *object = {};
1641 }
1642 } else {
1643 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
1644 }
1645 return success;
1646 }
1647
1648 template <typename ItemType>
1649 static bool resetProperty(int property, ItemType &&object)
1650 {
1651 return resetProperty(property, &object);
1652 }
1653
1654 // helpers
1655 const_row_reference rowData(const QModelIndex &index) const
1656 {
1657 Q_ASSERT(index.isValid());
1658 return that().rowDataImpl(index);
1659 }
1660
1661 row_reference rowData(const QModelIndex &index)
1662 {
1663 Q_ASSERT(index.isValid());
1664 return that().rowDataImpl(index);
1665 }
1666
1667 const range_type *childRange(const QModelIndex &index) const
1668 {
1669 if (!index.isValid())
1670 return m_data.model();
1671 if (index.column()) // only items at column 0 can have children
1672 return nullptr;
1673 return that().childRangeImpl(index);
1674 }
1675
1676 range_type *childRange(const QModelIndex &index)
1677 {
1678 if (!index.isValid())
1679 return m_data.model();
1680 if (index.column()) // only items at column 0 can have children
1681 return nullptr;
1682 return that().childRangeImpl(index);
1683 }
1684
1685
1688
1690 Protocol m_protocol;
1691};
1692
1693// Implementations that depends on the model structure (flat vs tree) that will
1694// be specialized based on a protocol type. The main template implements tree
1695// support through a protocol type.
1696template <typename Range, typename Protocol>
1698 : public QGenericItemModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
1699{
1700 using Base = QGenericItemModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
1701 friend class QGenericItemModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
1702
1703 using range_type = typename Base::range_type;
1704 using range_features = typename Base::range_features;
1705 using row_type = typename Base::row_type;
1706 using row_ptr = typename Base::row_ptr;
1707 using const_row_ptr = typename Base::const_row_ptr;
1708
1709 using tree_traits = typename Base::protocol_traits;
1710 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
1711
1712 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
1713 QGenericItemModelDetails::is_smart_ptr<row_type>() ||
1714 QGenericItemModelDetails::is_any_of<row_type, std::reference_wrapper>();
1715 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
1716
1717public:
1718 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QGenericItemModel *itemModel)
1719 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
1720 {};
1721
1722protected:
1723 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
1724 {
1725 if (!parent.isValid())
1726 return this->createIndex(row, column);
1727 // only items at column 0 can have children
1728 if (parent.column())
1729 return QModelIndex();
1730
1731 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
1732 const auto &parentSiblings = childrenOf(grandParent);
1733 const auto it = QGenericItemModelDetails::cpos(parentSiblings, parent.row());
1734 return this->createIndex(row, column, QGenericItemModelDetails::pointerTo(*it));
1735 }
1736
1737 QModelIndex parent(const QModelIndex &child) const
1738 {
1739 if (!child.isValid())
1740 return child;
1741
1742 // no pointer to parent row - no parent
1743 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
1744 if (!parentRow)
1745 return {};
1746
1747 // get the siblings of the parent via the grand parent
1748 decltype(auto) grandParent = this->protocol().parentRow(QGenericItemModelDetails::refTo(parentRow));
1749 const range_type &parentSiblings = childrenOf(QGenericItemModelDetails::pointerTo(grandParent));
1750 // find the index of parentRow
1751 const auto begin = QGenericItemModelDetails::cbegin(parentSiblings);
1752 const auto end = QGenericItemModelDetails::cend(parentSiblings);
1753 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
1754 return QGenericItemModelDetails::pointerTo(s) == parentRow;
1755 });
1756 if (it != end)
1757 return this->createIndex(std::distance(begin, it), 0,
1758 QGenericItemModelDetails::pointerTo(grandParent));
1759 return {};
1760 }
1761
1762 int rowCount(const QModelIndex &parent) const
1763 {
1764 return Base::size(this->childRange(parent));
1765 }
1766
1767 int columnCount(const QModelIndex &) const
1768 {
1769 // all levels of a tree have to have the same, static, column count
1770 if constexpr (Base::one_dimensional_range)
1771 return 1;
1772 else
1773 return Base::static_column_count; // if static_column_count is -1, static assert fires
1774 }
1775
1776 static constexpr Qt::ItemFlags defaultFlags()
1777 {
1778 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
1779 }
1780
1781 static constexpr bool canInsertRows()
1782 {
1783 // We must not insert rows if we cannot adjust the parents of the
1784 // children of the following rows. We don't have to do that if the
1785 // range operates on pointers.
1786 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
1787 && Base::dynamicRows() && range_features::has_insert;
1788 }
1789
1790 static constexpr bool canRemoveRows()
1791 {
1792 // We must not remove rows if we cannot adjust the parents of the
1793 // children of the following rows. We don't have to do that if the
1794 // range operates on pointers.
1795 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
1796 && Base::dynamicRows() && range_features::has_erase;
1797 }
1798
1799 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
1800 {
1801 return true;
1802 }
1803
1804 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
1805 {
1806 return true;
1807 }
1808
1809 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
1810 const QModelIndex &destParent, int destRow)
1811 {
1812 // If rows are pointers, then reference to the parent row don't
1813 // change, so we can move them around freely. Otherwise we need to
1814 // be able to explicitly update the parent pointer.
1815 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
1816 return false;
1817 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
1818 return false;
1819 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
1820 destParent, destRow)) {
1821 return false;
1822 }
1823
1824 range_type *source = this->childRange(sourceParent);
1825 range_type *destination = this->childRange(destParent);
1826
1827 // If we can insert data from another range into, then
1828 // use that to move the old data over.
1829 const auto destStart = QGenericItemModelDetails::pos(destination, destRow);
1830 if constexpr (range_features::has_insert_range) {
1831 const auto sourceStart = QGenericItemModelDetails::pos(*source, sourceRow);
1832 const auto sourceEnd = std::next(sourceStart, count);
1833
1834 destination->insert(destStart, std::move_iterator(sourceStart),
1835 std::move_iterator(sourceEnd));
1836 } else if constexpr (std::is_copy_constructible_v<row_type>) {
1837 // otherwise we have to make space first, and copy later.
1838 destination->insert(destStart, count, row_type{});
1839 }
1840
1841 row_ptr parentRow = destParent.isValid()
1842 ? QGenericItemModelDetails::pointerTo(this->rowData(destParent))
1843 : nullptr;
1844
1845 // if the source's parent was already inside the new parent row,
1846 // then the source row might have become invalid, so reset it.
1847 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
1848 if (sourceParent.row() < destRow) {
1849 source = this->childRange(sourceParent);
1850 } else {
1851 // the source parent moved down within destination
1852 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
1853 sourceParent.internalPointer()));
1854 }
1855 }
1856
1857 // move the data over and update the parent pointer
1858 {
1859 const auto writeStart = QGenericItemModelDetails::pos(destination, destRow);
1860 const auto writeEnd = std::next(writeStart, count);
1861 const auto sourceStart = QGenericItemModelDetails::pos(source, sourceRow);
1862 const auto sourceEnd = std::next(sourceStart, count);
1863
1864 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
1865 // move data over if not already done, otherwise
1866 // only fix the parent pointer
1867 if constexpr (!range_features::has_insert_range)
1868 *write = std::move(*read);
1869 this->protocol().setParentRow(QGenericItemModelDetails::refTo(*write), parentRow);
1870 }
1871 // remove the old rows from the source parent
1872 source->erase(sourceStart, sourceEnd);
1873 }
1874
1875 // Fix the parent pointers in children of both source and destination
1876 // ranges, as the references to the entries might have become invalid.
1877 // We don't have to do that if the rows are pointers, as in that case
1878 // the references to the entries are stable.
1879 resetParentInChildren(destination);
1881
1882 this->endMoveRows();
1883 return true;
1884 }
1885
1886 auto makeEmptyRow(const QModelIndex &parent)
1887 {
1888 // tree traversal protocol: if we are here, then it must be possible
1889 // to change the parent of a row.
1890 static_assert(tree_traits::has_setParentRow);
1891 row_type empty_row = this->protocol().newRow();
1892 if (QGenericItemModelDetails::isValid(empty_row) && parent.isValid()) {
1893 this->protocol().setParentRow(QGenericItemModelDetails::refTo(empty_row),
1894 QGenericItemModelDetails::pointerTo(this->rowData(parent)));
1895 }
1896 return empty_row;
1897 }
1898
1899 template <typename It, typename Sentinel>
1900 void deleteRemovedRows(It &&begin, Sentinel &&end)
1901 {
1902 if constexpr (tree_traits::has_deleteRow) {
1903 for (auto it = begin; it != end; ++it)
1904 this->protocol().deleteRow(*it);
1905 }
1906 }
1907
1908 void resetParentInChildren(range_type *children)
1909 {
1910 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
1911 const auto begin = QGenericItemModelDetails::begin(*children);
1912 const auto end = QGenericItemModelDetails::end(*children);
1913 for (auto it = begin; it != end; ++it) {
1914 if (auto &maybeChildren = this->protocol().childRows(*it)) {
1915 QModelIndexList fromIndexes;
1916 QModelIndexList toIndexes;
1917 fromIndexes.reserve(Base::size(*maybeChildren));
1918 toIndexes.reserve(Base::size(*maybeChildren));
1919 auto *parentRow = QGenericItemModelDetails::pointerTo(*it);
1920
1921 int row = 0;
1922 for (auto &child : *maybeChildren) {
1923 const_row_ptr oldParent = this->protocol().parentRow(child);
1924 if (oldParent != parentRow) {
1925 fromIndexes.append(this->createIndex(row, 0, oldParent));
1926 toIndexes.append(this->createIndex(row, 0, parentRow));
1927 this->protocol().setParentRow(child, parentRow);
1928 }
1929 ++row;
1930 }
1931 this->changePersistentIndexList(fromIndexes, toIndexes);
1932 resetParentInChildren(QGenericItemModelDetails::pointerTo(*maybeChildren));
1933 }
1934 }
1935 }
1936 }
1937
1938 decltype(auto) rowDataImpl(const QModelIndex &index) const
1939 {
1940 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1941 const range_type &siblings = childrenOf(parentRow);
1942 Q_ASSERT(index.row() < int(Base::size(siblings)));
1943 return *QGenericItemModelDetails::cpos(siblings, index.row());
1944 }
1945
1946 decltype(auto) rowDataImpl(const QModelIndex &index)
1947 {
1948 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
1949 range_type &siblings = childrenOf(parentRow);
1950 Q_ASSERT(index.row() < int(Base::size(siblings)));
1951 return *QGenericItemModelDetails::pos(siblings, index.row());
1952 }
1953
1954 const range_type *childRangeImpl(const QModelIndex &index) const
1955 {
1956 const auto &row = this->rowData(index);
1957 if (!QGenericItemModelDetails::isValid(row))
1958 return static_cast<const range_type *>(nullptr);
1959
1960 decltype(auto) children = this->protocol().childRows(QGenericItemModelDetails::refTo(row));
1961 return QGenericItemModelDetails::pointerTo(std::forward<decltype(children)>(children));
1962 }
1963
1964 range_type *childRangeImpl(const QModelIndex &index)
1965 {
1966 auto &row = this->rowData(index);
1967 if (!QGenericItemModelDetails::isValid(row))
1968 return static_cast<range_type *>(nullptr);
1969
1970 decltype(auto) children = this->protocol().childRows(QGenericItemModelDetails::refTo(row));
1971 using Children = std::remove_reference_t<decltype(children)>;
1972
1973 if constexpr (QGenericItemModelDetails::is_any_of<Children, std::optional>()
1974 && std::is_default_constructible<typename Children::value_type>()) {
1975 if (!children)
1976 children.emplace(range_type{});
1977 }
1978
1979 return QGenericItemModelDetails::pointerTo(std::forward<decltype(children)>(children));
1980 }
1981
1982 const range_type &childrenOf(const_row_ptr row) const
1983 {
1984 return row ? QGenericItemModelDetails::refTo(this->protocol().childRows(*row))
1985 : *this->m_data.model();
1986 }
1987
1988private:
1989 range_type &childrenOf(row_ptr row)
1990 {
1991 return row ? QGenericItemModelDetails::refTo(this->protocol().childRows(*row))
1992 : *this->m_data.model();
1993 }
1994};
1995
1996// specialization for flat models without protocol
1997template <typename Range>
2000{
2003
2004 using range_type = typename Base::range_type;
2005 using range_features = typename Base::range_features;
2006 using row_type = typename Base::row_type;
2007 using const_row_ptr = typename Base::const_row_ptr;
2008 using row_traits = typename Base::row_traits;
2009 using row_features = typename Base::row_features;
2010
2011 static constexpr bool is_mutable_impl = true;
2012
2013public:
2014 explicit QGenericTableItemModelImpl(Range &&model, QGenericItemModel *itemModel)
2015 : Base(std::forward<Range>(model), {}, itemModel)
2016 {}
2017
2018protected:
2019 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
2020 {
2021 if constexpr (Base::dynamicColumns()) {
2022 if (column < int(Base::size(*QGenericItemModelDetails::cpos(*this->m_data.model(), row))))
2023 return this->createIndex(row, column);
2024 // if we got here, then column < columnCount(), but this row is to short
2025 qCritical("QGenericItemModel: Column-range at row %d is not large enough!", row);
2026 return {};
2027 } else {
2028 return this->createIndex(row, column);
2029 }
2030 }
2031
2032 QModelIndex parent(const QModelIndex &) const
2033 {
2034 return {};
2035 }
2036
2037 int rowCount(const QModelIndex &parent) const
2038 {
2039 if (parent.isValid())
2040 return 0;
2041 return int(Base::size(*this->m_data.model()));
2042 }
2043
2044 int columnCount(const QModelIndex &parent) const
2045 {
2046 if (parent.isValid())
2047 return 0;
2048
2049 // in a table, all rows have the same number of columns (as the first row)
2050 if constexpr (Base::dynamicColumns()) {
2051 return int(Base::size(*this->m_data.model()) == 0
2052 ? 0
2053 : Base::size(*QGenericItemModelDetails::cbegin(*this->m_data.model())));
2054 } else if constexpr (Base::one_dimensional_range) {
2055 return row_traits::fixed_size();
2056 } else {
2057 return Base::static_column_count;
2058 }
2059 }
2060
2061 static constexpr Qt::ItemFlags defaultFlags()
2062 {
2063 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
2064 }
2065
2066 static constexpr bool canInsertRows()
2067 {
2068 return Base::dynamicRows() && range_features::has_insert;
2069 }
2070
2071 static constexpr bool canRemoveRows()
2072 {
2073 return Base::dynamicRows() && range_features::has_erase;
2074 }
2075
2076 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
2077 {
2078 return !source.isValid() && !destination.isValid();
2079 }
2080
2081 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
2082 {
2083 return !source.isValid() && !destination.isValid();
2084 }
2085
2086 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
2087 const QModelIndex &, int) noexcept
2088 {
2089 // table/flat model: can't move rows between different parents
2090 return false;
2091 }
2092
2093 auto makeEmptyRow(const QModelIndex &)
2094 {
2095 row_type empty_row = this->protocol().newRow();
2096
2097 // dynamically sized rows all have to have the same column count
2098 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
2099 if (QGenericItemModelDetails::isValid(empty_row))
2100 QGenericItemModelDetails::refTo(empty_row).resize(this->itemModel().columnCount());
2101 }
2102
2103 return empty_row;
2104 }
2105
2106 template <typename It, typename Sentinel>
2107 void deleteRemovedRows(It &&begin, Sentinel &&end)
2108 {
2109 if constexpr (Base::protocol_traits::has_deleteRow) {
2110 for (auto it = begin; it != end; ++it)
2111 this->protocol().deleteRow(*it);
2112 }
2113 }
2114
2115 decltype(auto) rowDataImpl(const QModelIndex &index) const
2116 {
2117 Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
2118 return *QGenericItemModelDetails::cpos(*this->m_data.model(), index.row());
2119 }
2120
2121 decltype(auto) rowDataImpl(const QModelIndex &index)
2122 {
2123 Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
2124 return *QGenericItemModelDetails::pos(*this->m_data.model(), index.row());
2125 }
2126
2127 auto childRangeImpl(const QModelIndex &) const
2128 {
2129 return nullptr;
2130 }
2131
2132 auto childRangeImpl(const QModelIndex &)
2133 {
2134 return nullptr;
2135 }
2136
2137 const range_type &childrenOf(const_row_ptr row) const
2138 {
2139 Q_ASSERT(!row);
2140 return *this->m_data.model();
2141 }
2142
2143 void resetParentInChildren(range_type *)
2144 {
2145 }
2146};
2147
2148QT_END_NAMESPACE
2149
2150#endif // Q_QDOC
2151
2152#endif // QGENERICITEMMODEL_IMPL_H
void beginRemoveRows(const QModelIndex &parent, int start, int count)
~QGenericItemModelImplBase()=default
Ret call(Op op, const Args &...args)
QGenericItemModelImplBase(QGenericItemModel *itemModel, const Impl *)
bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destParent, int destRow)
void dataChanged(const QModelIndex &from, const QModelIndex &to, const QList< int > &roles)
const QAbstractItemModel & itemModel() const
QModelIndex createIndex(int row, int column, const void *ptr=nullptr) const
Ret callConst(ConstOp op, const Args &...args) const
bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destParent, int destRow)
QHash< int, QByteArray > roleNames() const
void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to)
QAbstractItemModel & itemModel()
void beginRemoveColumns(const QModelIndex &parent, int start, int count)
void beginInsertColumns(const QModelIndex &parent, int start, int count)
void beginInsertRows(const QModelIndex &parent, int start, int count)
QModelIndex index(int row, int column, const QModelIndex &parent) const
bool removeColumns(int column, int count, const QModelIndex &parent)
static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
QModelIndex sibling(int row, int column, const QModelIndex &index) const
QVariant data(const QModelIndex &index, int role) const
QMetaProperty roleProperty(int role) const
static bool write(Target &target, const QVariant &value)
bool insertColumns(int column, int count, const QModelIndex &parent)
bool setItemData(const QModelIndex &index, const QMap< int, QVariant > &data)
static constexpr int size(const C &c)
static constexpr bool rows_are_owning_or_raw_pointers
static constexpr bool rows_are_raw_pointers
QMap< int, QVariant > itemData(const QModelIndex &index) const
static constexpr bool one_dimensional_range
static constexpr bool dynamicRows()
static constexpr bool canInsertRows()
bool clearItemData(const QModelIndex &index)
range_type * childRange(const QModelIndex &index)
static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
bool writeAt(const QModelIndex &index, F &&writer)
const Structure & that() const
QVariant readRole(int role, ItemType *gadget) const
static bool resetProperty(int property, ItemType &&object)
static constexpr int static_column_count
static QVariant readProperty(int property, const ItemType &gadget)
bool setData(const QModelIndex &index, const QVariant &data, int role)
static void call(Op op, QGenericItemModelImplBase *that, void *r, const void *args)
bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn)
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
QVariant headerData(int section, Qt::Orientation orientation, int role) const
QVariant readRole(int role, const ItemType &gadget) const
static constexpr bool dynamicColumns()
static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
static constexpr bool has_metaobject
static constexpr int static_row_count
static bool resetProperty(int property, ItemType *object)
Qt::ItemFlags flags(const QModelIndex &index) const
QGenericItemModelImpl(Range &&model, Protocol &&protocol, QGenericItemModel *itemModel)
static QVariant read(const Value &value)
bool insertRows(int row, int count, const QModelIndex &parent)
static constexpr bool isMutable()
const_row_reference rowData(const QModelIndex &index) const
static QVariant readProperty(int property, ItemType *gadget)
static bool write(Target *target, const QVariant &value)
bool writeRole(int role, ItemType &&gadget, const QVariant &data)
const protocol_type & protocol() const
static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
const range_type * childRange(const QModelIndex &index) const
bool removeRows(int row, int count, const QModelIndex &parent={})
void readAt(const QModelIndex &index, F &&reader) const
bool writeRole(int role, ItemType *gadget, const QVariant &data)
static QVariant read(Value *value)
row_reference rowData(const QModelIndex &index)
static void callConst(ConstOp op, const QGenericItemModelImplBase *that, void *r, const void *args)
static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
void deleteRemovedRows(It &&begin, Sentinel &&end)
QGenericTableItemModelImpl(Range &&model, QGenericItemModel *itemModel)
const range_type & childrenOf(const_row_ptr row) const
auto makeEmptyRow(const QModelIndex &)
auto childRangeImpl(const QModelIndex &)
QModelIndex parent(const QModelIndex &) const
auto childRangeImpl(const QModelIndex &) const
int rowCount(const QModelIndex &parent) const
static constexpr bool canRemoveRows()
constexpr bool moveRowsAcross(const QModelIndex &, int, int, const QModelIndex &, int) noexcept
decltype(auto) rowDataImpl(const QModelIndex &index) const
static constexpr bool canInsertRows()
int columnCount(const QModelIndex &parent) const
static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
decltype(auto) rowDataImpl(const QModelIndex &index)
QModelIndex indexImpl(int row, int column, const QModelIndex &) const
static constexpr Qt::ItemFlags defaultFlags()
static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
int columnCount(const QModelIndex &) const
range_type * childRangeImpl(const QModelIndex &index)
bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
void resetParentInChildren(range_type *children)
static constexpr bool canInsertRows()
static constexpr bool canRemoveRows()
static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
const range_type & childrenOf(const_row_ptr row) const
decltype(auto) rowDataImpl(const QModelIndex &index)
QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
void deleteRemovedRows(It &&begin, Sentinel &&end)
QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QGenericItemModel *itemModel)
const range_type * childRangeImpl(const QModelIndex &index) const
int rowCount(const QModelIndex &parent) const
decltype(auto) rowDataImpl(const QModelIndex &index) const
static constexpr Qt::ItemFlags defaultFlags()
auto makeEmptyRow(const QModelIndex &parent)
QModelIndex parent(const QModelIndex &child) const
static constexpr bool is_multi_role_v
static decltype(auto) refTo(T &&t)
static auto end(C &&c) -> decltype(std::end(refTo(std::forward< C >(c))))
static auto cend(C &&c) -> decltype(std::cend(refTo(std::forward< C >(c))))
auto value(It &&it) -> decltype(it.value())
static auto cpos(C &&c, int i)
static auto begin(C &&c) -> decltype(std::begin(refTo(std::forward< C >(c))))
static constexpr bool is_range_v
static auto pos(C &&c, int i)
static auto cbegin(C &&c) -> decltype(std::cbegin(refTo(std::forward< C >(c))))
auto key(It &&it) -> decltype(it.key())
static constexpr bool has_metaobject_v
static constexpr bool isValid(const T &t) noexcept
static constexpr int static_size_v
auto childRows(const R &row) const -> decltype(row.childRows())
auto childRows(R &row) -> decltype(row.childRows())
auto setParentRow(R &row, R *parent) -> decltype(row.setParentRow(parent))
auto parentRow(const R &row) const -> decltype(row.parentRow())
friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept