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
qrangemodel_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// Qt-Security score:significant reason:default
4
5#ifndef QRANGEMODEL_IMPL_H
6#define QRANGEMODEL_IMPL_H
7
8#ifndef Q_QDOC
9
10#ifndef QRANGEMODEL_H
11#error Do not include qrangemodel_impl.h directly
12#endif
13
14#if 0
15#pragma qt_sync_skip_header_check
16#pragma qt_sync_stop_processing
17#endif
18
19#include <QtCore/qabstractitemmodel.h>
20#include <QtCore/qmetaobject.h>
21#include <QtCore/qvariant.h>
22#include <QtCore/qmap.h>
23
24#include <algorithm>
25#include <functional>
26#include <iterator>
27#include <type_traits>
28#include <QtCore/q20type_traits.h>
29#include <tuple>
30#include <QtCore/q23utility.h>
31
32QT_BEGIN_NAMESPACE
33
34namespace QtPrivate {
35
36template <typename Applier, size_t ...Is>
37void applyIndexSwitch(size_t index, Applier&& applier, std::index_sequence<Is...>)
38{
39 // Performance considerations:
40 // The folding expression used here represents the same logic as a sequence of
41 // linear if/else if/... statements. Experiments show that Clang, GCC, and MSVC
42 // optimize it to essentially the same bytecode as a normal C++ switch,
43 // ensuring O(1) lookup complexity.
44 static_cast<void>(((Is == index ? (applier(std::integral_constant<size_t, Is>{}), true) : false)
45 || ...));
46}
47
48template <size_t IndexCount, typename Applier>
49void applyIndexSwitch(size_t index, Applier&& applier)
50{
51 applyIndexSwitch(index, std::forward<Applier>(applier), std::make_index_sequence<IndexCount>());
52}
53
54// TODO: move to a separate header in Qt 6.11
55template <typename Interface>
57{
58private:
59 template <typename Arg>
60 static constexpr bool passArgAsValue = sizeof(Arg) <= sizeof(size_t)
62
63 template <typename ...>
64 struct MethodImpl;
65
66 template <typename M, typename R, typename I, typename... Args>
67 struct MethodImpl<M, R, I, Args...>
68 {
69 static_assert(std::is_base_of_v<I, Interface>, "The method must belong to the interface");
70 using return_type = R;
71 using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
72
73 static constexpr size_t index()
74 {
75 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
76 }
77
78 private:
79 template <size_t Ix>
80 static constexpr bool matchesAt()
81 {
82 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
83 }
84
85 template <size_t... Is>
86 static constexpr size_t index(std::index_sequence<Is...>)
87 {
88 constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
89 static_assert(matchesCount == 1, "Expected exactly one match");
90 return ((size_t(matchesAt<Is>()) * Is) + ...);
91 }
92
93 static R invoke(I &intf /*const validation*/, Args... args)
94 {
95 Q_ASSERT(intf.m_callFN);
96
97 auto& baseIntf = static_cast<base_interface&>(const_cast<std::remove_const_t<I>&>(intf));
98 call_args callArgs(std::forward<Args>(args)...);
99 if constexpr (std::is_void_v<R>) {
100 intf.m_callFN(index(), baseIntf, nullptr, &callArgs);
101 } else {
102 alignas(R) std::byte buf[sizeof(R)];
103 intf.m_callFN(index(), baseIntf, buf, &callArgs);
104
105 R* result = std::launder(reinterpret_cast<R*>(buf));
106 QScopeGuard destroyBuffer([result]() { std::destroy_at(result); });
107 return std::forward<R>(*result);
108 }
109 }
110
111 friend class QQuasiVirtualInterface<Interface>;
112 };
113
114 template <typename M, typename R, typename I, typename... Args>
115 struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
116 template <typename Subclass>
117 using Overridden = R(Subclass::*)(Args...);
118 };
119
120 template <typename M, typename R, typename I, typename... Args>
121 struct MethodImpl<M, R(I::*)(Args...) const> : MethodImpl<M, R, const I, Args...> {
122 template <typename Subclass>
123 using Overridden = R(Subclass::*)(Args...) const;
124 };
125
126 template <typename C = Interface> using Methods = typename C::template MethodTemplates<C>;
127
128public:
129 template <auto prototype>
130 struct Method : MethodImpl<Method<prototype>, decltype(prototype)> {};
131
132 template <typename Method, typename... Args>
133 auto call(Args &&... args) const
134 {
135 return Method::invoke(static_cast<const Interface &>(*this), std::forward<Args>(args)...);
136 }
137
138 template <typename Method, typename... Args>
139 auto call(Args &&... args)
140 {
141 return Method::invoke(static_cast<Interface &>(*this), std::forward<Args>(args)...);
142 }
143
144 void destroy(); // quasi-virtual pure destructor
146
147 struct Deleter
148 {
149 void operator () (QQuasiVirtualInterface* self) const { self->call<Destroy>(); }
150 };
151
152protected:
154 using CallFN = void (*)(size_t index, base_interface &intf, void *ret, void *args);
155 void initCallFN(CallFN func) { m_callFN = func; }
156
159
160private:
162 CallFN m_callFN = nullptr;
163};
164
165template <typename Subclass, typename Interface>
166class QQuasiVirtualSubclass : public Interface
167{
168private:
169 template <typename C = Subclass> using Methods = typename C::template MethodTemplates<C>;
170
171 template <size_t OverriddenIndex>
172 static constexpr size_t interfaceMethodIndex() {
173 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
174 }
175
176 template <size_t... Is>
177 static void callImpl(size_t index, Subclass &subclass, void *ret, void *args, std::index_sequence<Is...>)
178 {
179 // TODO: come up with more sophisticated check if methods count becomes more than 64
180 static constexpr std::uint64_t methodIndexMask = ((uint64_t(1)
181 << interfaceMethodIndex<Is>()) | ...);
182 static_assert(sizeof...(Is) == std::tuple_size_v<Methods<Interface>>,
183 "Base and overridden methods count are different");
184 static_assert(methodIndexMask == (uint64_t(1) << sizeof...(Is)) - 1,
185 "Mapping between base and overridden methods is not unique");
186
187 auto doInvoke = [&](auto idxConstant) {
188 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
189 };
190 applyIndexSwitch(index, doInvoke, std::index_sequence<interfaceMethodIndex<Is>()...>{});
191 }
192
193 static void callImpl(size_t index, typename Interface::base_interface &intf, void *ret, void *args)
194 {
195 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
196 callImpl(index, static_cast<Subclass&>(intf), ret, args, seq);
197 }
198
199 template <typename BaseMethod>
200 using OverridenSignature = typename BaseMethod::template Overridden<Subclass>;
201
202protected:
203 template <typename... Args>
204 QQuasiVirtualSubclass(Args &&... args)
205 : Interface(std::forward<Args>(args)...)
206 {
207 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
208 }
209
210public:
211 template <typename BaseMethod, OverridenSignature<BaseMethod> overridden>
212 struct Override : BaseMethod
213 {
214 private:
215 static constexpr void doInvoke(Subclass &subclass, void *ret, void *args)
216 {
217 using Return = typename BaseMethod::return_type;
218 using PackedArgs = typename BaseMethod::call_args;
219
220 Q_ASSERT(args);
221 Q_ASSERT(std::is_void_v<Return> == !ret);
222
223 auto invoke = [&subclass](auto &&...params)
224 {
225 return std::invoke(overridden, &subclass, std::forward<decltype(params)>(params)...);
226 };
227
228 if constexpr (std::is_void_v<Return>) {
229 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args)));
230 } else {
231 // Note, that ::new Return(...) fails on Integrity.
232 // TODO: use std::construct_at for c++20
233 using Alloc = std::allocator<Return>;
234 Alloc alloc;
235 std::allocator_traits<Alloc>::construct(alloc, static_cast<Return *>(ret),
236 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args))));
237 }
238
239 }
240
241 friend class QQuasiVirtualSubclass<Subclass, Interface>;
242 };
243};
244
245}
246
247namespace QRangeModelDetails
248{
249 template <typename T, template <typename...> typename... Templates>
251
252 template <template <typename...> typename Template,
253 typename... Params,
254 template <typename...> typename... Templates>
256
257 template <typename T,
258 template <typename...> typename Template,
259 template <typename...> typename... Templates>
261
262 template <typename T, template <typename...> typename... Templates>
264
265 template <typename T, typename = void>
267
268 template <typename T>
269 struct is_validatable<T, std::void_t<decltype(*std::declval<T>())>>
270 : std::is_constructible<bool, T> {};
271
272 template <typename T, typename = void>
274
275 template <typename T>
278 std::is_pointer<decltype(std::declval<T&>().get())>,
279 std::is_same<decltype(*std::declval<T&>().get()), decltype(*std::declval<T&>())>,
281 >>>
282 : std::true_type
283 {};
284
285 // TODO: shouldn't we check is_smart_ptr && !is_copy_constructible && !is_copy_assignable
286 // to support users-specific ptrs?
287 template <typename T>
289#ifndef QT_NO_SCOPED_POINTER
291#endif
293 >;
294
295 template <typename T>
298
299 template <typename T>
301 std::is_pointer<T>>;
302
303 template <typename T>
304 static auto pointerTo(T&& t) {
305 using Type = q20::remove_cvref_t<T>;
306 if constexpr (is_any_of<Type, std::optional>())
307 return t ? std::addressof(*std::forward<T>(t)) : nullptr;
308 else if constexpr (std::is_pointer<Type>())
309 return t;
310 else if constexpr (is_smart_ptr<Type>())
311 return t.get();
312 else if constexpr (is_any_of<Type, std::reference_wrapper>())
313 return std::addressof(t.get());
314 else
315 return std::addressof(std::forward<T>(t));
316 }
317
318 template <typename T>
320
321 template <typename T>
323
324 template <typename T, typename = void>
326 template <typename T, std::size_t N>
327 struct tuple_like<std::array<T, N>> : std::false_type {};
328 template <typename T>
330 template <typename T>
331 [[maybe_unused]] static constexpr bool tuple_like_v = tuple_like<T>::value;
332
333 template <typename T, typename = void>
335 template <typename T, std::size_t N>
336 struct array_like<std::array<T, N>> : std::true_type {};
337 template <typename T, std::size_t N>
338 struct array_like<T[N]> : std::true_type {};
339 template <typename T>
340 [[maybe_unused]] static constexpr bool array_like_v = array_like<T>::value;
341
342 template <typename T, typename = void>
344 template <typename T>
346 : std::true_type {};
347 template <typename T>
348 [[maybe_unused]] static constexpr bool has_metaobject_v = has_metaobject<T>::value;
349
350 template <typename T>
351 static constexpr bool isValid(const T &t) noexcept
352 {
353 if constexpr (std::is_array_v<T>)
354 return true;
355 else if constexpr (is_validatable<T>())
356 return bool(t);
357 else
358 return true;
359 }
360
361 template <typename T>
362 static decltype(auto) refTo(T&& t) {
363 Q_ASSERT(isValid(t));
364 // it's allowed to move only if the object holds unique ownership of the wrapped data
365 using Type = q20::remove_cvref_t<T>;
366 if constexpr (is_any_of<T, std::optional>())
367 return *std::forward<T>(t); // let std::optional resolve dereferencing
368 if constexpr (!is_wrapped<Type>() || is_any_unique_ptr<Type>())
369 return q23::forward_like<T>(*pointerTo(t));
370 else
371 return *pointerTo(t);
372 }
373
374 template <typename It>
375 auto key(It&& it) -> decltype(it.key()) { return std::forward<It>(it).key(); }
376 template <typename It>
377 auto key(It&& it) -> decltype((it->first)) { return std::forward<It>(it)->first; }
378
379 template <typename It>
380 auto value(It&& it) -> decltype(it.value()) { return std::forward<It>(it).value(); }
381 template <typename It>
382 auto value(It&& it) -> decltype((it->second)) { return std::forward<It>(it)->second; }
383
384 // use our own version of begin/end so that we can overload for pointers
385 template <typename C>
386 static auto begin(C &&c) -> decltype(std::begin(refTo(std::forward<C>(c))))
387 { return std::begin(refTo(std::forward<C>(c))); }
388 template <typename C>
389 static auto end(C &&c) -> decltype(std::end(refTo(std::forward<C>(c))))
390 { return std::end(refTo(std::forward<C>(c))); }
391 template <typename C>
392 static auto pos(C &&c, int i)
393 { return std::next(QRangeModelDetails::begin(std::forward<C>(c)), i); }
394
395 // Test if a type is a range, and whether we can modify it using the
396 // standard C++ container member functions insert, erase, and resize.
397 // For the sake of QAIM, we cannot modify a range if it holds const data
398 // even if the range itself is not const; we'd need to initialize new rows
399 // and columns, and move old row and column data.
400 template <typename C, typename = void>
402
403 template <typename C>
404 struct test_insert<C, std::void_t<decltype(std::declval<C>().insert(
405 std::declval<typename C::const_iterator>(),
406 std::declval<typename C::size_type>(),
407 std::declval<typename C::value_type>()
408 ))>>
409 : std::true_type
410 {};
411
412 // Can we insert from another (identical) range? Required to support
413 // move-only types
414 template <typename C, typename = void>
416
417 template <typename C>
418 struct test_insert_range<C, std::void_t<decltype(std::declval<C&>().insert(
419 std::declval<typename C::const_iterator&>(),
420 std::declval<std::move_iterator<typename C::iterator>&>(),
421 std::declval<std::move_iterator<typename C::iterator>&>()
422 ))>>
423 : std::true_type
424 {};
425
426 template <typename C, typename = void>
428
429 template <typename C>
430 struct test_erase<C, std::void_t<decltype(std::declval<C>().erase(
431 std::declval<typename C::const_iterator>(),
432 std::declval<typename C::const_iterator>()
433 ))>>
434 : std::true_type
435 {};
436
437 template <typename C, typename = void>
439
440 template <typename C>
441 struct test_resize<C, std::void_t<decltype(std::declval<C>().resize(
442 std::declval<typename C::size_type>(),
443 std::declval<typename C::value_type>()
444 ))>>
445 : std::true_type
446 {};
447
448 // we use std::rotate in moveRows/Columns, which requires std::swap
449 template <typename It, typename = void>
451
452 template <typename It>
453 struct test_rotate<It, std::void_t<decltype(std::swap(*std::declval<It>(),
454 *std::declval<It>()))>>
455 : std::true_type
456 {};
457
458 template <typename C, typename = void>
460
461 template <typename C>
462 struct test_splice<C, std::void_t<decltype(std::declval<C>().splice(
463 std::declval<typename C::const_iterator>(),
464 std::declval<C&>(),
465 std::declval<typename C::const_iterator>(),
466 std::declval<typename C::const_iterator>()
467 ))>>
468 : std::true_type
469 {};
470
471 template <typename C>
472 static void rotate(C& c, int src, int count, int dst) {
473 auto& container = QRangeModelDetails::refTo(c);
474 using Container = std::remove_reference_t<decltype(container)>;
475
476 const auto srcBegin = QRangeModelDetails::pos(container, src);
477 const auto srcEnd = std::next(srcBegin, count);
478 const auto dstBegin = QRangeModelDetails::pos(container, dst);
479
480 if constexpr (test_splice<Container>::value) {
481 if (dst > src && dst < src + count) // dst must be out of the source range
482 container.splice(srcBegin, container, dstBegin, srcEnd);
483 else if (dst != src) // otherwise, std::list gets corrupted
484 container.splice(dstBegin, container, srcBegin, srcEnd);
485 } else {
486 if (src < dst) // moving right
487 std::rotate(srcBegin, srcEnd, dstBegin);
488 else // moving left
489 std::rotate(dstBegin, srcBegin, srcEnd);
490 }
491 }
492
493 // Test if a type is an associative container that we can use for multi-role
494 // data, i.e. has a key_type and a mapped_type typedef, and maps from int,
495 // Qt::ItemDataRole, or QString to QVariant. This excludes std::set (and
496 // unordered_set), which are not useful for us anyway even though they are
497 // considered associative containers.
498 template <typename C, typename = void> struct is_multi_role : std::false_type
499 {
500 static constexpr bool int_key = false;
501 };
502 template <typename C> // Qt::ItemDataRole -> QVariant, or QString -> QVariant, int -> QVariant
503 struct is_multi_role<C, std::void_t<typename C::key_type, typename C::mapped_type>>
504 : std::conjunction<std::disjunction<std::is_same<typename C::key_type, int>,
505 std::is_same<typename C::key_type, Qt::ItemDataRole>,
506 std::is_same<typename C::key_type, QString>>,
507 std::is_same<typename C::mapped_type, QVariant>>
508 {
509 static constexpr bool int_key = !std::is_same_v<typename C::key_type, QString>;
510 };
511 template <typename C>
512 [[maybe_unused]]
513 static constexpr bool is_multi_role_v = is_multi_role<C>::value;
514
515 template <typename C, typename = void>
517 template <typename C>
518 struct test_size<C, std::void_t<decltype(std::size(std::declval<C&>()))>> : std::true_type {};
519
520 template <typename C, typename = void>
522 template <typename C>
523 struct test_cbegin<C, std::void_t<decltype(std::begin(std::declval<const C&>()))>>
524 : std::true_type
525 {};
526
527 template <typename C, typename = void>
529 static constexpr bool is_mutable = !std::is_const_v<C>;
530 static constexpr bool has_insert = false;
531 static constexpr bool has_insert_range = false;
532 static constexpr bool has_erase = false;
533 static constexpr bool has_resize = false;
534 static constexpr bool has_rotate = false;
535 static constexpr bool has_splice = false;
536 static constexpr bool has_cbegin = false;
537 };
538 template <typename C>
539 struct range_traits<C, std::void_t<decltype(begin(std::declval<C&>())),
540 decltype(end(std::declval<C&>())),
542 >> : std::true_type
543 {
544 using iterator = decltype(begin(std::declval<C&>()));
546 static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
547 static constexpr bool has_insert = test_insert<C>();
548 static constexpr bool has_insert_range = test_insert_range<C>();
549 static constexpr bool has_erase = test_erase<C>();
550 static constexpr bool has_resize = test_resize<C>();
551 static constexpr bool has_rotate = test_rotate<iterator>();
552 static constexpr bool has_splice = test_splice<C>();
553 static constexpr bool has_cbegin = test_cbegin<C>::value;
554 };
555
556 // Specializations for types that look like ranges, but should be
557 // treated as values.
558 enum class Mutable { Yes, No };
559 template <Mutable IsMutable>
561 static constexpr bool is_mutable = IsMutable == Mutable::Yes;
562 static constexpr bool has_insert = false;
563 static constexpr bool has_erase = false;
564 static constexpr bool has_resize = false;
565 static constexpr bool has_rotate = false;
566 static constexpr bool has_splice = false;
567 static constexpr bool has_cbegin = true;
568 };
570 template <> struct range_traits<QString> : iterable_value<Mutable::Yes> {};
571 template <class CharT, class Traits, class Allocator>
574
575 // const T * and views are read-only
576 template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
578
579 template <typename C>
581 template <typename C>
582 [[maybe_unused]] static constexpr bool is_range_v = is_range<C>();
583
584 // Detect which options are set to override default heuristics. Since
585 // QRangeModel is not yet defined we need to delay the evaluation.
586 template <typename T> struct QRangeModelRowOptions;
587
588 template <typename T, typename = void>
590 {
591 static constexpr bool isMultiRole = false;
592 };
593
594 template <typename T>
596 : std::true_type
597 {
599 using RowCategory = decltype(rowCategory);
601 };
602
603 // Detect an ItemAccess specialization with static read/writeRole members
604 template <typename T> struct QRangeModelItemAccess;
605
606 template <typename T, typename = void>
608
609 template <typename T>
611 std::void_t<decltype(QRangeModelItemAccess<T>::readRole(std::declval<const T&>(),
612 Qt::DisplayRole)),
614 std::declval<QVariant>(),
615 Qt::DisplayRole))
616 >
617 > : std::true_type
618 {
620 static_assert(std::is_invocable_r_v<bool,
621 decltype(ItemAccess::writeRole), T&, QVariant, Qt::ItemDataRole>,
622 "The return type of the ItemAccess::writeRole implementation "
623 "needs to be convertible to a bool!");
624 static_assert(std::is_invocable_r_v<QVariant,
625 decltype(ItemAccess::readRole), const T&, Qt::ItemDataRole>,
626 "The return type of the ItemAccess::readRole implementation "
627 "needs to be convertible to QVariant!");
628 };
629
630 // Find out how many fixed elements can be retrieved from a row element.
631 // main template for simple values and ranges. Specializing for ranges
632 // is ambiguous with arrays, as they are also ranges
633 template <typename T, typename = void>
634 struct row_traits {
635 static constexpr bool is_range = is_range_v<q20::remove_cvref_t<T>>;
636 // A static size of -1 indicates dynamically sized range
637 // A static size of 0 indicates that the specified type doesn't
638 // represent static or dynamic range.
639 static constexpr int static_size = is_range ? -1 : 0;
641 static constexpr int fixed_size() { return 1; }
642 static constexpr bool hasMetaObject = false;
643 };
644
645 // Specialization for tuple-like semantics (prioritized over metaobject)
646 template <typename T>
648 {
649 static constexpr std::size_t size64 = std::tuple_size_v<T>;
650 static_assert(q20::in_range<int>(size64));
651 static constexpr int static_size = int(size64);
652
653 // are the types in a tuple all the same
654 template <std::size_t ...I>
655 static constexpr bool allSameTypes(std::index_sequence<I...>)
656 {
657 return (std::is_same_v<std::tuple_element_t<0, T>,
658 std::tuple_element_t<I, T>> && ...);
659 }
660
662 std::tuple_element_t<0, T>, void>;
663 static constexpr int fixed_size() { return 0; }
664 static constexpr bool hasMetaObject = false;
665 };
666
667 // Specialization for C arrays and std::array
668 template <typename T, std::size_t N>
669 struct row_traits<std::array<T, N>>
670 {
671 static_assert(q20::in_range<int>(N));
672 static constexpr int static_size = int(N);
673 using item_type = T;
674 static constexpr int fixed_size() { return 0; }
675 static constexpr bool hasMetaObject = false;
676 };
677
678 template <typename T, std::size_t N>
679 struct row_traits<T[N]> : row_traits<std::array<T, N>> {};
680
681 // prioritize tuple-like over metaobject
682 template <typename T>
684 {
685 static constexpr int static_size = 0;
687 static int fixed_size() {
688 if constexpr (row_category<T>::isMultiRole) {
689 return 1;
690 } else {
691 // Interpret a gadget in a list as a multi-column row item. To make
692 // a list of multi-role items, wrap it into SingleColumn.
693 static const int columnCount = []{
695 return mo.propertyCount() - mo.propertyOffset();
696 }();
697 return columnCount;
698 }
699 }
700 static constexpr bool hasMetaObject = true;
701 };
702
703 template <typename T>
704 [[maybe_unused]] static constexpr int static_size_v =
706
707 template <typename Range>
709 {
711
712 template <typename R = row_type>
713 auto newRow() -> decltype(R{}) { return R{}; }
714 };
715
716 template <typename Range>
718 {
720
721 template <typename R = row_type,
723 is_owning_or_raw_pointer<R>>, bool> = true>
724 auto newRow() -> decltype(R(new wrapped_t<R>)) {
725 if constexpr (is_any_of<R, std::shared_ptr>())
726 return std::make_shared<wrapped_t<R>>();
727 else
728 return R(new wrapped_t<R>);
729 }
730
731 template <typename R = row_type,
733 auto newRow() -> decltype(R{}) { return R{}; }
734
735 template <typename R = row_type,
737 auto deleteRow(R&& row) -> decltype(delete row) { delete row; }
738 };
739
740 template <typename Range, typename R = typename range_traits<wrapped_t<Range>>::value_type>
743
744 // Default tree traversal protocol implementation for row types that have
745 // the respective member functions. The trailing return type implicitly
746 // removes those functions that are not available.
747 template <typename Range>
749 {
750 template <typename R /*wrapped_row_type*/>
751 auto parentRow(const R& row) const -> decltype(row.parentRow())
752 {
753 return row.parentRow();
754 }
755
756 template <typename R /* = wrapped_row_type*/>
757 auto setParentRow(R &row, R* parent) -> decltype(row.setParentRow(parent))
758 {
759 row.setParentRow(parent);
760 }
761
762 template <typename R /* = wrapped_row_type*/>
763 auto childRows(const R &row) const -> decltype(row.childRows())
764 {
765 return row.childRows();
766 }
767
768 template <typename R /* = wrapped_row_type*/>
769 auto childRows(R &row) -> decltype(row.childRows())
770 {
771 return row.childRows();
772 }
773 };
774
775 template <typename P, typename R, typename = void>
777 template <typename P, typename R>
779 std::void_t<decltype(std::declval<P&>().parentRow(std::declval<wrapped_t<R>&>()))>>
780 : std::true_type {};
781
782 template <typename P, typename R, typename = void>
784 template <typename P, typename R>
786 std::void_t<decltype(std::declval<P&>().childRows(std::declval<wrapped_t<R>&>()))>>
787 : std::true_type {};
788
789 template <typename P, typename R, typename = void>
791 template <typename P, typename R>
793 std::void_t<decltype(std::declval<P&>().setParentRow(std::declval<wrapped_t<R>&>(),
794 std::declval<wrapped_t<R>*>()))>>
795 : std::true_type {};
796
797 template <typename P, typename R, typename = void>
799 template <typename P, typename R>
801 std::void_t<decltype(refTo(std::declval<P&>().childRows(std::declval<wrapped_t<R>&>()))
802 = {}) >>
803 : std::true_type {};
804
805 template <typename P, typename = void>
807 template <typename P>
808 struct protocol_newRow<P, std::void_t<decltype(std::declval<P&>().newRow())>>
809 : std::true_type {};
810
811 template <typename P, typename R, typename = void>
813 template <typename P, typename R>
815 std::void_t<decltype(std::declval<P&>().deleteRow(std::declval<R&&>()))>>
816 : std::true_type {};
817
818 template <typename Range,
819 typename Protocol = DefaultTreeProtocol<Range>,
820 typename R = typename range_traits<Range>::value_type,
821 typename = void>
823
824 template <typename Range, typename Protocol, typename R>
829
830 template <typename Range>
833 bool>;
834
835 template <typename Range, typename Protocol = DefaultTreeProtocol<Range>>
838 bool>;
839
840 template <typename Range, typename Protocol>
842 {
847 static constexpr bool is_list = static_size_v<row> == 0
849 static constexpr bool is_table = !is_list && !is_tree;
850
851 static constexpr bool has_newRow = protocol_newRow<protocol>();
852 static constexpr bool has_deleteRow = protocol_deleteRow<protocol, row>();
855
857 };
858
859 template <bool cacheProperties>
861 static constexpr bool cachesProperties = false;
862
864 };
865
866 template <>
867 struct PropertyData<true>
868 {
869 static constexpr bool cachesProperties = true;
871
873 {
874 properties.clear();
875 }
876 };
877
878 // The storage of the model data. We might store it as a pointer, or as a
879 // (copied- or moved-into) value (or smart pointer). But we always return a
880 // raw pointer.
881 template <typename ModelStorage, typename = void>
882 struct Storage
883 {
885
886 using iterator = decltype(begin(m_model));
887 using const_iterator = decltype(begin(m_model));
888 };
889
890 template <typename ModelStorage>
892 std::void_t<decltype(begin(std::declval<const ModelStorage&>()))>>
893 {
895
896 using iterator = decltype(begin(m_model));
898 };
899
900 template <typename ModelStorage, typename ItemType>
902 PropertyData<has_metaobject_v<ItemType>>
903 {
907
908 auto model() { return pointerTo(this->m_model); }
909 auto model() const { return pointerTo(this->m_model); }
910
911 template <typename Model = ModelStorage>
912 ModelData(Model &&model)
914 {}
915 };
916} // namespace QRangeModelDetails
917
918class QRangeModel;
919
921{
922private:
923 using Self = QRangeModelImplBase;
924 using QtPrivate::QQuasiVirtualInterface<Self>::Method;
925protected:
926 // Helper for calling a lambda with the element of a statically
927 // sized range (tuple or array) with a runtime index.
928 template <typename StaticContainer, typename F>
929 static auto for_element_at(StaticContainer &&container, std::size_t idx, F &&function)
930 {
931 using type = std::remove_cv_t<QRangeModelDetails::wrapped_t<StaticContainer>>;
932 static_assert(QRangeModelDetails::array_like_v<type> || QRangeModelDetails::tuple_like_v<type>,
933 "Internal error: expected an array-like or a tuple-like type");
934
935 if (QRangeModelDetails::isValid(container)) {
936 auto& ref = QRangeModelDetails::refTo(std::forward<StaticContainer>(container));
937 if constexpr (QRangeModelDetails::array_like_v<type>) {
938 Q_ASSERT(idx < std::size(ref));
939 function(ref[idx]);
940 } else {
941 constexpr size_t size = std::tuple_size_v<type>;
942 Q_ASSERT(idx < std::tuple_size_v<type>);
943 QtPrivate::applyIndexSwitch<size>(idx, [&](auto idxConstant) {
944 function(get<idxConstant>(ref));
945 });
946 }
947 }
948 }
949
950 // Get the QMetaType for a tuple-element at a runtime index.
951 // Used in the headerData implementation.
952 template <typename T>
953 static constexpr QMetaType meta_type_at(size_t idx)
954 {
955 using type = QRangeModelDetails::wrapped_t<T>;
956 if constexpr (QRangeModelDetails::array_like_v<type>) {
957 Q_UNUSED(idx);
958 return QMetaType::fromType<std::tuple_element_t<0, type>>();
959 } else {
960 constexpr auto size = std::tuple_size_v<type>;
961 Q_ASSERT(idx < size);
962 QMetaType metaType;
963 QtPrivate::applyIndexSwitch<size>(idx, [&metaType](auto idxConstant) {
964 using ElementType = std::tuple_element_t<idxConstant.value, type>;
965 metaType = QMetaType::fromType<QRangeModelDetails::wrapped_t<ElementType>>();
966 });
967 return metaType;
968 }
969 }
970
971public:
972 // overridable prototypes (quasi-pure-virtual methods)
973
975 bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
976 bool setData(const QModelIndex &index, const QVariant &data, int role);
977 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data);
978 bool clearItemData(const QModelIndex &index);
979 bool insertColumns(int column, int count, const QModelIndex &parent);
980 bool removeColumns(int column, int count, const QModelIndex &parent);
981 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn);
982 bool insertRows(int row, int count, const QModelIndex &parent);
983 bool removeRows(int row, int count, const QModelIndex &parent);
984 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow);
985
986 QModelIndex index(int row, int column, const QModelIndex &parent) const;
987 QModelIndex sibling(int row, int column, const QModelIndex &index) const;
988 int rowCount(const QModelIndex &parent) const;
989 int columnCount(const QModelIndex &parent) const;
990 Qt::ItemFlags flags(const QModelIndex &index) const;
991 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
992 QVariant data(const QModelIndex &index, int role) const;
993 QMap<int, QVariant> itemData(const QModelIndex &index) const;
995 QModelIndex parent(const QModelIndex &child) const;
996
997 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
998
999 // bindings for overriding
1000
1012
1019 using Data = Method<&Self::data>;
1023
1024 // 6.11
1026
1027 template <typename C>
1028 using MethodTemplates = std::tuple<
1029 typename C::Destroy,
1030 typename C::InvalidateCaches,
1031 typename C::SetHeaderData,
1032 typename C::SetData,
1033 typename C::SetItemData,
1034 typename C::ClearItemData,
1035 typename C::InsertColumns,
1036 typename C::RemoveColumns,
1037 typename C::MoveColumns,
1038 typename C::InsertRows,
1039 typename C::RemoveRows,
1040 typename C::MoveRows,
1041 typename C::Index,
1042 typename C::Parent,
1043 typename C::Sibling,
1044 typename C::RowCount,
1045 typename C::ColumnCount,
1046 typename C::Flags,
1047 typename C::HeaderData,
1048 typename C::Data,
1049 typename C::ItemData,
1050 typename C::RoleNames,
1051 typename C::MultiData
1052 >;
1053
1054 static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
1055 static Q_CORE_EXPORT const QRangeModelImplBase *getImplementation(const QRangeModel *model);
1056
1057private:
1059 QRangeModel *m_rangeModel;
1060
1061protected:
1062 explicit QRangeModelImplBase(QRangeModel *itemModel)
1063 : m_rangeModel(itemModel)
1064 {}
1065
1066 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
1067 inline void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to);
1068 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
1069 const QList<int> &roles);
1070 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
1071 inline void endInsertColumns();
1072 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
1073 inline void endRemoveColumns();
1074 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1075 const QModelIndex &destParent, int destRow);
1076 inline void endMoveColumns();
1077 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
1078 inline void endInsertRows();
1079 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
1080 inline void endRemoveRows();
1081 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1082 const QModelIndex &destParent, int destRow);
1083 inline void endMoveRows();
1084 inline QAbstractItemModel &itemModel();
1085 inline const QAbstractItemModel &itemModel() const;
1086
1087 // implemented in qrangemodel.cpp
1089 const QMetaObject &metaObject);
1091};
1092
1093template <typename Structure, typename Range,
1094 typename Protocol = QRangeModelDetails::table_protocol_t<Range>>
1096 : public QtPrivate::QQuasiVirtualSubclass<QRangeModelImpl<Structure, Range, Protocol>,
1099{
1100public:
1110
1114 >,
1115 typename row_traits::item_type
1116 >;
1118
1119 using const_row_reference = decltype(*std::declval<typename ModelData::const_iterator&>());
1120
1121 static_assert(!QRangeModelDetails::is_any_of<range_type, std::optional>() &&
1123 "Currently, std::optional is not supported for ranges and rows, as "
1124 "it has range semantics in c++26. Once the required behavior is clarified, "
1125 "std::optional for ranges and rows will be supported.");
1126
1127protected:
1128
1129 using Self = QRangeModelImpl<Structure, Range, Protocol>;
1131
1132 Structure& that() { return static_cast<Structure &>(*this); }
1133 const Structure& that() const { return static_cast<const Structure &>(*this); }
1134
1135 template <typename C>
1136 static constexpr int size(const C &c)
1137 {
1138 using namespace QRangeModelDetails;
1139 if (!isValid(c))
1140 return 0;
1141
1142 if constexpr (test_size<C>()) {
1143 return int(std::size(c));
1144 } else {
1145#if defined(__cpp_lib_ranges)
1146 using std::ranges::distance;
1147#else
1148 using std::distance;
1149#endif
1150 using container_type = std::conditional_t<range_traits<C>::has_cbegin,
1151 const wrapped_t<C>,
1152 wrapped_t<C>>;
1153 container_type& container = const_cast<container_type &>(refTo(c));
1154 return int(distance(std::begin(container), std::end(container)));
1155 }
1156 }
1157
1160 static constexpr bool rows_are_owning_or_raw_pointers =
1163 static constexpr bool one_dimensional_range = static_column_count == 0;
1164
1165 // A row might be a value (or range of values), or a pointer.
1166 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
1169
1170 template <typename T>
1173
1174 // A iterator type to use as the input iterator with the
1175 // range_type::insert(pos, start, end) overload if available (it is in
1176 // std::vector, but not in QList). Generates a prvalue when dereferenced,
1177 // which then gets moved into the newly constructed row, which allows us to
1178 // implement insertRows() for move-only row types.
1180 {
1184 using iterator_category = std::input_iterator_tag;
1185 using difference_type = int;
1186
1187 value_type operator*() { return impl->makeEmptyRow(parentRow); }
1188 EmptyRowGenerator &operator++() { ++n; return *this; }
1189 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1190 { return lhs.n == rhs.n; }
1191 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1192 { return !(lhs == rhs); }
1193
1195 Structure *impl = nullptr;
1196 const row_ptr parentRow = nullptr;
1197 };
1198
1199 // If we have a move-only row_type and can add/remove rows, then the range
1200 // must have an insert-from-range overload.
1203 "The range holding a move-only row-type must support insert(pos, start, end)");
1204
1205public:
1206 static constexpr bool isMutable()
1207 {
1208 return range_features::is_mutable && row_features::is_mutable
1209 && std::is_reference_v<row_reference>
1210 && Structure::is_mutable_impl;
1211 }
1212 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
1213 static constexpr bool dynamicColumns() { return static_column_count < 0; }
1214
1215 explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
1216 : Ancestor(itemModel)
1219 {
1220 }
1221
1222
1223 // static interface, called by QRangeModelImplBase
1224
1225 void invalidateCaches() { m_data.invalidateCaches(); }
1226
1227 // Not implemented
1228 bool setHeaderData(int , Qt::Orientation , const QVariant &, int ) { return false; }
1229
1230 // actual implementations
1231 QModelIndex index(int row, int column, const QModelIndex &parent) const
1232 {
1233 if (row < 0 || column < 0 || column >= columnCount(parent)
1234 || row >= rowCount(parent)) {
1235 return {};
1236 }
1237
1238 return that().indexImpl(row, column, parent);
1239 }
1240
1241 QModelIndex sibling(int row, int column, const QModelIndex &index) const
1242 {
1243 if (row == index.row() && column == index.column())
1244 return index;
1245
1246 if (column < 0 || column >= this->columnCount({}))
1247 return {};
1248
1249 if (row == index.row())
1250 return this->createIndex(row, column, index.constInternalPointer());
1251
1252 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1253 const auto siblingCount = size(that().childrenOf(parentRow));
1254 if (row < 0 || row >= int(siblingCount))
1255 return {};
1256 return this->createIndex(row, column, parentRow);
1257 }
1258
1259 Qt::ItemFlags flags(const QModelIndex &index) const
1260 {
1261 if (!index.isValid())
1262 return Qt::NoItemFlags;
1263
1264 Qt::ItemFlags f = Structure::defaultFlags();
1265
1266 if constexpr (isMutable()) {
1267 if constexpr (row_traits::hasMetaObject) {
1268 if (index.column() < row_traits::fixed_size()) {
1269 const QMetaObject mo = wrapped_row_type::staticMetaObject;
1270 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
1271 if (prop.isWritable())
1272 f |= Qt::ItemIsEditable;
1273 }
1274 } else if constexpr (static_column_count <= 0) {
1275 f |= Qt::ItemIsEditable;
1276 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
1277 // we want to know if the elements in the tuple are const; they'd always be, if
1278 // we didn't remove the const of the range first.
1279 const_row_reference row = rowData(index);
1280 row_reference mutableRow = const_cast<row_reference>(row);
1281 if (QRangeModelDetails::isValid(mutableRow)) {
1282 QRangeModelImplBase::for_element_at(mutableRow, index.column(), [&f](auto &&ref){
1283 using target_type = decltype(ref);
1284 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
1285 f &= ~Qt::ItemIsEditable;
1286 else if constexpr (std::is_lvalue_reference_v<target_type>)
1287 f |= Qt::ItemIsEditable;
1288 });
1289 } else {
1290 // If there's no usable value stored in the row, then we can't
1291 // do anything with this item.
1292 f &= ~Qt::ItemIsEditable;
1293 }
1294 }
1295 }
1296 return f;
1297 }
1298
1299 QVariant headerData(int section, Qt::Orientation orientation, int role) const
1300 {
1301 QVariant result;
1302 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
1303 || section < 0 || section >= columnCount({})) {
1304 return this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1305 }
1306
1307 if constexpr (row_traits::hasMetaObject) {
1308 if (row_traits::fixed_size() == 1) {
1309 const QMetaType metaType = QMetaType::fromType<wrapped_row_type>();
1310 result = QString::fromUtf8(metaType.name());
1311 } else if (section <= row_traits::fixed_size()) {
1312 const QMetaProperty prop = wrapped_row_type::staticMetaObject.property(
1313 section + wrapped_row_type::staticMetaObject.propertyOffset());
1314 result = QString::fromUtf8(prop.name());
1315 }
1316 } else if constexpr (static_column_count >= 1) {
1317 if constexpr (QRangeModelDetails::array_like_v<wrapped_row_type>) {
1318 return section;
1319 } else {
1320 const QMetaType metaType = QRangeModelImplBase::meta_type_at<wrapped_row_type>(section);
1321 if (metaType.isValid())
1322 result = QString::fromUtf8(metaType.name());
1323 }
1324 }
1325 if (!result.isValid())
1326 result = this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1327 return result;
1328 }
1329
1330 QVariant data(const QModelIndex &index, int role) const
1331 {
1332 if (!index.isValid())
1333 return {};
1334
1335 QModelRoleData result(role);
1336 multiData(index, result);
1337 return std::move(result.data());
1338 }
1339
1340 QMap<int, QVariant> itemData(const QModelIndex &index) const
1341 {
1342 QMap<int, QVariant> result;
1343 bool tried = false;
1344 const auto readItemData = [this, &result, &tried](const auto &value){
1345 Q_UNUSED(this);
1346 using value_type = q20::remove_cvref_t<decltype(value)>;
1347 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1348 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1349
1350 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1351 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1352 tried = true;
1353 const auto roles = this->itemModel().roleNames().keys();
1354 for (auto &role : roles) {
1355 if (role == Qt::RangeModelDataRole)
1356 continue;
1357 QVariant data = ItemAccess::readRole(value, role);
1358 if (data.isValid())
1359 result[role] = std::move(data);
1360 }
1361 } else if constexpr (multi_role()) {
1362 tried = true;
1363 if constexpr (std::is_convertible_v<value_type, decltype(result)>) {
1364 result = value;
1365 } else {
1366 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1367 Q_UNUSED(this);
1368 if constexpr (!multi_role::int_key)
1369 return this->itemModel().roleNames();
1370 else
1371 return {};
1372 }();
1373 for (auto it = std::begin(value); it != std::end(value); ++it) {
1374 const int role = [&roleNames, key = QRangeModelDetails::key(it)]() {
1375 Q_UNUSED(roleNames);
1376 if constexpr (multi_role::int_key)
1377 return int(key);
1378 else
1379 return roleNames.key(key.toUtf8(), -1);
1380 }();
1381
1382 if (role != -1 && role != Qt::RangeModelDataRole)
1383 result.insert(role, QRangeModelDetails::value(it));
1384 }
1385 }
1386 } else if constexpr (has_metaobject<value_type>) {
1387 if (row_traits::fixed_size() <= 1) {
1388 tried = true;
1389 const auto roleNames = this->itemModel().roleNames();
1390 const auto end = roleNames.keyEnd();
1391 for (auto it = roleNames.keyBegin(); it != end; ++it) {
1392 const int role = *it;
1393 if (role == Qt::RangeModelDataRole)
1394 continue;
1395 QVariant data = readRole(role, QRangeModelDetails::pointerTo(value));
1396 if (data.isValid())
1397 result[role] = std::move(data);
1398 }
1399 }
1400 }
1401 };
1402
1403 if (index.isValid()) {
1404 readAt(index, readItemData);
1405
1406 if (!tried) // no multi-role item found
1407 result = this->itemModel().QAbstractItemModel::itemData(index);
1408 }
1409 return result;
1410 }
1411
1412 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1413 {
1414 bool tried = false;
1415 readAt(index, [this, &index, roleDataSpan, &tried](const auto &value) {
1416 Q_UNUSED(this);
1417 Q_UNUSED(index);
1418 using value_type = q20::remove_cvref_t<decltype(value)>;
1419 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1420 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1421
1422 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1423 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1424 tried = true;
1425 for (auto &roleData : roleDataSpan) {
1426 if (roleData.role() == Qt::RangeModelDataRole) {
1427 // Qt QML support: "modelData" role returns the entire multi-role item.
1428 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1429 // only by value (so we take the reference).
1430 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1431 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1432 else
1433 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1434 } else {
1435 roleData.setData(ItemAccess::readRole(value, roleData.role()));
1436 }
1437 }
1438 } else if constexpr (multi_role()) {
1439 tried = true;
1440 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1441 Q_UNUSED(this);
1442 if constexpr (!multi_role::int_key)
1443 return this->itemModel().roleNames();
1444 else
1445 return {};
1446 }();
1447 using key_type = typename value_type::key_type;
1448 for (auto &roleData : roleDataSpan) {
1449 const auto &it = [&roleNames, &value, role = roleData.role()]{
1450 Q_UNUSED(roleNames);
1451 if constexpr (multi_role::int_key)
1452 return value.find(key_type(role));
1453 else
1454 return value.find(roleNames.value(role));
1455 }();
1456 if (it != QRangeModelDetails::end(value))
1457 roleData.setData(QRangeModelDetails::value(it));
1458 else
1459 roleData.clearData();
1460 }
1461 } else if constexpr (has_metaobject<value_type>) {
1462 if (row_traits::fixed_size() <= 1) {
1463 tried = true;
1464 for (auto &roleData : roleDataSpan) {
1465 if (roleData.role() == Qt::RangeModelDataRole) {
1466 // Qt QML support: "modelData" role returns the entire multi-role item.
1467 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1468 // only by value (so we take the reference).
1469 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1470 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1471 else
1472 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1473 } else {
1474 roleData.setData(readRole(roleData.role(),
1475 QRangeModelDetails::pointerTo(value)));
1476 }
1477 }
1478 } else if (index.column() <= row_traits::fixed_size()) {
1479 tried = true;
1480 for (auto &roleData : roleDataSpan) {
1481 const int role = roleData.role();
1482 if (role == Qt::DisplayRole || role == Qt::EditRole) {
1483 roleData.setData(readProperty(index.column(),
1484 QRangeModelDetails::pointerTo(value)));
1485 } else {
1486 roleData.clearData();
1487 }
1488 }
1489 }
1490 } else {
1491 tried = true;
1492 for (auto &roleData : roleDataSpan) {
1493 const int role = roleData.role();
1494 if (role == Qt::DisplayRole || role == Qt::EditRole
1495 || role == Qt::RangeModelDataRole) {
1496 roleData.setData(read(value));
1497 } else {
1498 roleData.clearData();
1499 }
1500 }
1501 }
1502 });
1503
1504 Q_ASSERT(tried);
1505 }
1506
1507 bool setData(const QModelIndex &index, const QVariant &data, int role)
1508 {
1509 if (!index.isValid())
1510 return false;
1511
1512 bool success = false;
1513 if constexpr (isMutable()) {
1514 auto emitDataChanged = qScopeGuard([&success, this, &index, role]{
1515 if (success) {
1516 Q_EMIT this->dataChanged(index, index,
1517 role == Qt::EditRole || role == Qt::RangeModelDataRole
1518 ? QList<int>{} : QList<int>{role});
1519 }
1520 });
1521
1522 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1523 using value_type = q20::remove_cvref_t<decltype(target)>;
1524 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1525 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1526
1527 auto setRangeModelDataRole = [&target, &data]{
1528 auto &targetRef = QRangeModelDetails::refTo(target);
1529 constexpr auto targetMetaType = QMetaType::fromType<value_type>();
1530 const auto dataMetaType = data.metaType();
1531 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
1532 // This covers move-only types, but also polymorph types like QObject.
1533 // We don't support replacing a stored object with another one, as this
1534 // makes object ownership very messy.
1535 // fall through to error handling
1536 } else if constexpr (QRangeModelDetails::is_wrapped<value_type>()) {
1537 if (QRangeModelDetails::isValid(targetRef)) {
1538 // we need to get a wrapped value type out of the QVariant, which
1539 // might carry a pointer. We have to try all alternatives.
1540 if (const auto mt = QMetaType::fromType<wrapped_value_type>();
1541 data.canConvert(mt)) {
1542 targetRef = data.value<wrapped_value_type>();
1543 return true;
1544 } else if (const auto mtp = QMetaType::fromType<wrapped_value_type *>();
1545 data.canConvert(mtp)) {
1546 targetRef = *data.value<wrapped_value_type *>();
1547 return true;
1548 }
1549 }
1550 } else if (targetMetaType == dataMetaType) {
1551 targetRef = data.value<value_type>();
1552 return true;
1553 } else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1554 targetRef = *data.value<value_type *>();
1555 return true;
1556 }
1557#ifndef QT_NO_DEBUG
1558 qCritical("Not able to assign %s to %s",
1559 qPrintable(QDebug::toString(data)), targetMetaType.name());
1560#endif
1561 return false;
1562 };
1563
1564 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1565 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1566 if (role == Qt::RangeModelDataRole)
1567 return setRangeModelDataRole();
1568 return ItemAccess::writeRole(target, data, role);
1569 } if constexpr (has_metaobject<value_type>) {
1570 if (row_traits::fixed_size() <= 1) { // multi-role value
1571 if (role == Qt::RangeModelDataRole)
1572 return setRangeModelDataRole();
1573 return writeRole(role, QRangeModelDetails::pointerTo(target), data);
1574 } else if (column <= row_traits::fixed_size() // multi-column
1575 && (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::RangeModelDataRole)) {
1576 return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
1577 }
1578 } else if constexpr (multi_role::value) {
1579 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1580 // If there is an entry for EditRole, overwrite that; otherwise,
1581 // set the entry for DisplayRole.
1582 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1583 Q_UNUSED(this);
1584 if constexpr (!multi_role::int_key)
1585 return this->itemModel().roleNames();
1586 else
1587 return {};
1588 }();
1589 if (role == Qt::EditRole) {
1590 if constexpr (multi_role::int_key) {
1591 if (target.find(roleToSet) == target.end())
1592 roleToSet = Qt::DisplayRole;
1593 } else {
1594 if (target.find(roleNames.value(roleToSet)) == target.end())
1595 roleToSet = Qt::DisplayRole;
1596 }
1597 }
1598 if constexpr (multi_role::int_key)
1599 return write(target[roleToSet], data);
1600 else
1601 return write(target[roleNames.value(roleToSet)], data);
1602 } else if (role == Qt::DisplayRole || role == Qt::EditRole
1603 || role == Qt::RangeModelDataRole) {
1604 return write(target, data);
1605 }
1606 return false;
1607 };
1608
1609 success = writeAt(index, writeData);
1610 }
1611 return success;
1612 }
1613
1614 template <typename LHS, typename RHS>
1615 void updateTarget(LHS &org, RHS &&copy) noexcept
1616 {
1617 if constexpr (std::is_pointer_v<RHS>) {
1618 return;
1619 } else {
1620 using std::swap;
1621 if constexpr (std::is_assignable_v<LHS, RHS>)
1622 org = std::forward<RHS>(copy);
1623 else
1624 swap(org, copy);
1625 }
1626 }
1627 template <typename LHS, typename RHS>
1628 void updateTarget(LHS *org, RHS &&copy) noexcept
1629 {
1630 updateTarget(*org, std::forward<RHS>(copy));
1631 }
1632
1633 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1634 {
1635 if (!index.isValid() || data.isEmpty())
1636 return false;
1637
1638 bool success = false;
1639 if constexpr (isMutable()) {
1640 auto emitDataChanged = qScopeGuard([&success, this, &index, &data]{
1641 if (success)
1642 Q_EMIT this->dataChanged(index, index, data.keys());
1643 });
1644
1645 bool tried = false;
1646 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
1647 Q_UNUSED(this);
1648 using value_type = q20::remove_cvref_t<decltype(target)>;
1649 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1650 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1651
1652 // transactional: if possible, modify a copy and only
1653 // update target if all values from data could be stored.
1654 auto makeCopy = [](const value_type &original){
1655 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>)
1656 return QRangeModelDetails::pointerTo(original); // no transaction support
1657 else if constexpr (std::is_pointer_v<decltype(original)>)
1658 return *original;
1659 else if constexpr (std::is_copy_assignable_v<value_type>)
1660 return original;
1661 else
1662 return QRangeModelDetails::pointerTo(original);
1663 };
1664
1665 const auto roleNames = this->itemModel().roleNames();
1666
1667 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1668 tried = true;
1669 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1670 const auto roles = roleNames.keys();
1671 auto targetCopy = makeCopy(target);
1672 for (int role : roles) {
1673 if (!ItemAccess::writeRole(QRangeModelDetails::refTo(targetCopy),
1674 data.value(role), role)) {
1675 return false;
1676 }
1677 }
1678 updateTarget(target, std::move(targetCopy));
1679 return true;
1680 } else if constexpr (multi_role()) {
1681 using key_type = typename value_type::key_type;
1682 tried = true;
1683 const auto roleName = [&roleNames](int role) {
1684 return roleNames.value(role);
1685 };
1686
1687 // transactional: only update target if all values from data
1688 // can be stored. Storing never fails with int-keys.
1689 if constexpr (!multi_role::int_key)
1690 {
1691 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
1692 [&roleName](int role) { return roleName(role).isEmpty(); }
1693 );
1694
1695 if (invalid != data.keyEnd()) {
1696#ifndef QT_NO_DEBUG
1697 qWarning("No role name set for %d", *invalid);
1698#endif
1699 return false;
1700 }
1701 }
1702
1703 for (auto &&[role, value] : data.asKeyValueRange()) {
1704 if constexpr (multi_role::int_key)
1705 target[static_cast<key_type>(role)] = value;
1706 else
1707 target[QString::fromUtf8(roleName(role))] = value;
1708 }
1709 return true;
1710 } else if constexpr (has_metaobject<value_type>) {
1711 if (row_traits::fixed_size() <= 1) {
1712 tried = true;
1713 auto targetCopy = makeCopy(target);
1714 for (auto &&[role, value] : data.asKeyValueRange()) {
1715 if (role == Qt::RangeModelDataRole)
1716 continue;
1717 if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
1718 const QByteArray roleName = roleNames.value(role);
1719#ifndef QT_NO_DEBUG
1720 qWarning("Failed to write value '%s' to role '%s'",
1721 qPrintable(QDebug::toString(value)), roleName.data());
1722#endif
1723 return false;
1724 }
1725 }
1726 updateTarget(target, std::move(targetCopy));
1727 return true;
1728 }
1729 }
1730 return false;
1731 };
1732
1733 success = writeAt(index, writeItemData);
1734
1735 if (!tried) {
1736 // setItemData will emit the dataChanged signal
1737 Q_ASSERT(!success);
1738 emitDataChanged.dismiss();
1739 success = this->itemModel().QAbstractItemModel::setItemData(index, data);
1740 }
1741 }
1742 return success;
1743 }
1744
1745 bool clearItemData(const QModelIndex &index)
1746 {
1747 if (!index.isValid())
1748 return false;
1749
1750 bool success = false;
1751 if constexpr (isMutable()) {
1752 auto emitDataChanged = qScopeGuard([&success, this, &index]{
1753 if (success)
1754 Q_EMIT this->dataChanged(index, index, {});
1755 });
1756
1757 auto clearData = [column = index.column()](auto &&target) {
1758 if constexpr (row_traits::hasMetaObject) {
1759 if (row_traits::fixed_size() <= 1) {
1760 // multi-role object/gadget: reset all properties
1761 return resetProperty(-1, QRangeModelDetails::pointerTo(target));
1762 } else if (column <= row_traits::fixed_size()) {
1763 return resetProperty(column, QRangeModelDetails::pointerTo(target));
1764 }
1765 } else { // normal structs, values, associative containers
1766 target = {};
1767 return true;
1768 }
1769 return false;
1770 };
1771
1772 success = writeAt(index, clearData);
1773 }
1774 return success;
1775 }
1776
1778 {
1779 // will be 'void' if columns don't all have the same type
1780 using item_type = typename row_traits::item_type;
1781 if constexpr (QRangeModelDetails::has_metaobject_v<item_type>) {
1782 return QRangeModelImplBase::roleNamesForMetaObject(this->itemModel(),
1783 QRangeModelDetails::wrapped_t<item_type>::staticMetaObject);
1784 } else if constexpr (std::negation_v<std::disjunction<std::is_void<item_type>,
1785 QRangeModelDetails::is_multi_role<item_type>>>) {
1786 return QRangeModelImplBase::roleNamesForSimpleType();
1787 }
1788
1789 return this->itemModel().QAbstractItemModel::roleNames();
1790 }
1791
1792
1793 template <typename InsertFn>
1794 bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
1795 {
1796 if (count == 0)
1797 return false;
1798 range_type * const children = childRange(parent);
1799 if (!children)
1800 return false;
1801
1802 this->beginInsertColumns(parent, column, column + count - 1);
1803
1804 for (auto &child : *children) {
1805 auto it = QRangeModelDetails::pos(child, column);
1806 (void)insertFn(QRangeModelDetails::refTo(child), it, count);
1807 }
1808
1809 this->endInsertColumns();
1810
1811 return true;
1812 }
1813
1814 bool insertColumns(int column, int count, const QModelIndex &parent)
1815 {
1816 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
1817 return doInsertColumns(column, count, parent, [](auto &row, auto it, int n){
1818 row.insert(it, n, {});
1819 return true;
1820 });
1821 } else {
1822 return false;
1823 }
1824 }
1825
1826 bool removeColumns(int column, int count, const QModelIndex &parent)
1827 {
1828 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
1829 if (column < 0 || column + count > columnCount(parent))
1830 return false;
1831
1832 range_type * const children = childRange(parent);
1833 if (!children)
1834 return false;
1835
1836 this->beginRemoveColumns(parent, column, column + count - 1);
1837 for (auto &child : *children) {
1838 const auto start = QRangeModelDetails::pos(child, column);
1839 QRangeModelDetails::refTo(child).erase(start, std::next(start, count));
1840 }
1841 this->endRemoveColumns();
1842 return true;
1843 }
1844 return false;
1845 }
1846
1847 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1848 const QModelIndex &destParent, int destColumn)
1849 {
1850 // we only support moving columns within the same parent
1851 if (sourceParent != destParent)
1852 return false;
1853 if constexpr (isMutable() && (row_features::has_rotate || row_features::has_splice)) {
1854 if (!Structure::canMoveColumns(sourceParent, destParent))
1855 return false;
1856
1857 if constexpr (dynamicColumns()) {
1858 // we only support ranges as columns, as other types might
1859 // not have the same data type across all columns
1860 range_type * const children = childRange(sourceParent);
1861 if (!children)
1862 return false;
1863
1864 if (!this->beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
1865 destParent, destColumn)) {
1866 return false;
1867 }
1868
1869 for (auto &child : *children)
1870 QRangeModelDetails::rotate(child, sourceColumn, count, destColumn);
1871
1872 this->endMoveColumns();
1873 return true;
1874 }
1875 }
1876 return false;
1877 }
1878
1879 template <typename InsertFn>
1880 bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
1881 {
1882 range_type *children = childRange(parent);
1883 if (!children)
1884 return false;
1885
1886 this->beginInsertRows(parent, row, row + count - 1);
1887
1888 row_ptr parentRow = parent.isValid()
1889 ? QRangeModelDetails::pointerTo(this->rowData(parent))
1890 : nullptr;
1891 (void)std::forward<InsertFn>(insertFn)(*children, parentRow, row, count);
1892
1893 // fix the parent in all children of the modified row, as the
1894 // references back to the parent might have become invalid.
1895 that().resetParentInChildren(children);
1896
1897 this->endInsertRows();
1898
1899 return true;
1900 }
1901
1902 bool insertRows(int row, int count, const QModelIndex &parent)
1903 {
1904 if constexpr (canInsertRows()) {
1905 return doInsertRows(row, count, parent,
1906 [this](range_type &children, row_ptr parentRow, int r, int n){
1907 EmptyRowGenerator generator{0, &that(), parentRow};
1908
1909 const auto pos = QRangeModelDetails::pos(children, r);
1910 if constexpr (range_features::has_insert_range) {
1911 children.insert(pos, std::move(generator), EmptyRowGenerator{n});
1912 } else if constexpr (rows_are_owning_or_raw_pointers) {
1913 auto start = children.insert(pos, n, nullptr); // MSVC doesn't like row_type{}
1914 std::copy(std::move(generator), EmptyRowGenerator{n}, start);
1915 } else {
1916 children.insert(pos, n, std::move(*generator));
1917 }
1918 return true;
1919 });
1920 } else {
1921 return false;
1922 }
1923 }
1924
1925 bool removeRows(int row, int count, const QModelIndex &parent = {})
1926 {
1927 if constexpr (canRemoveRows()) {
1928 const int prevRowCount = rowCount(parent);
1929 if (row < 0 || row + count > prevRowCount)
1930 return false;
1931
1932 range_type *children = childRange(parent);
1933 if (!children)
1934 return false;
1935
1936 this->beginRemoveRows(parent, row, row + count - 1);
1937 [[maybe_unused]] bool callEndRemoveColumns = false;
1938 if constexpr (dynamicColumns()) {
1939 // if we remove the last row in a dynamic model, then we no longer
1940 // know how many columns we should have, so they will be reported as 0.
1941 if (prevRowCount == count) {
1942 if (const int columns = columnCount(parent)) {
1943 callEndRemoveColumns = true;
1944 this->beginRemoveColumns(parent, 0, columns - 1);
1945 }
1946 }
1947 }
1948 { // erase invalidates iterators
1949 const auto begin = QRangeModelDetails::pos(children, row);
1950 const auto end = std::next(begin, count);
1951 that().deleteRemovedRows(begin, end);
1952 children->erase(begin, end);
1953 }
1954 // fix the parent in all children of the modified row, as the
1955 // references back to the parent might have become invalid.
1956 that().resetParentInChildren(children);
1957
1958 if constexpr (dynamicColumns()) {
1959 if (callEndRemoveColumns) {
1960 Q_ASSERT(columnCount(parent) == 0);
1961 this->endRemoveColumns();
1962 }
1963 }
1964 this->endRemoveRows();
1965 return true;
1966 } else {
1967 return false;
1968 }
1969 }
1970
1971 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
1972 const QModelIndex &destParent, int destRow)
1973 {
1974 if constexpr (isMutable() && (range_features::has_rotate || range_features::has_splice)) {
1975 if (!Structure::canMoveRows(sourceParent, destParent))
1976 return false;
1977
1978 if (sourceParent != destParent) {
1979 return that().moveRowsAcross(sourceParent, sourceRow, count,
1980 destParent, destRow);
1981 }
1982
1983 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
1984 || sourceRow < 0 || sourceRow + count - 1 >= this->rowCount(sourceParent)
1985 || destRow < 0 || destRow > this->rowCount(destParent)) {
1986 return false;
1987 }
1988
1989 range_type *source = childRange(sourceParent);
1990 // moving within the same range
1991 if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
1992 return false;
1993
1994 QRangeModelDetails::rotate(source, sourceRow, count, destRow);
1995
1996 that().resetParentInChildren(source);
1997
1998 this->endMoveRows();
1999 return true;
2000 } else {
2001 return false;
2002 }
2003 }
2004
2005 const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2006 protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2007
2008 QModelIndex parent(const QModelIndex &child) const { return that().parentImpl(child); }
2009
2010 int rowCount(const QModelIndex &parent) const { return that().rowCountImpl(parent); }
2011
2012 int columnCount(const QModelIndex &parent) const { return that().columnCountImpl(parent); }
2013
2014 void destroy() { delete std::addressof(that()); }
2015
2016 template <typename BaseMethod, typename BaseMethod::template Overridden<Self> overridden>
2017 using Override = typename Ancestor::template Override<BaseMethod, overridden>;
2018
2027
2042
2044
2045protected:
2047 {
2049 }
2050
2052 {
2053 // We delete row objects if we are not operating on a reference or pointer
2054 // to a range, as in that case, the owner of the referenced/pointed to
2055 // range also owns the row entries.
2056 // ### Problem: if we get a copy of a range (no matter if shared or not),
2057 // then adding rows will create row objects in the model's copy, and the
2058 // client can never delete those. But copied rows will be the same pointer,
2059 // which we must not delete (as we didn't create them).
2060
2061 static constexpr bool modelCopied = !QRangeModelDetails::is_wrapped<Range>() &&
2062 (std::is_reference_v<Range> || std::is_const_v<std::remove_reference_t<Range>>);
2063
2064 static constexpr bool modelShared = QRangeModelDetails::is_any_shared_ptr<Range>();
2065
2066 static constexpr bool default_row_deleter = protocol_traits::is_default &&
2067 protocol_traits::has_deleteRow;
2068
2069 static constexpr bool ambiguousRowOwnership = (modelCopied || modelShared) &&
2070 rows_are_raw_pointers && default_row_deleter;
2071
2072 static_assert(!ambiguousRowOwnership,
2073 "Using of copied and shared tree and table models with rows as raw pointers, "
2074 "and the default protocol is not allowed due to ambiguity of rows ownership. "
2075 "Move the model in, use another row type, or implement a custom tree protocol.");
2076
2077 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
2078 && !QRangeModelDetails::is_any_of<Range, std::reference_wrapper>()) {
2079 const auto begin = QRangeModelDetails::begin(*m_data.model());
2080 const auto end = QRangeModelDetails::end(*m_data.model());
2081 that().deleteRemovedRows(begin, end);
2082 }
2083 }
2084
2085 static constexpr bool canInsertRows()
2086 {
2087 if constexpr (dynamicColumns() && !row_features::has_resize) {
2088 // If we operate on dynamic columns and cannot resize a newly
2089 // constructed row, then we cannot insert.
2090 return false;
2091 } else if constexpr (!protocol_traits::has_newRow) {
2092 // We also cannot insert if we cannot create a new row element
2093 return false;
2094 } else if constexpr (!range_features::has_insert_range
2095 && !std::is_copy_constructible_v<row_type>) {
2096 // And if the row is a move-only type, then the range needs to be
2097 // backed by a container that can move-insert default-constructed
2098 // row elements.
2099 return false;
2100 } else {
2101 return Structure::canInsertRowsImpl();
2102 }
2103 }
2104
2105 static constexpr bool canRemoveRows()
2106 {
2107 return Structure::canRemoveRowsImpl();
2108 }
2109
2110 template <typename F>
2111 bool writeAt(const QModelIndex &index, F&& writer)
2112 {
2113 bool result = false;
2114 row_reference row = rowData(index);
2115
2116 if constexpr (one_dimensional_range) {
2117 result = writer(row);
2118 } else if (QRangeModelDetails::isValid(row)) {
2119 if constexpr (dynamicColumns()) {
2120 result = writer(*QRangeModelDetails::pos(row, index.column()));
2121 } else {
2122 QRangeModelImplBase::for_element_at(row, index.column(), [&writer, &result](auto &&target) {
2123 using target_type = decltype(target);
2124 // we can only assign to an lvalue reference
2125 if constexpr (std::is_lvalue_reference_v<target_type>
2126 && !std::is_const_v<std::remove_reference_t<target_type>>) {
2127 result = writer(std::forward<target_type>(target));
2128 }
2129 });
2130 }
2131 }
2132
2133 return result;
2134 }
2135
2136 template <typename F>
2137 void readAt(const QModelIndex &index, F&& reader) const {
2138 const_row_reference row = rowData(index);
2139 if constexpr (one_dimensional_range) {
2140 return reader(row);
2141 } else if (QRangeModelDetails::isValid(row)) {
2142 if constexpr (dynamicColumns())
2143 reader(*QRangeModelDetails::pos(row, index.column()));
2144 else
2145 QRangeModelImplBase::for_element_at(row, index.column(), std::forward<F>(reader));
2146 }
2147 }
2148
2149 template <typename Value>
2150 static QVariant read(const Value &value)
2151 {
2152 if constexpr (std::is_constructible_v<QVariant, Value>)
2153 return QVariant(value);
2154 else
2155 return QVariant::fromValue(value);
2156 }
2157 template <typename Value>
2158 static QVariant read(Value *value)
2159 {
2160 if (value) {
2161 if constexpr (std::is_constructible_v<QVariant, Value *>)
2162 return QVariant(value);
2163 else
2164 return read(*value);
2165 }
2166 return {};
2167 }
2168
2169 template <typename Target>
2170 static bool write(Target &target, const QVariant &value)
2171 {
2172 using Type = std::remove_reference_t<Target>;
2173 if constexpr (std::is_constructible_v<Target, QVariant>) {
2174 target = value;
2175 return true;
2176 } else if (value.canConvert<Type>()) {
2177 target = value.value<Type>();
2178 return true;
2179 }
2180 return false;
2181 }
2182 template <typename Target>
2183 static bool write(Target *target, const QVariant &value)
2184 {
2185 if (target)
2186 return write(*target, value);
2187 return false;
2188 }
2189
2190 template <typename ItemType>
2192 {
2193 struct {
2194 operator QMetaProperty() const {
2195 const QByteArray roleName = that.itemModel().roleNames().value(role);
2196 const QMetaObject &mo = ItemType::staticMetaObject;
2197 if (const int index = mo.indexOfProperty(roleName.data());
2198 index >= 0) {
2199 return mo.property(index);
2200 }
2201 return {};
2202 }
2203 const QRangeModelImpl &that;
2204 const int role;
2205 } findProperty{*this, role};
2206
2207 if constexpr (ModelData::cachesProperties)
2208 return *m_data.properties.tryEmplace(role, findProperty).iterator;
2209 else
2210 return findProperty;
2211 }
2212
2213 template <typename ItemType>
2214 QVariant readRole(int role, ItemType *gadget) const
2215 {
2216 using item_type = std::remove_pointer_t<ItemType>;
2217 QVariant result;
2218 QMetaProperty prop = roleProperty<item_type>(role);
2219 if (!prop.isValid() && role == Qt::EditRole)
2220 prop = roleProperty<item_type>(Qt::DisplayRole);
2221
2222 if (prop.isValid())
2223 result = readProperty(prop, gadget);
2224 return result;
2225 }
2226
2227 template <typename ItemType>
2228 QVariant readRole(int role, const ItemType &gadget) const
2229 {
2230 return readRole(role, &gadget);
2231 }
2232
2233 template <typename ItemType>
2234 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
2235 {
2236 if constexpr (std::is_base_of_v<QObject, ItemType>)
2237 return prop.read(gadget);
2238 else
2239 return prop.readOnGadget(gadget);
2240 }
2241 template <typename ItemType>
2242 static QVariant readProperty(int property, ItemType *gadget)
2243 {
2244 using item_type = std::remove_pointer_t<ItemType>;
2245 const QMetaObject &mo = item_type::staticMetaObject;
2246 const QMetaProperty prop = mo.property(property + mo.propertyOffset());
2247 return readProperty(prop, gadget);
2248 }
2249
2250 template <typename ItemType>
2251 static QVariant readProperty(int property, const ItemType &gadget)
2252 {
2253 return readProperty(property, &gadget);
2254 }
2255
2256 template <typename ItemType>
2257 bool writeRole(int role, ItemType *gadget, const QVariant &data)
2258 {
2259 using item_type = std::remove_pointer_t<ItemType>;
2260 auto prop = roleProperty<item_type>(role);
2261 if (!prop.isValid() && role == Qt::EditRole)
2262 prop = roleProperty<item_type>(Qt::DisplayRole);
2263
2264 return prop.isValid() ? writeProperty(prop, gadget, data) : false;
2265 }
2266
2267 template <typename ItemType>
2268 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
2269 {
2270 return writeRole(role, &gadget, data);
2271 }
2272
2273 template <typename ItemType>
2274 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
2275 {
2276 if constexpr (std::is_base_of_v<QObject, ItemType>)
2277 return prop.write(gadget, data);
2278 else
2279 return prop.writeOnGadget(gadget, data);
2280 }
2281 template <typename ItemType>
2282 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
2283 {
2284 using item_type = std::remove_pointer_t<ItemType>;
2285 const QMetaObject &mo = item_type::staticMetaObject;
2286 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
2287 }
2288
2289 template <typename ItemType>
2290 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
2291 {
2292 return writeProperty(property, &gadget, data);
2293 }
2294
2295 template <typename ItemType>
2296 static bool resetProperty(int property, ItemType *object)
2297 {
2298 using item_type = std::remove_pointer_t<ItemType>;
2299 const QMetaObject &mo = item_type::staticMetaObject;
2300 bool success = true;
2301 if (property == -1) {
2302 // reset all properties
2303 if constexpr (std::is_base_of_v<QObject, item_type>) {
2304 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
2305 success = writeProperty(mo.property(p), object, {}) && success;
2306 } else { // reset a gadget by assigning a default-constructed
2307 *object = {};
2308 }
2309 } else {
2310 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
2311 }
2312 return success;
2313 }
2314
2315 template <typename ItemType>
2316 static bool resetProperty(int property, ItemType &&object)
2317 {
2318 return resetProperty(property, &object);
2319 }
2320
2321 // helpers
2322 const_row_reference rowData(const QModelIndex &index) const
2323 {
2324 Q_ASSERT(index.isValid());
2325 return that().rowDataImpl(index);
2326 }
2327
2328 row_reference rowData(const QModelIndex &index)
2329 {
2330 Q_ASSERT(index.isValid());
2331 return that().rowDataImpl(index);
2332 }
2333
2334 const range_type *childRange(const QModelIndex &index) const
2335 {
2336 if (!index.isValid())
2337 return m_data.model();
2338 if (index.column()) // only items at column 0 can have children
2339 return nullptr;
2340 return that().childRangeImpl(index);
2341 }
2342
2343 range_type *childRange(const QModelIndex &index)
2344 {
2345 if (!index.isValid())
2346 return m_data.model();
2347 if (index.column()) // only items at column 0 can have children
2348 return nullptr;
2349 return that().childRangeImpl(index);
2350 }
2351
2352
2354};
2355
2356// Implementations that depends on the model structure (flat vs tree) that will
2357// be specialized based on a protocol type. The main template implements tree
2358// support through a protocol type.
2359template <typename Range, typename Protocol>
2361 : public QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
2362{
2363 using Base = QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2364 friend class QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2365
2366 using range_type = typename Base::range_type;
2367 using range_features = typename Base::range_features;
2368 using row_type = typename Base::row_type;
2369 using row_ptr = typename Base::row_ptr;
2370 using const_row_ptr = typename Base::const_row_ptr;
2371
2372 using tree_traits = typename Base::protocol_traits;
2373 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
2374
2375 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
2376 QRangeModelDetails::is_smart_ptr<row_type>() ||
2377 QRangeModelDetails::is_any_of<row_type, std::reference_wrapper>();
2378 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
2379
2380public:
2381 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
2382 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
2383 {};
2384
2385protected:
2386 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
2387 {
2388 if (!parent.isValid())
2389 return this->createIndex(row, column);
2390 // only items at column 0 can have children
2391 if (parent.column())
2392 return QModelIndex();
2393
2394 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
2395 const auto &parentSiblings = childrenOf(grandParent);
2396 const auto it = QRangeModelDetails::pos(parentSiblings, parent.row());
2397 return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
2398 }
2399
2400 QModelIndex parentImpl(const QModelIndex &child) const
2401 {
2402 if (!child.isValid())
2403 return {};
2404
2405 // no pointer to parent row - no parent
2406 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
2407 if (!parentRow)
2408 return {};
2409
2410 // get the siblings of the parent via the grand parent
2411 auto &&grandParent = this->protocol().parentRow(QRangeModelDetails::refTo(parentRow));
2412 const range_type &parentSiblings = childrenOf(QRangeModelDetails::pointerTo(grandParent));
2413 // find the index of parentRow
2414 const auto begin = QRangeModelDetails::begin(parentSiblings);
2415 const auto end = QRangeModelDetails::end(parentSiblings);
2416 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
2417 return QRangeModelDetails::pointerTo(std::forward<decltype(s)>(s)) == parentRow;
2418 });
2419 if (it != end)
2420 return this->createIndex(std::distance(begin, it), 0,
2421 QRangeModelDetails::pointerTo(grandParent));
2422 return {};
2423 }
2424
2425 int rowCountImpl(const QModelIndex &parent) const
2426 {
2427 return Base::size(this->childRange(parent));
2428 }
2429
2430 int columnCountImpl(const QModelIndex &) const
2431 {
2432 // all levels of a tree have to have the same, static, column count
2433 if constexpr (Base::one_dimensional_range)
2434 return 1;
2435 else
2436 return Base::static_column_count; // if static_column_count is -1, static assert fires
2437 }
2438
2439 static constexpr Qt::ItemFlags defaultFlags()
2440 {
2441 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2442 }
2443
2444 static constexpr bool canInsertRowsImpl()
2445 {
2446 // We must not insert rows if we cannot adjust the parents of the
2447 // children of the following rows. We don't have to do that if the
2448 // range operates on pointers.
2449 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2450 && Base::dynamicRows() && range_features::has_insert;
2451 }
2452
2453 static constexpr bool canRemoveRowsImpl()
2454 {
2455 // We must not remove rows if we cannot adjust the parents of the
2456 // children of the following rows. We don't have to do that if the
2457 // range operates on pointers.
2458 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2459 && Base::dynamicRows() && range_features::has_erase;
2460 }
2461
2462 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
2463 {
2464 return true;
2465 }
2466
2467 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
2468 {
2469 return true;
2470 }
2471
2472 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
2473 const QModelIndex &destParent, int destRow)
2474 {
2475 // If rows are pointers, then reference to the parent row don't
2476 // change, so we can move them around freely. Otherwise we need to
2477 // be able to explicitly update the parent pointer.
2478 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
2479 return false;
2480 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
2481 return false;
2482 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
2483 destParent, destRow)) {
2484 return false;
2485 }
2486
2487 range_type *source = this->childRange(sourceParent);
2488 range_type *destination = this->childRange(destParent);
2489
2490 // If we can insert data from another range into, then
2491 // use that to move the old data over.
2492 const auto destStart = QRangeModelDetails::pos(destination, destRow);
2493 if constexpr (range_features::has_insert_range) {
2494 const auto sourceStart = QRangeModelDetails::pos(*source, sourceRow);
2495 const auto sourceEnd = std::next(sourceStart, count);
2496
2497 destination->insert(destStart, std::move_iterator(sourceStart),
2498 std::move_iterator(sourceEnd));
2499 } else if constexpr (std::is_copy_constructible_v<row_type>) {
2500 // otherwise we have to make space first, and copy later.
2501 destination->insert(destStart, count, row_type{});
2502 }
2503
2504 row_ptr parentRow = destParent.isValid()
2505 ? QRangeModelDetails::pointerTo(this->rowData(destParent))
2506 : nullptr;
2507
2508 // if the source's parent was already inside the new parent row,
2509 // then the source row might have become invalid, so reset it.
2510 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
2511 if (sourceParent.row() < destRow) {
2512 source = this->childRange(sourceParent);
2513 } else {
2514 // the source parent moved down within destination
2515 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
2516 sourceParent.internalPointer()));
2517 }
2518 }
2519
2520 // move the data over and update the parent pointer
2521 {
2522 const auto writeStart = QRangeModelDetails::pos(destination, destRow);
2523 const auto writeEnd = std::next(writeStart, count);
2524 const auto sourceStart = QRangeModelDetails::pos(source, sourceRow);
2525 const auto sourceEnd = std::next(sourceStart, count);
2526
2527 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
2528 // move data over if not already done, otherwise
2529 // only fix the parent pointer
2530 if constexpr (!range_features::has_insert_range)
2531 *write = std::move(*read);
2532 this->protocol().setParentRow(QRangeModelDetails::refTo(*write), parentRow);
2533 }
2534 // remove the old rows from the source parent
2535 source->erase(sourceStart, sourceEnd);
2536 }
2537
2538 // Fix the parent pointers in children of both source and destination
2539 // ranges, as the references to the entries might have become invalid.
2540 // We don't have to do that if the rows are pointers, as in that case
2541 // the references to the entries are stable.
2542 resetParentInChildren(destination);
2544
2545 this->endMoveRows();
2546 return true;
2547 }
2548
2549 auto makeEmptyRow(row_ptr parentRow)
2550 {
2551 // tree traversal protocol: if we are here, then it must be possible
2552 // to change the parent of a row.
2553 static_assert(tree_traits::has_setParentRow);
2554 row_type empty_row = this->protocol().newRow();
2555 if (QRangeModelDetails::isValid(empty_row) && parentRow)
2556 this->protocol().setParentRow(QRangeModelDetails::refTo(empty_row), parentRow);
2557 return empty_row;
2558 }
2559
2560 template <typename It, typename Sentinel>
2561 void deleteRemovedRows(It &&begin, Sentinel &&end)
2562 {
2563 if constexpr (tree_traits::has_deleteRow) {
2564 for (auto it = begin; it != end; ++it) {
2565 if constexpr (Base::isMutable()) {
2566 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
2567 if (QRangeModelDetails::isValid(children)) {
2568 deleteRemovedRows(QRangeModelDetails::begin(children),
2569 QRangeModelDetails::end(children));
2570 QRangeModelDetails::refTo(children) = range_type{ };
2571 }
2572 }
2573
2574 this->protocol().deleteRow(std::move(*it));
2575 }
2576 }
2577 }
2578
2579 void resetParentInChildren(range_type *children)
2580 {
2581 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
2582 const auto begin = QRangeModelDetails::begin(*children);
2583 const auto end = QRangeModelDetails::end(*children);
2584 for (auto it = begin; it != end; ++it) {
2585 decltype(auto) maybeChildren = this->protocol().childRows(*it);
2586 if (QRangeModelDetails::isValid(maybeChildren)) {
2587 auto &childrenRef = QRangeModelDetails::refTo(maybeChildren);
2588 QModelIndexList fromIndexes;
2589 QModelIndexList toIndexes;
2590 fromIndexes.reserve(Base::size(childrenRef));
2591 toIndexes.reserve(Base::size(childrenRef));
2592 auto *parentRow = QRangeModelDetails::pointerTo(*it);
2593
2594 int row = 0;
2595 for (auto &child : childrenRef) {
2596 const_row_ptr oldParent = this->protocol().parentRow(child);
2597 if (oldParent != parentRow) {
2598 fromIndexes.append(this->createIndex(row, 0, oldParent));
2599 toIndexes.append(this->createIndex(row, 0, parentRow));
2600 this->protocol().setParentRow(child, parentRow);
2601 }
2602 ++row;
2603 }
2604 this->changePersistentIndexList(fromIndexes, toIndexes);
2605 resetParentInChildren(&childrenRef);
2606 }
2607 }
2608 }
2609 }
2610
2611 decltype(auto) rowDataImpl(const QModelIndex &index) const
2612 {
2613 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
2614 const range_type &siblings = childrenOf(parentRow);
2615 Q_ASSERT(index.row() < int(Base::size(siblings)));
2616 return *QRangeModelDetails::pos(siblings, index.row());
2617 }
2618
2619 decltype(auto) rowDataImpl(const QModelIndex &index)
2620 {
2621 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
2622 range_type &siblings = childrenOf(parentRow);
2623 Q_ASSERT(index.row() < int(Base::size(siblings)));
2624 return *QRangeModelDetails::pos(siblings, index.row());
2625 }
2626
2627 const range_type *childRangeImpl(const QModelIndex &index) const
2628 {
2629 const auto &row = this->rowData(index);
2630 if (!QRangeModelDetails::isValid(row))
2631 return static_cast<const range_type *>(nullptr);
2632
2633 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2634 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2635 }
2636
2637 range_type *childRangeImpl(const QModelIndex &index)
2638 {
2639 auto &row = this->rowData(index);
2640 if (!QRangeModelDetails::isValid(row))
2641 return static_cast<range_type *>(nullptr);
2642
2643 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2644 using Children = std::remove_reference_t<decltype(children)>;
2645
2646 if constexpr (QRangeModelDetails::is_any_of<Children, std::optional>())
2647 if constexpr (std::is_default_constructible<typename Children::value_type>()) {
2648 if (!children)
2649 children.emplace(range_type{});
2650 }
2651
2652 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2653 }
2654
2655 const range_type &childrenOf(const_row_ptr row) const
2656 {
2657 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2658 : *this->m_data.model();
2659 }
2660
2661private:
2662 range_type &childrenOf(row_ptr row)
2663 {
2664 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2665 : *this->m_data.model();
2666 }
2667};
2668
2669// specialization for flat models without protocol
2670template <typename Range>
2673{
2676
2677 static constexpr bool is_mutable_impl = true;
2678
2679public:
2680 using range_type = typename Base::range_type;
2682 using row_type = typename Base::row_type;
2684 using row_traits = typename Base::row_traits;
2685 using row_features = typename Base::row_features;
2686
2687 explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
2688 : Base(std::forward<Range>(model), {}, itemModel)
2689 {}
2690
2691protected:
2692 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
2693 {
2694 if constexpr (Base::dynamicColumns()) {
2695 if (column < int(Base::size(*QRangeModelDetails::pos(*this->m_data.model(), row))))
2696 return this->createIndex(row, column);
2697#ifndef QT_NO_DEBUG
2698 // if we got here, then column < columnCount(), but this row is too short
2699 qCritical("QRangeModel: Column-range at row %d is not large enough!", row);
2700#endif
2701 return {};
2702 } else {
2703 return this->createIndex(row, column);
2704 }
2705 }
2706
2707 QModelIndex parentImpl(const QModelIndex &) const
2708 {
2709 return {};
2710 }
2711
2712 int rowCountImpl(const QModelIndex &parent) const
2713 {
2714 if (parent.isValid())
2715 return 0;
2716 return int(Base::size(*this->m_data.model()));
2717 }
2718
2719 int columnCountImpl(const QModelIndex &parent) const
2720 {
2721 if (parent.isValid())
2722 return 0;
2723
2724 // in a table, all rows have the same number of columns (as the first row)
2725 if constexpr (Base::dynamicColumns()) {
2726 return int(Base::size(*this->m_data.model()) == 0
2727 ? 0
2728 : Base::size(*QRangeModelDetails::begin(*this->m_data.model())));
2729 } else if constexpr (Base::one_dimensional_range) {
2730 return row_traits::fixed_size();
2731 } else {
2732 return Base::static_column_count;
2733 }
2734 }
2735
2736 static constexpr Qt::ItemFlags defaultFlags()
2737 {
2738 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
2739 }
2740
2741 static constexpr bool canInsertRowsImpl()
2742 {
2743 return Base::dynamicRows() && range_features::has_insert;
2744 }
2745
2746 static constexpr bool canRemoveRowsImpl()
2747 {
2748 return Base::dynamicRows() && range_features::has_erase;
2749 }
2750
2751 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
2752 {
2753 return !source.isValid() && !destination.isValid();
2754 }
2755
2756 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
2757 {
2758 return !source.isValid() && !destination.isValid();
2759 }
2760
2761 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
2762 const QModelIndex &, int) noexcept
2763 {
2764 // table/flat model: can't move rows between different parents
2765 return false;
2766 }
2767
2768 auto makeEmptyRow(typename Base::row_ptr)
2769 {
2770 row_type empty_row = this->protocol().newRow();
2771
2772 // dynamically sized rows all have to have the same column count
2773 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
2774 if (QRangeModelDetails::isValid(empty_row))
2775 QRangeModelDetails::refTo(empty_row).resize(this->columnCount({}));
2776 }
2777
2778 return empty_row;
2779 }
2780
2781 template <typename It, typename Sentinel>
2782 void deleteRemovedRows(It &&begin, Sentinel &&end)
2783 {
2784 if constexpr (Base::protocol_traits::has_deleteRow) {
2785 for (auto it = begin; it != end; ++it)
2786 this->protocol().deleteRow(std::move(*it));
2787 }
2788 }
2789
2790 decltype(auto) rowDataImpl(const QModelIndex &index) const
2791 {
2792 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
2793 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
2794 }
2795
2796 decltype(auto) rowDataImpl(const QModelIndex &index)
2797 {
2798 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
2799 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
2800 }
2801
2802 const range_type *childRangeImpl(const QModelIndex &) const
2803 {
2804 return nullptr;
2805 }
2806
2807 range_type *childRangeImpl(const QModelIndex &)
2808 {
2809 return nullptr;
2810 }
2811
2812 const range_type &childrenOf(const_row_ptr row) const
2813 {
2814 Q_ASSERT(!row);
2815 return *this->m_data.model();
2816 }
2817
2818 void resetParentInChildren(range_type *)
2819 {
2820 }
2821};
2822
2823QT_END_NAMESPACE
2824
2825#endif // Q_QDOC
2826
2827#endif // QRANGEMODEL_IMPL_H
QModelIndex parentImpl(const QModelIndex &) const
static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
void deleteRemovedRows(It &&begin, Sentinel &&end)
const range_type & childrenOf(const_row_ptr row) const
static constexpr bool canRemoveRowsImpl()
QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
range_type * childRangeImpl(const QModelIndex &)
int columnCountImpl(const QModelIndex &parent) const
constexpr bool moveRowsAcross(const QModelIndex &, int, int, const QModelIndex &, int) noexcept
decltype(auto) rowDataImpl(const QModelIndex &index) const
static constexpr bool canInsertRowsImpl()
const range_type * childRangeImpl(const QModelIndex &) const
int rowCountImpl(const QModelIndex &parent) const
void resetParentInChildren(range_type *)
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()
auto makeEmptyRow(typename Base::row_ptr)
static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
auto makeEmptyRow(row_ptr parentRow)
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)
int rowCountImpl(const QModelIndex &parent) const
static constexpr bool canRemoveRowsImpl()
int columnCountImpl(const QModelIndex &) const
static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
const range_type & childrenOf(const_row_ptr row) const
decltype(auto) rowDataImpl(const QModelIndex &index)
QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
void deleteRemovedRows(It &&begin, Sentinel &&end)
const range_type * childRangeImpl(const QModelIndex &index) const
static constexpr bool canInsertRowsImpl()
decltype(auto) rowDataImpl(const QModelIndex &index) const
static constexpr Qt::ItemFlags defaultFlags()
QModelIndex parentImpl(const QModelIndex &child) const
const QAbstractItemModel & itemModel() const
void beginInsertRows(const QModelIndex &parent, int start, int count)
void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destParent, int destRow)
bool setItemData(const QModelIndex &index, const QMap< int, QVariant > &data)
QMap< int, QVariant > itemData(const QModelIndex &index) const
bool insertRows(int row, int count, const QModelIndex &parent)
static constexpr QMetaType meta_type_at(size_t idx)
Qt::ItemFlags flags(const QModelIndex &index) const
bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destParent, int destRow)
void beginRemoveRows(const QModelIndex &parent, int start, int count)
bool clearItemData(const QModelIndex &index)
QVariant data(const QModelIndex &index, int role) const
QRangeModelImplBase(QRangeModel *itemModel)
static auto for_element_at(StaticContainer &&container, std::size_t idx, F &&function)
void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to)
int columnCount(const QModelIndex &parent) const
QModelIndex sibling(int row, int column, const QModelIndex &index) const
void beginInsertColumns(const QModelIndex &parent, int start, int count)
std::tuple< typename C::Destroy, typename C::InvalidateCaches, typename C::SetHeaderData, typename C::SetData, typename C::SetItemData, typename C::ClearItemData, typename C::InsertColumns, typename C::RemoveColumns, typename C::MoveColumns, typename C::InsertRows, typename C::RemoveRows, typename C::MoveRows, typename C::Index, typename C::Parent, typename C::Sibling, typename C::RowCount, typename C::ColumnCount, typename C::Flags, typename C::HeaderData, typename C::Data, typename C::ItemData, typename C::RoleNames, typename C::MultiData > MethodTemplates
bool insertColumns(int column, int count, const QModelIndex &parent)
bool removeRows(int row, int count, const QModelIndex &parent)
QModelIndex parent(const QModelIndex &child) const
QAbstractItemModel & itemModel()
QModelIndex createIndex(int row, int column, const void *ptr=nullptr) const
QHash< int, QByteArray > roleNames() const
bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn)
bool setData(const QModelIndex &index, const QVariant &data, int role)
void dataChanged(const QModelIndex &from, const QModelIndex &to, const QList< int > &roles)
QVariant headerData(int section, Qt::Orientation orientation, int role) const
bool removeColumns(int column, int count, const QModelIndex &parent)
QModelIndex index(int row, int column, const QModelIndex &parent) const
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role)
int rowCount(const QModelIndex &parent) const
void beginRemoveColumns(const QModelIndex &parent, int start, int count)
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
bool removeRows(int row, int count, const QModelIndex &parent={})
static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
QHash< int, QByteArray > roleNames() const
row_reference rowData(const QModelIndex &index)
static constexpr bool dynamicRows()
static QVariant read(const Value &value)
const range_type * childRange(const QModelIndex &index) const
bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn)
static constexpr bool has_metaobject
QMetaProperty roleProperty(int role) const
QVariant readRole(int role, const ItemType &gadget) const
int columnCount(const QModelIndex &parent) const
bool writeRole(int role, ItemType &&gadget, const QVariant &data)
static bool write(Target *target, const QVariant &value)
Qt::ItemFlags flags(const QModelIndex &index) const
void readAt(const QModelIndex &index, F &&reader) const
bool insertColumns(int column, int count, const QModelIndex &parent)
typename Ancestor::template Override< BaseMethod, overridden > Override
QVariant data(const QModelIndex &index, int role) const
QVariant headerData(int section, Qt::Orientation orientation, int role) const
bool clearItemData(const QModelIndex &index)
void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
const protocol_type & protocol() const
static bool resetProperty(int property, ItemType &&object)
QModelIndex sibling(int row, int column, const QModelIndex &index) const
static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
bool setItemData(const QModelIndex &index, const QMap< int, QVariant > &data)
bool insertRows(int row, int count, const QModelIndex &parent)
static constexpr bool canInsertRows()
static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
static constexpr int size(const C &c)
const_row_reference rowData(const QModelIndex &index) const
bool writeAt(const QModelIndex &index, F &&writer)
static constexpr bool one_dimensional_range
static constexpr bool rows_are_raw_pointers
bool setHeaderData(int, Qt::Orientation, const QVariant &, int)
bool removeColumns(int column, int count, const QModelIndex &parent)
static constexpr bool isMutable()
bool setData(const QModelIndex &index, const QVariant &data, int role)
QModelIndex parent(const QModelIndex &child) const
static QVariant read(Value *value)
static bool write(Target &target, const QVariant &value)
static QVariant readProperty(int property, const ItemType &gadget)
bool writeRole(int role, ItemType *gadget, const QVariant &data)
QRangeModelImpl< Structure, Range, Protocol > Self
static constexpr bool canRemoveRows()
static constexpr int static_column_count
static QVariant readProperty(int property, ItemType *gadget)
protocol_type & protocol()
QtPrivate::QQuasiVirtualSubclass< Self, QRangeModelImplBase > Ancestor
int rowCount(const QModelIndex &parent) const
static constexpr bool rows_are_owning_or_raw_pointers
QModelIndex index(int row, int column, const QModelIndex &parent) const
static constexpr int static_row_count
static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
QRangeModelImpl(Range &&model, Protocol &&protocol, QRangeModel *itemModel)
const Structure & that() const
void updateTarget(LHS *org, RHS &&copy) noexcept
static constexpr bool dynamicColumns()
range_type * childRange(const QModelIndex &index)
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
QVariant readRole(int role, ItemType *gadget) const
static bool resetProperty(int property, ItemType *object)
bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
void updateTarget(LHS &org, RHS &&copy) noexcept
QMap< int, QVariant > itemData(const QModelIndex &index) const
bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
Method<&QQuasiVirtualInterface::destroy > Destroy
auto call(Args &&... args) const
QQuasiVirtualInterface< Interface > base_interface
void(*)(size_t index, base_interface &intf, void *ret, void *args) CallFN
static auto end(C &&c) -> decltype(std::end(refTo(std::forward< C >(c))))
static constexpr bool has_metaobject_v
static constexpr bool is_range_v
static constexpr bool array_like_v
static void rotate(C &c, int src, int count, int dst)
static constexpr bool tuple_like_v
auto value(It &&it) -> decltype(it.value())
static auto begin(C &&c) -> decltype(std::begin(refTo(std::forward< C >(c))))
auto key(It &&it) -> decltype(it.key())
static constexpr int static_size_v
static constexpr bool isValid(const T &t) noexcept
static auto pos(C &&c, int i)
static decltype(auto) refTo(T &&t)
static auto pointerTo(T &&t)
static constexpr bool is_multi_role_v
void applyIndexSwitch(size_t index, Applier &&applier, std::index_sequence< Is... >)
void applyIndexSwitch(size_t index, Applier &&applier)
auto setParentRow(R &row, R *parent) -> decltype(row.setParentRow(parent))
auto childRows(R &row) -> decltype(row.childRows())
auto childRows(const R &row) const -> decltype(row.childRows())
auto parentRow(const R &row) const -> decltype(row.parentRow())
static constexpr bool cachesProperties
std::remove_const_t< ModelStorage > m_model
static constexpr bool has_mutable_childRows
static constexpr bool has_insert_range
static constexpr int fixed_size()
static constexpr bool hasMetaObject
std::input_iterator_tag iterator_category
friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
void operator()(QQuasiVirtualInterface *self) const