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>
853
854 template <bool cacheProperties>
856 static constexpr bool cachesProperties = false;
857
859 };
860
861 template <>
862 struct PropertyData<true>
863 {
864 static constexpr bool cachesProperties = true;
866
868 {
869 properties.clear();
870 }
871 };
872
873 // The storage of the model data. We might store it as a pointer, or as a
874 // (copied- or moved-into) value (or smart pointer). But we always return a
875 // raw pointer.
876 template <typename ModelStorage, typename = void>
877 struct Storage
878 {
880
881 using iterator = decltype(begin(m_model));
882 using const_iterator = decltype(begin(m_model));
883 };
884
885 template <typename ModelStorage>
887 std::void_t<decltype(begin(std::declval<const ModelStorage&>()))>>
888 {
890
891 using iterator = decltype(begin(m_model));
893 };
894
895 template <typename ModelStorage, typename ItemType>
897 PropertyData<has_metaobject_v<ItemType>>
898 {
902
903 auto model() { return pointerTo(this->m_model); }
904 auto model() const { return pointerTo(this->m_model); }
905
906 template <typename Model = ModelStorage>
907 ModelData(Model &&model)
909 {}
910 };
911} // namespace QRangeModelDetails
912
913class QRangeModel;
914
916{
917private:
918 using Self = QRangeModelImplBase;
919 using QtPrivate::QQuasiVirtualInterface<Self>::Method;
920protected:
921 // Helper for calling a lambda with the element of a statically
922 // sized range (tuple or array) with a runtime index.
923 template <typename StaticContainer, typename F>
924 static auto for_element_at(StaticContainer &&container, std::size_t idx, F &&function)
925 {
926 using type = std::remove_cv_t<QRangeModelDetails::wrapped_t<StaticContainer>>;
927 static_assert(QRangeModelDetails::array_like_v<type> || QRangeModelDetails::tuple_like_v<type>,
928 "Internal error: expected an array-like or a tuple-like type");
929
930 if (QRangeModelDetails::isValid(container)) {
931 auto& ref = QRangeModelDetails::refTo(std::forward<StaticContainer>(container));
932 if constexpr (QRangeModelDetails::array_like_v<type>) {
933 Q_ASSERT(idx < std::size(ref));
934 function(ref[idx]);
935 } else {
936 constexpr size_t size = std::tuple_size_v<type>;
937 Q_ASSERT(idx < std::tuple_size_v<type>);
938 QtPrivate::applyIndexSwitch<size>(idx, [&](auto idxConstant) {
939 function(get<idxConstant>(ref));
940 });
941 }
942 }
943 }
944
945 // Get the QMetaType for a tuple-element at a runtime index.
946 // Used in the headerData implementation.
947 template <typename T>
948 static constexpr QMetaType meta_type_at(size_t idx)
949 {
950 using type = QRangeModelDetails::wrapped_t<T>;
951 if constexpr (QRangeModelDetails::array_like_v<type>) {
952 Q_UNUSED(idx);
953 return QMetaType::fromType<std::tuple_element_t<0, type>>();
954 } else {
955 constexpr auto size = std::tuple_size_v<type>;
956 Q_ASSERT(idx < size);
957 QMetaType metaType;
958 QtPrivate::applyIndexSwitch<size>(idx, [&metaType](auto idxConstant) {
959 using ElementType = std::tuple_element_t<idxConstant.value, type>;
960 metaType = QMetaType::fromType<QRangeModelDetails::wrapped_t<ElementType>>();
961 });
962 return metaType;
963 }
964 }
965
966public:
967 // overridable prototypes (quasi-pure-virtual methods)
968
970 bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
971 bool setData(const QModelIndex &index, const QVariant &data, int role);
972 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data);
973 bool clearItemData(const QModelIndex &index);
974 bool insertColumns(int column, int count, const QModelIndex &parent);
975 bool removeColumns(int column, int count, const QModelIndex &parent);
976 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn);
977 bool insertRows(int row, int count, const QModelIndex &parent);
978 bool removeRows(int row, int count, const QModelIndex &parent);
979 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow);
980
981 QModelIndex index(int row, int column, const QModelIndex &parent) const;
982 QModelIndex sibling(int row, int column, const QModelIndex &index) const;
983 int rowCount(const QModelIndex &parent) const;
984 int columnCount(const QModelIndex &parent) const;
985 Qt::ItemFlags flags(const QModelIndex &index) const;
986 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
987 QVariant data(const QModelIndex &index, int role) const;
988 QMap<int, QVariant> itemData(const QModelIndex &index) const;
990 QModelIndex parent(const QModelIndex &child) const;
991
992 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
993
994 // bindings for overriding
995
1007
1014 using Data = Method<&Self::data>;
1018
1019 // 6.11
1021
1022 template <typename C>
1023 using MethodTemplates = std::tuple<
1024 typename C::Destroy,
1025 typename C::InvalidateCaches,
1026 typename C::SetHeaderData,
1027 typename C::SetData,
1028 typename C::SetItemData,
1029 typename C::ClearItemData,
1030 typename C::InsertColumns,
1031 typename C::RemoveColumns,
1032 typename C::MoveColumns,
1033 typename C::InsertRows,
1034 typename C::RemoveRows,
1035 typename C::MoveRows,
1036 typename C::Index,
1037 typename C::Parent,
1038 typename C::Sibling,
1039 typename C::RowCount,
1040 typename C::ColumnCount,
1041 typename C::Flags,
1042 typename C::HeaderData,
1043 typename C::Data,
1044 typename C::ItemData,
1045 typename C::RoleNames,
1046 typename C::MultiData
1047 >;
1048
1049private:
1051 QRangeModel *m_rangeModel;
1052
1053protected:
1054 explicit QRangeModelImplBase(QRangeModel *itemModel)
1055 : m_rangeModel(itemModel)
1056 {}
1057
1058 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
1059 inline void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to);
1060 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
1061 const QList<int> &roles);
1062 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
1063 inline void endInsertColumns();
1064 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
1065 inline void endRemoveColumns();
1066 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1067 const QModelIndex &destParent, int destRow);
1068 inline void endMoveColumns();
1069 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
1070 inline void endInsertRows();
1071 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
1072 inline void endRemoveRows();
1073 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1074 const QModelIndex &destParent, int destRow);
1075 inline void endMoveRows();
1076 inline QAbstractItemModel &itemModel();
1077 inline const QAbstractItemModel &itemModel() const;
1078
1079 // implemented in qrangemodel.cpp
1081 const QMetaObject &metaObject);
1083};
1084
1085template <typename Structure, typename Range,
1086 typename Protocol = QRangeModelDetails::table_protocol_t<Range>>
1088 : public QtPrivate::QQuasiVirtualSubclass<QRangeModelImpl<Structure, Range, Protocol>,
1091{
1092public:
1102
1106 >,
1107 typename row_traits::item_type
1108 >;
1110
1111 using const_row_reference = decltype(*std::declval<typename ModelData::const_iterator&>());
1112
1113 static_assert(!QRangeModelDetails::is_any_of<range_type, std::optional>() &&
1115 "Currently, std::optional is not supported for ranges and rows, as "
1116 "it has range semantics in c++26. Once the required behavior is clarified, "
1117 "std::optional for ranges and rows will be supported.");
1118
1119protected:
1120
1121 using Self = QRangeModelImpl<Structure, Range, Protocol>;
1123
1124 Structure& that() { return static_cast<Structure &>(*this); }
1125 const Structure& that() const { return static_cast<const Structure &>(*this); }
1126
1127 template <typename C>
1128 static constexpr int size(const C &c)
1129 {
1130 using namespace QRangeModelDetails;
1131 if (!isValid(c))
1132 return 0;
1133
1134 if constexpr (test_size<C>()) {
1135 return int(std::size(c));
1136 } else {
1137#if defined(__cpp_lib_ranges)
1138 using std::ranges::distance;
1139#else
1140 using std::distance;
1141#endif
1142 using container_type = std::conditional_t<range_traits<C>::has_cbegin,
1143 const wrapped_t<C>,
1144 wrapped_t<C>>;
1145 container_type& container = const_cast<container_type &>(refTo(c));
1146 return int(distance(std::begin(container), std::end(container)));
1147 }
1148 }
1149
1150 static constexpr bool isMutable()
1151 {
1152 return range_features::is_mutable && row_features::is_mutable
1153 && std::is_reference_v<row_reference>
1154 && Structure::is_mutable_impl;
1155 }
1156
1159 static constexpr bool rows_are_owning_or_raw_pointers =
1162 static constexpr bool one_dimensional_range = static_column_count == 0;
1163
1164 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
1165 static constexpr bool dynamicColumns() { return static_column_count < 0; }
1166
1167 // A row might be a value (or range of values), or a pointer.
1168 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
1171
1172 template <typename T>
1175
1176 // A iterator type to use as the input iterator with the
1177 // range_type::insert(pos, start, end) overload if available (it is in
1178 // std::vector, but not in QList). Generates a prvalue when dereferenced,
1179 // which then gets moved into the newly constructed row, which allows us to
1180 // implement insertRows() for move-only row types.
1182 {
1186 using iterator_category = std::input_iterator_tag;
1187 using difference_type = int;
1188
1189 value_type operator*() { return impl->makeEmptyRow(parentRow); }
1190 EmptyRowGenerator &operator++() { ++n; return *this; }
1191 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1192 { return lhs.n == rhs.n; }
1193 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1194 { return !(lhs == rhs); }
1195
1197 Structure *impl = nullptr;
1198 const row_ptr parentRow = nullptr;
1199 };
1200
1201 // If we have a move-only row_type and can add/remove rows, then the range
1202 // must have an insert-from-range overload.
1205 "The range holding a move-only row-type must support insert(pos, start, end)");
1206
1207public:
1208 explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
1209 : Ancestor(itemModel)
1212 {
1213 }
1214
1215
1216 // static interface, called by QRangeModelImplBase
1217
1218 void invalidateCaches() { m_data.invalidateCaches(); }
1219
1220 // Not implemented
1221 bool setHeaderData(int , Qt::Orientation , const QVariant &, int ) { return false; }
1222
1223 // actual implementations
1224 QModelIndex index(int row, int column, const QModelIndex &parent) const
1225 {
1226 if (row < 0 || column < 0 || column >= columnCount(parent)
1227 || row >= rowCount(parent)) {
1228 return {};
1229 }
1230
1231 return that().indexImpl(row, column, parent);
1232 }
1233
1234 QModelIndex sibling(int row, int column, const QModelIndex &index) const
1235 {
1236 if (row == index.row() && column == index.column())
1237 return index;
1238
1239 if (column < 0 || column >= this->itemModel().columnCount())
1240 return {};
1241
1242 if (row == index.row())
1243 return this->createIndex(row, column, index.constInternalPointer());
1244
1245 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1246 const auto siblingCount = size(that().childrenOf(parentRow));
1247 if (row < 0 || row >= int(siblingCount))
1248 return {};
1249 return this->createIndex(row, column, parentRow);
1250 }
1251
1252 Qt::ItemFlags flags(const QModelIndex &index) const
1253 {
1254 if (!index.isValid())
1255 return Qt::NoItemFlags;
1256
1257 Qt::ItemFlags f = Structure::defaultFlags();
1258
1259 if constexpr (isMutable()) {
1260 if constexpr (row_traits::hasMetaObject) {
1261 if (index.column() < row_traits::fixed_size()) {
1262 const QMetaObject mo = wrapped_row_type::staticMetaObject;
1263 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
1264 if (prop.isWritable())
1265 f |= Qt::ItemIsEditable;
1266 }
1267 } else if constexpr (static_column_count <= 0) {
1268 f |= Qt::ItemIsEditable;
1269 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
1270 // we want to know if the elements in the tuple are const; they'd always be, if
1271 // we didn't remove the const of the range first.
1272 const_row_reference row = rowData(index);
1273 row_reference mutableRow = const_cast<row_reference>(row);
1274 if (QRangeModelDetails::isValid(mutableRow)) {
1275 QRangeModelImplBase::for_element_at(mutableRow, index.column(), [&f](auto &&ref){
1276 using target_type = decltype(ref);
1277 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
1278 f &= ~Qt::ItemIsEditable;
1279 else if constexpr (std::is_lvalue_reference_v<target_type>)
1280 f |= Qt::ItemIsEditable;
1281 });
1282 } else {
1283 // If there's no usable value stored in the row, then we can't
1284 // do anything with this item.
1285 f &= ~Qt::ItemIsEditable;
1286 }
1287 }
1288 }
1289 return f;
1290 }
1291
1292 QVariant headerData(int section, Qt::Orientation orientation, int role) const
1293 {
1294 QVariant result;
1295 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
1296 || section < 0 || section >= columnCount({})) {
1297 return this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1298 }
1299
1300 if constexpr (row_traits::hasMetaObject) {
1301 if (row_traits::fixed_size() == 1) {
1302 const QMetaType metaType = QMetaType::fromType<wrapped_row_type>();
1303 result = QString::fromUtf8(metaType.name());
1304 } else if (section <= row_traits::fixed_size()) {
1305 const QMetaProperty prop = wrapped_row_type::staticMetaObject.property(
1306 section + wrapped_row_type::staticMetaObject.propertyOffset());
1307 result = QString::fromUtf8(prop.name());
1308 }
1309 } else if constexpr (static_column_count >= 1) {
1310 if constexpr (QRangeModelDetails::array_like_v<wrapped_row_type>) {
1311 return section;
1312 } else {
1313 const QMetaType metaType = QRangeModelImplBase::meta_type_at<wrapped_row_type>(section);
1314 if (metaType.isValid())
1315 result = QString::fromUtf8(metaType.name());
1316 }
1317 }
1318 if (!result.isValid())
1319 result = this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1320 return result;
1321 }
1322
1323 QVariant data(const QModelIndex &index, int role) const
1324 {
1325 if (!index.isValid())
1326 return {};
1327
1328 QModelRoleData result(role);
1329 multiData(index, result);
1330 return std::move(result.data());
1331 }
1332
1333 QMap<int, QVariant> itemData(const QModelIndex &index) const
1334 {
1335 QMap<int, QVariant> result;
1336 bool tried = false;
1337 const auto readItemData = [this, &result, &tried](const auto &value){
1338 Q_UNUSED(this);
1339 using value_type = q20::remove_cvref_t<decltype(value)>;
1340 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1341 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1342
1343 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1344 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1345 tried = true;
1346 const auto roles = this->itemModel().roleNames().keys();
1347 for (auto &role : roles) {
1348 if (role == Qt::RangeModelDataRole)
1349 continue;
1350 QVariant data = ItemAccess::readRole(value, role);
1351 if (data.isValid())
1352 result[role] = std::move(data);
1353 }
1354 } else if constexpr (multi_role()) {
1355 tried = true;
1356 if constexpr (std::is_convertible_v<value_type, decltype(result)>) {
1357 result = value;
1358 } else {
1359 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1360 Q_UNUSED(this);
1361 if constexpr (!multi_role::int_key)
1362 return this->itemModel().roleNames();
1363 else
1364 return {};
1365 }();
1366 for (auto it = std::begin(value); it != std::end(value); ++it) {
1367 const int role = [&roleNames, key = QRangeModelDetails::key(it)]() {
1368 Q_UNUSED(roleNames);
1369 if constexpr (multi_role::int_key)
1370 return int(key);
1371 else
1372 return roleNames.key(key.toUtf8(), -1);
1373 }();
1374
1375 if (role != -1 && role != Qt::RangeModelDataRole)
1376 result.insert(role, QRangeModelDetails::value(it));
1377 }
1378 }
1379 } else if constexpr (has_metaobject<value_type>) {
1380 if (row_traits::fixed_size() <= 1) {
1381 tried = true;
1382 const auto roleNames = this->itemModel().roleNames();
1383 const auto end = roleNames.keyEnd();
1384 for (auto it = roleNames.keyBegin(); it != end; ++it) {
1385 const int role = *it;
1386 if (role == Qt::RangeModelDataRole)
1387 continue;
1388 QVariant data = readRole(role, QRangeModelDetails::pointerTo(value));
1389 if (data.isValid())
1390 result[role] = std::move(data);
1391 }
1392 }
1393 }
1394 };
1395
1396 if (index.isValid()) {
1397 readAt(index, readItemData);
1398
1399 if (!tried) // no multi-role item found
1400 result = this->itemModel().QAbstractItemModel::itemData(index);
1401 }
1402 return result;
1403 }
1404
1405 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1406 {
1407 bool tried = false;
1408 readAt(index, [this, &index, roleDataSpan, &tried](const auto &value) {
1409 Q_UNUSED(this);
1410 Q_UNUSED(index);
1411 using value_type = q20::remove_cvref_t<decltype(value)>;
1412 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1413 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1414
1415 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1416 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1417 tried = true;
1418 for (auto &roleData : roleDataSpan) {
1419 if (roleData.role() == Qt::RangeModelDataRole) {
1420 // Qt QML support: "modelData" role returns the entire multi-role item.
1421 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1422 // only by value (so we take the reference).
1423 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1424 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1425 else
1426 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1427 } else {
1428 roleData.setData(ItemAccess::readRole(value, roleData.role()));
1429 }
1430 }
1431 } else if constexpr (multi_role()) {
1432 tried = true;
1433 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1434 Q_UNUSED(this);
1435 if constexpr (!multi_role::int_key)
1436 return this->itemModel().roleNames();
1437 else
1438 return {};
1439 }();
1440 using key_type = typename value_type::key_type;
1441 for (auto &roleData : roleDataSpan) {
1442 const auto &it = [&roleNames, &value, role = roleData.role()]{
1443 Q_UNUSED(roleNames);
1444 if constexpr (multi_role::int_key)
1445 return value.find(key_type(role));
1446 else
1447 return value.find(roleNames.value(role));
1448 }();
1449 if (it != QRangeModelDetails::end(value))
1450 roleData.setData(QRangeModelDetails::value(it));
1451 else
1452 roleData.clearData();
1453 }
1454 } else if constexpr (has_metaobject<value_type>) {
1455 if (row_traits::fixed_size() <= 1) {
1456 tried = true;
1457 for (auto &roleData : roleDataSpan) {
1458 if (roleData.role() == Qt::RangeModelDataRole) {
1459 // Qt QML support: "modelData" role returns the entire multi-role item.
1460 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1461 // only by value (so we take the reference).
1462 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1463 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1464 else
1465 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1466 } else {
1467 roleData.setData(readRole(roleData.role(),
1468 QRangeModelDetails::pointerTo(value)));
1469 }
1470 }
1471 } else if (index.column() <= row_traits::fixed_size()) {
1472 tried = true;
1473 for (auto &roleData : roleDataSpan) {
1474 const int role = roleData.role();
1475 if (role == Qt::DisplayRole || role == Qt::EditRole) {
1476 roleData.setData(readProperty(index.column(),
1477 QRangeModelDetails::pointerTo(value)));
1478 } else {
1479 roleData.clearData();
1480 }
1481 }
1482 }
1483 } else {
1484 tried = true;
1485 for (auto &roleData : roleDataSpan) {
1486 const int role = roleData.role();
1487 if (role == Qt::DisplayRole || role == Qt::EditRole
1488 || role == Qt::RangeModelDataRole) {
1489 roleData.setData(read(value));
1490 } else {
1491 roleData.clearData();
1492 }
1493 }
1494 }
1495 });
1496
1497 Q_ASSERT(tried);
1498 }
1499
1500 bool setData(const QModelIndex &index, const QVariant &data, int role)
1501 {
1502 if (!index.isValid())
1503 return false;
1504
1505 bool success = false;
1506 if constexpr (isMutable()) {
1507 auto emitDataChanged = qScopeGuard([&success, this, &index, role]{
1508 if (success) {
1509 Q_EMIT this->dataChanged(index, index,
1510 role == Qt::EditRole || role == Qt::RangeModelDataRole
1511 ? QList<int>{} : QList<int>{role});
1512 }
1513 });
1514
1515 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1516 using value_type = q20::remove_cvref_t<decltype(target)>;
1517 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1518 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1519
1520 auto setRangeModelDataRole = [&target, &data]{
1521 auto &targetRef = QRangeModelDetails::refTo(target);
1522 constexpr auto targetMetaType = QMetaType::fromType<value_type>();
1523 const auto dataMetaType = data.metaType();
1524 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
1525 // This covers move-only types, but also polymorph types like QObject.
1526 // We don't support replacing a stored object with another one, as this
1527 // makes object ownership very messy.
1528 // fall through to error handling
1529 } else if constexpr (QRangeModelDetails::is_wrapped<value_type>()) {
1530 if (QRangeModelDetails::isValid(targetRef)) {
1531 // we need to get a wrapped value type out of the QVariant, which
1532 // might carry a pointer. We have to try all alternatives.
1533 if (const auto mt = QMetaType::fromType<wrapped_value_type>();
1534 data.canConvert(mt)) {
1535 targetRef = data.value<wrapped_value_type>();
1536 return true;
1537 } else if (const auto mtp = QMetaType::fromType<wrapped_value_type *>();
1538 data.canConvert(mtp)) {
1539 targetRef = *data.value<wrapped_value_type *>();
1540 return true;
1541 }
1542 }
1543 } else if (targetMetaType == dataMetaType) {
1544 targetRef = data.value<value_type>();
1545 return true;
1546 } else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1547 targetRef = *data.value<value_type *>();
1548 return true;
1549 }
1550#ifndef QT_NO_DEBUG
1551 qCritical("Not able to assign %s to %s",
1552 qPrintable(QDebug::toString(data)), targetMetaType.name());
1553#endif
1554 return false;
1555 };
1556
1557 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1558 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1559 if (role == Qt::RangeModelDataRole)
1560 return setRangeModelDataRole();
1561 return ItemAccess::writeRole(target, data, role);
1562 } if constexpr (has_metaobject<value_type>) {
1563 if (row_traits::fixed_size() <= 1) { // multi-role value
1564 if (role == Qt::RangeModelDataRole)
1565 return setRangeModelDataRole();
1566 return writeRole(role, QRangeModelDetails::pointerTo(target), data);
1567 } else if (column <= row_traits::fixed_size() // multi-column
1568 && (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::RangeModelDataRole)) {
1569 return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
1570 }
1571 } else if constexpr (multi_role::value) {
1572 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1573 // If there is an entry for EditRole, overwrite that; otherwise,
1574 // set the entry for DisplayRole.
1575 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1576 Q_UNUSED(this);
1577 if constexpr (!multi_role::int_key)
1578 return this->itemModel().roleNames();
1579 else
1580 return {};
1581 }();
1582 if (role == Qt::EditRole) {
1583 if constexpr (multi_role::int_key) {
1584 if (target.find(roleToSet) == target.end())
1585 roleToSet = Qt::DisplayRole;
1586 } else {
1587 if (target.find(roleNames.value(roleToSet)) == target.end())
1588 roleToSet = Qt::DisplayRole;
1589 }
1590 }
1591 if constexpr (multi_role::int_key)
1592 return write(target[roleToSet], data);
1593 else
1594 return write(target[roleNames.value(roleToSet)], data);
1595 } else if (role == Qt::DisplayRole || role == Qt::EditRole
1596 || role == Qt::RangeModelDataRole) {
1597 return write(target, data);
1598 }
1599 return false;
1600 };
1601
1602 success = writeAt(index, writeData);
1603 }
1604 return success;
1605 }
1606
1607 template <typename LHS, typename RHS>
1608 void updateTarget(LHS &org, RHS &&copy) noexcept
1609 {
1610 if constexpr (std::is_pointer_v<RHS>) {
1611 return;
1612 } else {
1613 using std::swap;
1614 if constexpr (std::is_assignable_v<LHS, RHS>)
1615 org = std::forward<RHS>(copy);
1616 else
1617 swap(org, copy);
1618 }
1619 }
1620 template <typename LHS, typename RHS>
1621 void updateTarget(LHS *org, RHS &&copy) noexcept
1622 {
1623 updateTarget(*org, std::forward<RHS>(copy));
1624 }
1625
1626 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1627 {
1628 if (!index.isValid() || data.isEmpty())
1629 return false;
1630
1631 bool success = false;
1632 if constexpr (isMutable()) {
1633 auto emitDataChanged = qScopeGuard([&success, this, &index, &data]{
1634 if (success)
1635 Q_EMIT this->dataChanged(index, index, data.keys());
1636 });
1637
1638 bool tried = false;
1639 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
1640 Q_UNUSED(this);
1641 using value_type = q20::remove_cvref_t<decltype(target)>;
1642 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1643 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1644
1645 // transactional: if possible, modify a copy and only
1646 // update target if all values from data could be stored.
1647 auto makeCopy = [](const value_type &original){
1648 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>)
1649 return QRangeModelDetails::pointerTo(original); // no transaction support
1650 else if constexpr (std::is_pointer_v<decltype(original)>)
1651 return *original;
1652 else if constexpr (std::is_copy_assignable_v<value_type>)
1653 return original;
1654 else
1655 return QRangeModelDetails::pointerTo(original);
1656 };
1657
1658 const auto roleNames = this->itemModel().roleNames();
1659
1660 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1661 tried = true;
1662 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1663 const auto roles = roleNames.keys();
1664 auto targetCopy = makeCopy(target);
1665 for (int role : roles) {
1666 if (!ItemAccess::writeRole(QRangeModelDetails::refTo(targetCopy),
1667 data.value(role), role)) {
1668 return false;
1669 }
1670 }
1671 updateTarget(target, std::move(targetCopy));
1672 return true;
1673 } else if constexpr (multi_role()) {
1674 using key_type = typename value_type::key_type;
1675 tried = true;
1676 const auto roleName = [&roleNames](int role) {
1677 return roleNames.value(role);
1678 };
1679
1680 // transactional: only update target if all values from data
1681 // can be stored. Storing never fails with int-keys.
1682 if constexpr (!multi_role::int_key)
1683 {
1684 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
1685 [&roleName](int role) { return roleName(role).isEmpty(); }
1686 );
1687
1688 if (invalid != data.keyEnd()) {
1689#ifndef QT_NO_DEBUG
1690 qWarning("No role name set for %d", *invalid);
1691#endif
1692 return false;
1693 }
1694 }
1695
1696 for (auto &&[role, value] : data.asKeyValueRange()) {
1697 if constexpr (multi_role::int_key)
1698 target[static_cast<key_type>(role)] = value;
1699 else
1700 target[QString::fromUtf8(roleName(role))] = value;
1701 }
1702 return true;
1703 } else if constexpr (has_metaobject<value_type>) {
1704 if (row_traits::fixed_size() <= 1) {
1705 tried = true;
1706 auto targetCopy = makeCopy(target);
1707 for (auto &&[role, value] : data.asKeyValueRange()) {
1708 if (role == Qt::RangeModelDataRole)
1709 continue;
1710 if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
1711 const QByteArray roleName = roleNames.value(role);
1712#ifndef QT_NO_DEBUG
1713 qWarning("Failed to write value '%s' to role '%s'",
1714 qPrintable(QDebug::toString(value)), roleName.data());
1715#endif
1716 return false;
1717 }
1718 }
1719 updateTarget(target, std::move(targetCopy));
1720 return true;
1721 }
1722 }
1723 return false;
1724 };
1725
1726 success = writeAt(index, writeItemData);
1727
1728 if (!tried) {
1729 // setItemData will emit the dataChanged signal
1730 Q_ASSERT(!success);
1731 emitDataChanged.dismiss();
1732 success = this->itemModel().QAbstractItemModel::setItemData(index, data);
1733 }
1734 }
1735 return success;
1736 }
1737
1738 bool clearItemData(const QModelIndex &index)
1739 {
1740 if (!index.isValid())
1741 return false;
1742
1743 bool success = false;
1744 if constexpr (isMutable()) {
1745 auto emitDataChanged = qScopeGuard([&success, this, &index]{
1746 if (success)
1747 Q_EMIT this->dataChanged(index, index, {});
1748 });
1749
1750 auto clearData = [column = index.column()](auto &&target) {
1751 if constexpr (row_traits::hasMetaObject) {
1752 if (row_traits::fixed_size() <= 1) {
1753 // multi-role object/gadget: reset all properties
1754 return resetProperty(-1, QRangeModelDetails::pointerTo(target));
1755 } else if (column <= row_traits::fixed_size()) {
1756 return resetProperty(column, QRangeModelDetails::pointerTo(target));
1757 }
1758 } else { // normal structs, values, associative containers
1759 target = {};
1760 return true;
1761 }
1762 return false;
1763 };
1764
1765 success = writeAt(index, clearData);
1766 }
1767 return success;
1768 }
1769
1771 {
1772 // will be 'void' if columns don't all have the same type
1773 using item_type = typename row_traits::item_type;
1774 if constexpr (QRangeModelDetails::has_metaobject_v<item_type>) {
1775 return QRangeModelImplBase::roleNamesForMetaObject(this->itemModel(),
1776 QRangeModelDetails::wrapped_t<item_type>::staticMetaObject);
1777 } else if constexpr (std::negation_v<std::disjunction<std::is_void<item_type>,
1778 QRangeModelDetails::is_multi_role<item_type>>>) {
1779 return QRangeModelImplBase::roleNamesForSimpleType();
1780 }
1781
1782 return this->itemModel().QAbstractItemModel::roleNames();
1783 }
1784
1785
1786 template <typename InsertFn>
1787 bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
1788 {
1789 if (count == 0)
1790 return false;
1791 range_type * const children = childRange(parent);
1792 if (!children)
1793 return false;
1794
1795 this->beginInsertColumns(parent, column, column + count - 1);
1796
1797 for (auto &child : *children) {
1798 auto it = QRangeModelDetails::pos(child, column);
1799 (void)insertFn(QRangeModelDetails::refTo(child), it, count);
1800 }
1801
1802 this->endInsertColumns();
1803
1804 return true;
1805 }
1806
1807 bool insertColumns(int column, int count, const QModelIndex &parent)
1808 {
1809 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
1810 return doInsertColumns(column, count, parent, [](auto &row, auto it, int n){
1811 row.insert(it, n, {});
1812 return true;
1813 });
1814 } else {
1815 return false;
1816 }
1817 }
1818
1819 bool removeColumns(int column, int count, const QModelIndex &parent)
1820 {
1821 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
1822 if (column < 0 || column + count > columnCount(parent))
1823 return false;
1824
1825 range_type * const children = childRange(parent);
1826 if (!children)
1827 return false;
1828
1829 this->beginRemoveColumns(parent, column, column + count - 1);
1830 for (auto &child : *children) {
1831 const auto start = QRangeModelDetails::pos(child, column);
1832 QRangeModelDetails::refTo(child).erase(start, std::next(start, count));
1833 }
1834 this->endRemoveColumns();
1835 return true;
1836 }
1837 return false;
1838 }
1839
1840 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1841 const QModelIndex &destParent, int destColumn)
1842 {
1843 // we only support moving columns within the same parent
1844 if (sourceParent != destParent)
1845 return false;
1846 if constexpr (isMutable() && (row_features::has_rotate || row_features::has_splice)) {
1847 if (!Structure::canMoveColumns(sourceParent, destParent))
1848 return false;
1849
1850 if constexpr (dynamicColumns()) {
1851 // we only support ranges as columns, as other types might
1852 // not have the same data type across all columns
1853 range_type * const children = childRange(sourceParent);
1854 if (!children)
1855 return false;
1856
1857 if (!this->beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
1858 destParent, destColumn)) {
1859 return false;
1860 }
1861
1862 for (auto &child : *children)
1863 QRangeModelDetails::rotate(child, sourceColumn, count, destColumn);
1864
1865 this->endMoveColumns();
1866 return true;
1867 }
1868 }
1869 return false;
1870 }
1871
1872 template <typename InsertFn>
1873 bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
1874 {
1875 range_type *children = childRange(parent);
1876 if (!children)
1877 return false;
1878
1879 this->beginInsertRows(parent, row, row + count - 1);
1880
1881 row_ptr parentRow = parent.isValid()
1882 ? QRangeModelDetails::pointerTo(this->rowData(parent))
1883 : nullptr;
1884 (void)std::forward<InsertFn>(insertFn)(*children, parentRow, row, count);
1885
1886 // fix the parent in all children of the modified row, as the
1887 // references back to the parent might have become invalid.
1888 that().resetParentInChildren(children);
1889
1890 this->endInsertRows();
1891
1892 return true;
1893 }
1894
1895 bool insertRows(int row, int count, const QModelIndex &parent)
1896 {
1897 if constexpr (canInsertRows()) {
1898 return doInsertRows(row, count, parent,
1899 [this](range_type &children, row_ptr parentRow, int r, int n){
1900 EmptyRowGenerator generator{0, &that(), parentRow};
1901
1902 const auto pos = QRangeModelDetails::pos(children, r);
1903 if constexpr (range_features::has_insert_range) {
1904 children.insert(pos, std::move(generator), EmptyRowGenerator{n});
1905 } else if constexpr (rows_are_owning_or_raw_pointers) {
1906 auto start = children.insert(pos, n, nullptr); // MSVC doesn't like row_type{}
1907 std::copy(std::move(generator), EmptyRowGenerator{n}, start);
1908 } else {
1909 children.insert(pos, n, std::move(*generator));
1910 }
1911 return true;
1912 });
1913 } else {
1914 return false;
1915 }
1916 }
1917
1918 bool removeRows(int row, int count, const QModelIndex &parent = {})
1919 {
1920 if constexpr (canRemoveRows()) {
1921 const int prevRowCount = rowCount(parent);
1922 if (row < 0 || row + count > prevRowCount)
1923 return false;
1924
1925 range_type *children = childRange(parent);
1926 if (!children)
1927 return false;
1928
1929 this->beginRemoveRows(parent, row, row + count - 1);
1930 [[maybe_unused]] bool callEndRemoveColumns = false;
1931 if constexpr (dynamicColumns()) {
1932 // if we remove the last row in a dynamic model, then we no longer
1933 // know how many columns we should have, so they will be reported as 0.
1934 if (prevRowCount == count) {
1935 if (const int columns = columnCount(parent)) {
1936 callEndRemoveColumns = true;
1937 this->beginRemoveColumns(parent, 0, columns - 1);
1938 }
1939 }
1940 }
1941 { // erase invalidates iterators
1942 const auto begin = QRangeModelDetails::pos(children, row);
1943 const auto end = std::next(begin, count);
1944 that().deleteRemovedRows(begin, end);
1945 children->erase(begin, end);
1946 }
1947 // fix the parent in all children of the modified row, as the
1948 // references back to the parent might have become invalid.
1949 that().resetParentInChildren(children);
1950
1951 if constexpr (dynamicColumns()) {
1952 if (callEndRemoveColumns) {
1953 Q_ASSERT(columnCount(parent) == 0);
1954 this->endRemoveColumns();
1955 }
1956 }
1957 this->endRemoveRows();
1958 return true;
1959 } else {
1960 return false;
1961 }
1962 }
1963
1964 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
1965 const QModelIndex &destParent, int destRow)
1966 {
1967 if constexpr (isMutable() && (range_features::has_rotate || range_features::has_splice)) {
1968 if (!Structure::canMoveRows(sourceParent, destParent))
1969 return false;
1970
1971 if (sourceParent != destParent) {
1972 return that().moveRowsAcross(sourceParent, sourceRow, count,
1973 destParent, destRow);
1974 }
1975
1976 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
1977 || sourceRow < 0 || sourceRow + count - 1 >= this->itemModel().rowCount(sourceParent)
1978 || destRow < 0 || destRow > this->itemModel().rowCount(destParent)) {
1979 return false;
1980 }
1981
1982 range_type *source = childRange(sourceParent);
1983 // moving within the same range
1984 if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
1985 return false;
1986
1987 QRangeModelDetails::rotate(source, sourceRow, count, destRow);
1988
1989 that().resetParentInChildren(source);
1990
1991 this->endMoveRows();
1992 return true;
1993 } else {
1994 return false;
1995 }
1996 }
1997
1998 QModelIndex parent(const QModelIndex &child) const { return that().parent(child); }
1999
2000 int rowCount(const QModelIndex &parent) const { return that().rowCount(parent); }
2001
2002 int columnCount(const QModelIndex &parent) const { return that().columnCount(parent); }
2003
2004 void destroy() { delete std::addressof(that()); }
2005
2006 template <typename BaseMethod, typename BaseMethod::template Overridden<Self> overridden>
2007 using Override = typename Ancestor::template Override<BaseMethod, overridden>;
2008
2017
2032
2034
2035protected:
2037 {
2038 // We delete row objects if we are not operating on a reference or pointer
2039 // to a range, as in that case, the owner of the referenced/pointed to
2040 // range also owns the row entries.
2041 // ### Problem: if we get a copy of a range (no matter if shared or not),
2042 // then adding rows will create row objects in the model's copy, and the
2043 // client can never delete those. But copied rows will be the same pointer,
2044 // which we must not delete (as we didn't create them).
2045
2046 static constexpr bool modelCopied = !QRangeModelDetails::is_wrapped<Range>() &&
2047 (std::is_reference_v<Range> || std::is_const_v<std::remove_reference_t<Range>>);
2048
2049 static constexpr bool modelShared = QRangeModelDetails::is_any_shared_ptr<Range>();
2050
2051 static constexpr bool default_row_deleter = protocol_traits::is_default &&
2052 protocol_traits::has_deleteRow;
2053
2054 static constexpr bool ambiguousRowOwnership = (modelCopied || modelShared) &&
2055 rows_are_raw_pointers && default_row_deleter;
2056
2057 static_assert(!ambiguousRowOwnership,
2058 "Using of copied and shared tree and table models with rows as raw pointers, "
2059 "and the default protocol is not allowed due to ambiguity of rows ownership. "
2060 "Move the model in, use another row type, or implement a custom tree protocol.");
2061
2062 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
2063 && !QRangeModelDetails::is_any_of<Range, std::reference_wrapper>()) {
2064 const auto begin = QRangeModelDetails::begin(*m_data.model());
2065 const auto end = QRangeModelDetails::end(*m_data.model());
2066 that().deleteRemovedRows(begin, end);
2067 }
2068 }
2069
2070 static constexpr bool canInsertRows()
2071 {
2072 if constexpr (dynamicColumns() && !row_features::has_resize) {
2073 // If we operate on dynamic columns and cannot resize a newly
2074 // constructed row, then we cannot insert.
2075 return false;
2076 } else if constexpr (!protocol_traits::has_newRow) {
2077 // We also cannot insert if we cannot create a new row element
2078 return false;
2079 } else if constexpr (!range_features::has_insert_range
2080 && !std::is_copy_constructible_v<row_type>) {
2081 // And if the row is a move-only type, then the range needs to be
2082 // backed by a container that can move-insert default-constructed
2083 // row elements.
2084 return false;
2085 } else {
2086 return Structure::canInsertRowsImpl();
2087 }
2088 }
2089
2090 static constexpr bool canRemoveRows()
2091 {
2092 return Structure::canRemoveRowsImpl();
2093 }
2094
2095 template <typename F>
2096 bool writeAt(const QModelIndex &index, F&& writer)
2097 {
2098 bool result = false;
2099 row_reference row = rowData(index);
2100
2101 if constexpr (one_dimensional_range) {
2102 result = writer(row);
2103 } else if (QRangeModelDetails::isValid(row)) {
2104 if constexpr (dynamicColumns()) {
2105 result = writer(*QRangeModelDetails::pos(row, index.column()));
2106 } else {
2107 QRangeModelImplBase::for_element_at(row, index.column(), [&writer, &result](auto &&target) {
2108 using target_type = decltype(target);
2109 // we can only assign to an lvalue reference
2110 if constexpr (std::is_lvalue_reference_v<target_type>
2111 && !std::is_const_v<std::remove_reference_t<target_type>>) {
2112 result = writer(std::forward<target_type>(target));
2113 }
2114 });
2115 }
2116 }
2117
2118 return result;
2119 }
2120
2121 template <typename F>
2122 void readAt(const QModelIndex &index, F&& reader) const {
2123 const_row_reference row = rowData(index);
2124 if constexpr (one_dimensional_range) {
2125 return reader(row);
2126 } else if (QRangeModelDetails::isValid(row)) {
2127 if constexpr (dynamicColumns())
2128 reader(*QRangeModelDetails::pos(row, index.column()));
2129 else
2130 QRangeModelImplBase::for_element_at(row, index.column(), std::forward<F>(reader));
2131 }
2132 }
2133
2134 template <typename Value>
2135 static QVariant read(const Value &value)
2136 {
2137 if constexpr (std::is_constructible_v<QVariant, Value>)
2138 return QVariant(value);
2139 else
2140 return QVariant::fromValue(value);
2141 }
2142 template <typename Value>
2143 static QVariant read(Value *value)
2144 {
2145 if (value) {
2146 if constexpr (std::is_constructible_v<QVariant, Value *>)
2147 return QVariant(value);
2148 else
2149 return read(*value);
2150 }
2151 return {};
2152 }
2153
2154 template <typename Target>
2155 static bool write(Target &target, const QVariant &value)
2156 {
2157 using Type = std::remove_reference_t<Target>;
2158 if constexpr (std::is_constructible_v<Target, QVariant>) {
2159 target = value;
2160 return true;
2161 } else if (value.canConvert<Type>()) {
2162 target = value.value<Type>();
2163 return true;
2164 }
2165 return false;
2166 }
2167 template <typename Target>
2168 static bool write(Target *target, const QVariant &value)
2169 {
2170 if (target)
2171 return write(*target, value);
2172 return false;
2173 }
2174
2175 template <typename ItemType>
2177 {
2178 struct {
2179 operator QMetaProperty() const {
2180 const QByteArray roleName = that.itemModel().roleNames().value(role);
2181 const QMetaObject &mo = ItemType::staticMetaObject;
2182 if (const int index = mo.indexOfProperty(roleName.data());
2183 index >= 0) {
2184 return mo.property(index);
2185 }
2186 return {};
2187 }
2188 const QRangeModelImpl &that;
2189 const int role;
2190 } findProperty{*this, role};
2191
2192 if constexpr (ModelData::cachesProperties)
2193 return *m_data.properties.tryEmplace(role, findProperty).iterator;
2194 else
2195 return findProperty;
2196 }
2197
2198 template <typename ItemType>
2199 QVariant readRole(int role, ItemType *gadget) const
2200 {
2201 using item_type = std::remove_pointer_t<ItemType>;
2202 QVariant result;
2203 QMetaProperty prop = roleProperty<item_type>(role);
2204 if (!prop.isValid() && role == Qt::EditRole)
2205 prop = roleProperty<item_type>(Qt::DisplayRole);
2206
2207 if (prop.isValid())
2208 result = readProperty(prop, gadget);
2209 return result;
2210 }
2211
2212 template <typename ItemType>
2213 QVariant readRole(int role, const ItemType &gadget) const
2214 {
2215 return readRole(role, &gadget);
2216 }
2217
2218 template <typename ItemType>
2219 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
2220 {
2221 if constexpr (std::is_base_of_v<QObject, ItemType>)
2222 return prop.read(gadget);
2223 else
2224 return prop.readOnGadget(gadget);
2225 }
2226 template <typename ItemType>
2227 static QVariant readProperty(int property, ItemType *gadget)
2228 {
2229 using item_type = std::remove_pointer_t<ItemType>;
2230 const QMetaObject &mo = item_type::staticMetaObject;
2231 const QMetaProperty prop = mo.property(property + mo.propertyOffset());
2232 return readProperty(prop, gadget);
2233 }
2234
2235 template <typename ItemType>
2236 static QVariant readProperty(int property, const ItemType &gadget)
2237 {
2238 return readProperty(property, &gadget);
2239 }
2240
2241 template <typename ItemType>
2242 bool writeRole(int role, ItemType *gadget, const QVariant &data)
2243 {
2244 using item_type = std::remove_pointer_t<ItemType>;
2245 auto prop = roleProperty<item_type>(role);
2246 if (!prop.isValid() && role == Qt::EditRole)
2247 prop = roleProperty<item_type>(Qt::DisplayRole);
2248
2249 return prop.isValid() ? writeProperty(prop, gadget, data) : false;
2250 }
2251
2252 template <typename ItemType>
2253 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
2254 {
2255 return writeRole(role, &gadget, data);
2256 }
2257
2258 template <typename ItemType>
2259 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
2260 {
2261 if constexpr (std::is_base_of_v<QObject, ItemType>)
2262 return prop.write(gadget, data);
2263 else
2264 return prop.writeOnGadget(gadget, data);
2265 }
2266 template <typename ItemType>
2267 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
2268 {
2269 using item_type = std::remove_pointer_t<ItemType>;
2270 const QMetaObject &mo = item_type::staticMetaObject;
2271 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
2272 }
2273
2274 template <typename ItemType>
2275 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
2276 {
2277 return writeProperty(property, &gadget, data);
2278 }
2279
2280 template <typename ItemType>
2281 static bool resetProperty(int property, ItemType *object)
2282 {
2283 using item_type = std::remove_pointer_t<ItemType>;
2284 const QMetaObject &mo = item_type::staticMetaObject;
2285 bool success = true;
2286 if (property == -1) {
2287 // reset all properties
2288 if constexpr (std::is_base_of_v<QObject, item_type>) {
2289 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
2290 success = writeProperty(mo.property(p), object, {}) && success;
2291 } else { // reset a gadget by assigning a default-constructed
2292 *object = {};
2293 }
2294 } else {
2295 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
2296 }
2297 return success;
2298 }
2299
2300 template <typename ItemType>
2301 static bool resetProperty(int property, ItemType &&object)
2302 {
2303 return resetProperty(property, &object);
2304 }
2305
2306 // helpers
2307 const_row_reference rowData(const QModelIndex &index) const
2308 {
2309 Q_ASSERT(index.isValid());
2310 return that().rowDataImpl(index);
2311 }
2312
2313 row_reference rowData(const QModelIndex &index)
2314 {
2315 Q_ASSERT(index.isValid());
2316 return that().rowDataImpl(index);
2317 }
2318
2319 const range_type *childRange(const QModelIndex &index) const
2320 {
2321 if (!index.isValid())
2322 return m_data.model();
2323 if (index.column()) // only items at column 0 can have children
2324 return nullptr;
2325 return that().childRangeImpl(index);
2326 }
2327
2328 range_type *childRange(const QModelIndex &index)
2329 {
2330 if (!index.isValid())
2331 return m_data.model();
2332 if (index.column()) // only items at column 0 can have children
2333 return nullptr;
2334 return that().childRangeImpl(index);
2335 }
2336
2337
2338 const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2339 protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2340
2342};
2343
2344// Implementations that depends on the model structure (flat vs tree) that will
2345// be specialized based on a protocol type. The main template implements tree
2346// support through a protocol type.
2347template <typename Range, typename Protocol>
2349 : public QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
2350{
2351 using Base = QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2352 friend class QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2353
2354 using range_type = typename Base::range_type;
2355 using range_features = typename Base::range_features;
2356 using row_type = typename Base::row_type;
2357 using row_ptr = typename Base::row_ptr;
2358 using const_row_ptr = typename Base::const_row_ptr;
2359
2360 using tree_traits = typename Base::protocol_traits;
2361 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
2362
2363 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
2364 QRangeModelDetails::is_smart_ptr<row_type>() ||
2365 QRangeModelDetails::is_any_of<row_type, std::reference_wrapper>();
2366 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
2367
2368public:
2369 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
2370 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
2371 {};
2372
2373protected:
2374 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
2375 {
2376 if (!parent.isValid())
2377 return this->createIndex(row, column);
2378 // only items at column 0 can have children
2379 if (parent.column())
2380 return QModelIndex();
2381
2382 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
2383 const auto &parentSiblings = childrenOf(grandParent);
2384 const auto it = QRangeModelDetails::pos(parentSiblings, parent.row());
2385 return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
2386 }
2387
2388 QModelIndex parent(const QModelIndex &child) const
2389 {
2390 if (!child.isValid())
2391 return {};
2392
2393 // no pointer to parent row - no parent
2394 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
2395 if (!parentRow)
2396 return {};
2397
2398 // get the siblings of the parent via the grand parent
2399 auto &&grandParent = this->protocol().parentRow(QRangeModelDetails::refTo(parentRow));
2400 const range_type &parentSiblings = childrenOf(QRangeModelDetails::pointerTo(grandParent));
2401 // find the index of parentRow
2402 const auto begin = QRangeModelDetails::begin(parentSiblings);
2403 const auto end = QRangeModelDetails::end(parentSiblings);
2404 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
2405 return QRangeModelDetails::pointerTo(std::forward<decltype(s)>(s)) == parentRow;
2406 });
2407 if (it != end)
2408 return this->createIndex(std::distance(begin, it), 0,
2409 QRangeModelDetails::pointerTo(grandParent));
2410 return {};
2411 }
2412
2413 int rowCount(const QModelIndex &parent) const
2414 {
2415 return Base::size(this->childRange(parent));
2416 }
2417
2418 int columnCount(const QModelIndex &) const
2419 {
2420 // all levels of a tree have to have the same, static, column count
2421 if constexpr (Base::one_dimensional_range)
2422 return 1;
2423 else
2424 return Base::static_column_count; // if static_column_count is -1, static assert fires
2425 }
2426
2427 static constexpr Qt::ItemFlags defaultFlags()
2428 {
2429 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2430 }
2431
2432 static constexpr bool canInsertRowsImpl()
2433 {
2434 // We must not insert rows if we cannot adjust the parents of the
2435 // children of the following rows. We don't have to do that if the
2436 // range operates on pointers.
2437 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2438 && Base::dynamicRows() && range_features::has_insert;
2439 }
2440
2441 static constexpr bool canRemoveRowsImpl()
2442 {
2443 // We must not remove rows if we cannot adjust the parents of the
2444 // children of the following rows. We don't have to do that if the
2445 // range operates on pointers.
2446 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2447 && Base::dynamicRows() && range_features::has_erase;
2448 }
2449
2450 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
2451 {
2452 return true;
2453 }
2454
2455 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
2456 {
2457 return true;
2458 }
2459
2460 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
2461 const QModelIndex &destParent, int destRow)
2462 {
2463 // If rows are pointers, then reference to the parent row don't
2464 // change, so we can move them around freely. Otherwise we need to
2465 // be able to explicitly update the parent pointer.
2466 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
2467 return false;
2468 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
2469 return false;
2470 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
2471 destParent, destRow)) {
2472 return false;
2473 }
2474
2475 range_type *source = this->childRange(sourceParent);
2476 range_type *destination = this->childRange(destParent);
2477
2478 // If we can insert data from another range into, then
2479 // use that to move the old data over.
2480 const auto destStart = QRangeModelDetails::pos(destination, destRow);
2481 if constexpr (range_features::has_insert_range) {
2482 const auto sourceStart = QRangeModelDetails::pos(*source, sourceRow);
2483 const auto sourceEnd = std::next(sourceStart, count);
2484
2485 destination->insert(destStart, std::move_iterator(sourceStart),
2486 std::move_iterator(sourceEnd));
2487 } else if constexpr (std::is_copy_constructible_v<row_type>) {
2488 // otherwise we have to make space first, and copy later.
2489 destination->insert(destStart, count, row_type{});
2490 }
2491
2492 row_ptr parentRow = destParent.isValid()
2493 ? QRangeModelDetails::pointerTo(this->rowData(destParent))
2494 : nullptr;
2495
2496 // if the source's parent was already inside the new parent row,
2497 // then the source row might have become invalid, so reset it.
2498 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
2499 if (sourceParent.row() < destRow) {
2500 source = this->childRange(sourceParent);
2501 } else {
2502 // the source parent moved down within destination
2503 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
2504 sourceParent.internalPointer()));
2505 }
2506 }
2507
2508 // move the data over and update the parent pointer
2509 {
2510 const auto writeStart = QRangeModelDetails::pos(destination, destRow);
2511 const auto writeEnd = std::next(writeStart, count);
2512 const auto sourceStart = QRangeModelDetails::pos(source, sourceRow);
2513 const auto sourceEnd = std::next(sourceStart, count);
2514
2515 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
2516 // move data over if not already done, otherwise
2517 // only fix the parent pointer
2518 if constexpr (!range_features::has_insert_range)
2519 *write = std::move(*read);
2520 this->protocol().setParentRow(QRangeModelDetails::refTo(*write), parentRow);
2521 }
2522 // remove the old rows from the source parent
2523 source->erase(sourceStart, sourceEnd);
2524 }
2525
2526 // Fix the parent pointers in children of both source and destination
2527 // ranges, as the references to the entries might have become invalid.
2528 // We don't have to do that if the rows are pointers, as in that case
2529 // the references to the entries are stable.
2530 resetParentInChildren(destination);
2532
2533 this->endMoveRows();
2534 return true;
2535 }
2536
2537 auto makeEmptyRow(row_ptr parentRow)
2538 {
2539 // tree traversal protocol: if we are here, then it must be possible
2540 // to change the parent of a row.
2541 static_assert(tree_traits::has_setParentRow);
2542 row_type empty_row = this->protocol().newRow();
2543 if (QRangeModelDetails::isValid(empty_row) && parentRow)
2544 this->protocol().setParentRow(QRangeModelDetails::refTo(empty_row), parentRow);
2545 return empty_row;
2546 }
2547
2548 template <typename It, typename Sentinel>
2549 void deleteRemovedRows(It &&begin, Sentinel &&end)
2550 {
2551 if constexpr (tree_traits::has_deleteRow) {
2552 for (auto it = begin; it != end; ++it) {
2553 if constexpr (Base::isMutable()) {
2554 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
2555 if (QRangeModelDetails::isValid(children)) {
2556 deleteRemovedRows(QRangeModelDetails::begin(children),
2557 QRangeModelDetails::end(children));
2558 QRangeModelDetails::refTo(children) = range_type{ };
2559 }
2560 }
2561
2562 this->protocol().deleteRow(std::move(*it));
2563 }
2564 }
2565 }
2566
2567 void resetParentInChildren(range_type *children)
2568 {
2569 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
2570 const auto begin = QRangeModelDetails::begin(*children);
2571 const auto end = QRangeModelDetails::end(*children);
2572 for (auto it = begin; it != end; ++it) {
2573 decltype(auto) maybeChildren = this->protocol().childRows(*it);
2574 if (QRangeModelDetails::isValid(maybeChildren)) {
2575 auto &childrenRef = QRangeModelDetails::refTo(maybeChildren);
2576 QModelIndexList fromIndexes;
2577 QModelIndexList toIndexes;
2578 fromIndexes.reserve(Base::size(childrenRef));
2579 toIndexes.reserve(Base::size(childrenRef));
2580 auto *parentRow = QRangeModelDetails::pointerTo(*it);
2581
2582 int row = 0;
2583 for (auto &child : childrenRef) {
2584 const_row_ptr oldParent = this->protocol().parentRow(child);
2585 if (oldParent != parentRow) {
2586 fromIndexes.append(this->createIndex(row, 0, oldParent));
2587 toIndexes.append(this->createIndex(row, 0, parentRow));
2588 this->protocol().setParentRow(child, parentRow);
2589 }
2590 ++row;
2591 }
2592 this->changePersistentIndexList(fromIndexes, toIndexes);
2593 resetParentInChildren(&childrenRef);
2594 }
2595 }
2596 }
2597 }
2598
2599 decltype(auto) rowDataImpl(const QModelIndex &index) const
2600 {
2601 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
2602 const range_type &siblings = childrenOf(parentRow);
2603 Q_ASSERT(index.row() < int(Base::size(siblings)));
2604 return *QRangeModelDetails::pos(siblings, index.row());
2605 }
2606
2607 decltype(auto) rowDataImpl(const QModelIndex &index)
2608 {
2609 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
2610 range_type &siblings = childrenOf(parentRow);
2611 Q_ASSERT(index.row() < int(Base::size(siblings)));
2612 return *QRangeModelDetails::pos(siblings, index.row());
2613 }
2614
2615 const range_type *childRangeImpl(const QModelIndex &index) const
2616 {
2617 const auto &row = this->rowData(index);
2618 if (!QRangeModelDetails::isValid(row))
2619 return static_cast<const range_type *>(nullptr);
2620
2621 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2622 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2623 }
2624
2625 range_type *childRangeImpl(const QModelIndex &index)
2626 {
2627 auto &row = this->rowData(index);
2628 if (!QRangeModelDetails::isValid(row))
2629 return static_cast<range_type *>(nullptr);
2630
2631 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2632 using Children = std::remove_reference_t<decltype(children)>;
2633
2634 if constexpr (QRangeModelDetails::is_any_of<Children, std::optional>())
2635 if constexpr (std::is_default_constructible<typename Children::value_type>()) {
2636 if (!children)
2637 children.emplace(range_type{});
2638 }
2639
2640 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2641 }
2642
2643 const range_type &childrenOf(const_row_ptr row) const
2644 {
2645 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2646 : *this->m_data.model();
2647 }
2648
2649private:
2650 range_type &childrenOf(row_ptr row)
2651 {
2652 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2653 : *this->m_data.model();
2654 }
2655};
2656
2657// specialization for flat models without protocol
2658template <typename Range>
2661{
2664
2665 using range_type = typename Base::range_type;
2666 using range_features = typename Base::range_features;
2667 using row_type = typename Base::row_type;
2668 using const_row_ptr = typename Base::const_row_ptr;
2669 using row_traits = typename Base::row_traits;
2670 using row_features = typename Base::row_features;
2671
2672 static constexpr bool is_mutable_impl = true;
2673
2674public:
2675 explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
2676 : Base(std::forward<Range>(model), {}, itemModel)
2677 {}
2678
2679protected:
2680 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
2681 {
2682 if constexpr (Base::dynamicColumns()) {
2683 if (column < int(Base::size(*QRangeModelDetails::pos(*this->m_data.model(), row))))
2684 return this->createIndex(row, column);
2685#ifndef QT_NO_DEBUG
2686 // if we got here, then column < columnCount(), but this row is too short
2687 qCritical("QRangeModel: Column-range at row %d is not large enough!", row);
2688#endif
2689 return {};
2690 } else {
2691 return this->createIndex(row, column);
2692 }
2693 }
2694
2695 QModelIndex parent(const QModelIndex &) const
2696 {
2697 return {};
2698 }
2699
2700 int rowCount(const QModelIndex &parent) const
2701 {
2702 if (parent.isValid())
2703 return 0;
2704 return int(Base::size(*this->m_data.model()));
2705 }
2706
2707 int columnCount(const QModelIndex &parent) const
2708 {
2709 if (parent.isValid())
2710 return 0;
2711
2712 // in a table, all rows have the same number of columns (as the first row)
2713 if constexpr (Base::dynamicColumns()) {
2714 return int(Base::size(*this->m_data.model()) == 0
2715 ? 0
2716 : Base::size(*QRangeModelDetails::begin(*this->m_data.model())));
2717 } else if constexpr (Base::one_dimensional_range) {
2718 return row_traits::fixed_size();
2719 } else {
2720 return Base::static_column_count;
2721 }
2722 }
2723
2724 static constexpr Qt::ItemFlags defaultFlags()
2725 {
2726 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
2727 }
2728
2729 static constexpr bool canInsertRowsImpl()
2730 {
2731 return Base::dynamicRows() && range_features::has_insert;
2732 }
2733
2734 static constexpr bool canRemoveRowsImpl()
2735 {
2736 return Base::dynamicRows() && range_features::has_erase;
2737 }
2738
2739 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
2740 {
2741 return !source.isValid() && !destination.isValid();
2742 }
2743
2744 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
2745 {
2746 return !source.isValid() && !destination.isValid();
2747 }
2748
2749 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
2750 const QModelIndex &, int) noexcept
2751 {
2752 // table/flat model: can't move rows between different parents
2753 return false;
2754 }
2755
2756 auto makeEmptyRow(typename Base::row_ptr)
2757 {
2758 row_type empty_row = this->protocol().newRow();
2759
2760 // dynamically sized rows all have to have the same column count
2761 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
2762 if (QRangeModelDetails::isValid(empty_row))
2763 QRangeModelDetails::refTo(empty_row).resize(this->itemModel().columnCount());
2764 }
2765
2766 return empty_row;
2767 }
2768
2769 template <typename It, typename Sentinel>
2770 void deleteRemovedRows(It &&begin, Sentinel &&end)
2771 {
2772 if constexpr (Base::protocol_traits::has_deleteRow) {
2773 for (auto it = begin; it != end; ++it)
2774 this->protocol().deleteRow(std::move(*it));
2775 }
2776 }
2777
2778 decltype(auto) rowDataImpl(const QModelIndex &index) const
2779 {
2780 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
2781 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
2782 }
2783
2784 decltype(auto) rowDataImpl(const QModelIndex &index)
2785 {
2786 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
2787 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
2788 }
2789
2790 const range_type *childRangeImpl(const QModelIndex &) const
2791 {
2792 return nullptr;
2793 }
2794
2795 range_type *childRangeImpl(const QModelIndex &)
2796 {
2797 return nullptr;
2798 }
2799
2800 const range_type &childrenOf(const_row_ptr row) const
2801 {
2802 Q_ASSERT(!row);
2803 return *this->m_data.model();
2804 }
2805
2806 void resetParentInChildren(range_type *)
2807 {
2808 }
2809};
2810
2811QT_END_NAMESPACE
2812
2813#endif // Q_QDOC
2814
2815#endif // QRANGEMODEL_IMPL_H
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)
QModelIndex parent(const QModelIndex &) const
int rowCount(const QModelIndex &parent) const
range_type * childRangeImpl(const QModelIndex &)
constexpr bool moveRowsAcross(const QModelIndex &, int, int, const QModelIndex &, int) noexcept
decltype(auto) rowDataImpl(const QModelIndex &index) const
static constexpr bool canInsertRowsImpl()
int columnCount(const QModelIndex &parent) const
const range_type * childRangeImpl(const QModelIndex &) 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)
int columnCount(const QModelIndex &) const
range_type * childRangeImpl(const QModelIndex &index)
bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
void resetParentInChildren(range_type *children)
static constexpr bool canRemoveRowsImpl()
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()
int rowCount(const QModelIndex &parent) const
decltype(auto) rowDataImpl(const QModelIndex &index) const
static constexpr Qt::ItemFlags defaultFlags()
QModelIndex parent(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