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/qquasivirtual_impl.h>
21#include <QtCore/qmetaobject.h>
22#include <QtCore/qvariant.h>
23#include <QtCore/qmap.h>
24#include <QtCore/qscopedvaluerollback.h>
25#include <QtCore/qset.h>
26#include <QtCore/qvarlengtharray.h>
27
28#include <algorithm>
29#include <functional>
30#include <iterator>
31#include <type_traits>
32#include <QtCore/qxptype_traits.h>
33#include <tuple>
34#include <QtCore/q23utility.h>
35
36QT_BEGIN_NAMESPACE
37
38namespace QRangeModelDetails
39{
40 template <typename T, template <typename...> typename... Templates>
42
43 template <template <typename...> typename Template,
44 typename... Params,
45 template <typename...> typename... Templates>
47
48 template <typename T,
49 template <typename...> typename Template,
50 template <typename...> typename... Templates>
52
53 template <typename T, template <typename...> typename... Templates>
55
56 template <typename T, typename = void>
58
59 template <typename T>
60 struct is_validatable<T, std::void_t<decltype(*std::declval<T>())>>
61 : std::is_constructible<bool, T> {};
62
63 template <typename T, typename = void>
65
66 template <typename T>
69 std::is_pointer<decltype(std::declval<T&>().get())>,
70 std::is_same<decltype(*std::declval<T&>().get()), decltype(*std::declval<T&>())>,
72 >>>
73 : std::true_type
74 {};
75
76 // TODO: shouldn't we check is_smart_ptr && !is_copy_constructible && !is_copy_assignable
77 // to support users-specific ptrs?
78 template <typename T>
80#ifndef QT_NO_SCOPED_POINTER
82#endif
84 >;
85
86 template <typename T>
89
90 template <typename T>
92 std::is_pointer<T>>;
93
94 template <typename T>
95 static auto pointerTo(T&& t) {
96 using Type = q20::remove_cvref_t<T>;
97 if constexpr (is_any_of<Type, std::optional>())
98 return t ? std::addressof(*std::forward<T>(t)) : nullptr;
99 else if constexpr (std::is_pointer<Type>())
100 return t;
101 else if constexpr (is_smart_ptr<Type>())
102 return t.get();
103 else if constexpr (is_any_of<Type, std::reference_wrapper>())
104 return std::addressof(t.get());
105 else
106 return std::addressof(std::forward<T>(t));
107 }
108
109 template <typename T>
111 {
113 };
114 template <>
115 struct wrapped_helper<void>
116 {
117 using type = void;
118 };
119 template <typename T>
121
122 template <typename T>
125 >>;
126
127 template <typename T, typename = void>
129 template <typename T, std::size_t N>
130 struct tuple_like<std::array<T, N>> : std::false_type {};
131 template <typename T>
134 template <typename T>
135 [[maybe_unused]] static constexpr bool tuple_like_v = tuple_like<T>::value;
136
137 template <typename T, typename = void>
139 template <typename T, std::size_t N>
140 struct array_like<std::array<T, N>> : std::true_type {};
141 template <typename T, std::size_t N>
142 struct array_like<T[N]> : std::true_type {};
143 template <typename T>
144 [[maybe_unused]] static constexpr bool array_like_v = array_like<T>::value;
145
146 template <typename T, typename = void>
148 template <typename T>
151 template <typename T>
152 [[maybe_unused]] static constexpr bool has_metaobject_v = has_metaobject<T>::value;
153
154 template <typename T>
155 static constexpr bool isValid(const T &t) noexcept
156 {
157 if constexpr (std::is_array_v<T>)
158 return true;
159 else if constexpr (is_validatable<T>())
160 return bool(t);
161 else
162 return true;
163 }
164
165 template <typename T>
166 static decltype(auto) refTo(T&& t) {
167 Q_ASSERT(QRangeModelDetails::isValid(t));
168 // it's allowed to move only if the object holds unique ownership of the wrapped data
169 using Type = q20::remove_cvref_t<T>;
170 if constexpr (is_any_of<T, std::optional>())
171 return *std::forward<T>(t); // let std::optional resolve dereferencing
172 if constexpr (!is_wrapped<Type>() || is_any_unique_ptr<Type>())
173 return q23::forward_like<T>(*QRangeModelDetails::pointerTo(t));
174 else
175 return *QRangeModelDetails::pointerTo(t);
176 }
177
178 template <typename It>
179 auto key(It&& it) -> decltype(it.key()) { return std::forward<It>(it).key(); }
180 template <typename It>
181 auto key(It&& it) -> decltype((it->first)) { return std::forward<It>(it)->first; }
182
183 template <typename It>
184 auto value(It&& it) -> decltype(it.value()) { return std::forward<It>(it).value(); }
185 template <typename It>
186 auto value(It&& it) -> decltype((it->second)) { return std::forward<It>(it)->second; }
187
188 // use our own, ADL friendly versions of begin/end so that we can overload
189 // for pointers.
190 using std::begin;
191 using std::end;
192 template <typename C>
193 static auto adl_begin(C &&c) -> decltype(begin(QRangeModelDetails::refTo(std::forward<C>(c))))
194 { return begin(QRangeModelDetails::refTo(std::forward<C>(c))); }
195 template <typename C>
196 static auto adl_end(C &&c) -> decltype(end(QRangeModelDetails::refTo(std::forward<C>(c))))
197 { return end(QRangeModelDetails::refTo(std::forward<C>(c))); }
198 template <typename C>
199 static auto pos(C &&c, int i)
200 { return std::next(QRangeModelDetails::adl_begin(std::forward<C>(c)), i); }
201
202 // Test if a type is a range, and whether we can modify it using the
203 // standard C++ container member functions insert, erase, and resize.
204 // For the sake of QAIM, we cannot modify a range if it holds const data
205 // even if the range itself is not const; we'd need to initialize new rows
206 // and columns, and move old row and column data.
207 template <typename C, typename = void>
209
210 template <typename C>
211 struct test_insert<C, std::void_t<decltype(std::declval<C>().insert(
212 std::declval<typename C::const_iterator>(),
213 std::declval<typename C::size_type>(),
214 std::declval<typename C::value_type>()
215 ))>>
216 : std::true_type
217 {};
218
219 // Can we insert from another (identical) range? Required to support
220 // move-only types
221 template <typename C, typename = void>
223
224 template <typename C>
225 struct test_insert_range<C, std::void_t<decltype(std::declval<C&>().insert(
226 std::declval<typename C::const_iterator&>(),
227 std::declval<std::move_iterator<typename C::iterator>&>(),
228 std::declval<std::move_iterator<typename C::iterator>&>()
229 ))>>
230 : std::true_type
231 {};
232
233 template <typename C, typename = void>
235
236 template <typename C>
237 struct test_erase<C, std::void_t<decltype(std::declval<C>().erase(
238 std::declval<typename C::const_iterator>(),
239 std::declval<typename C::const_iterator>()
240 ))>>
241 : std::true_type
242 {};
243
244 template <typename C, typename = void>
246
247 template <typename C>
248 struct test_resize<C, std::void_t<decltype(std::declval<C>().resize(
249 std::declval<typename C::size_type>(),
250 std::declval<typename C::value_type>()
251 ))>>
252 : std::true_type
253 {};
254
255 // we use std::rotate in moveRows/Columns, which requires the values (which
256 // might be const if we only get a const iterator) to be swappable, and the
257 // iterator type to be at least a forward iterator
258 template <typename It>
259 using test_rotate = std::conjunction<
260 std::is_swappable<decltype(*std::declval<It>())>,
261 std::is_base_of<std::forward_iterator_tag,
262 typename std::iterator_traits<It>::iterator_category>
263 >;
264
265 template <typename C, typename = void>
267
268 template <typename C>
269 struct test_splice<C, std::void_t<decltype(std::declval<C>().splice(
270 std::declval<typename C::const_iterator>(),
271 std::declval<C&>(),
272 std::declval<typename C::const_iterator>(),
273 std::declval<typename C::const_iterator>()
274 ))>>
275 : std::true_type
276 {};
277
278 template <typename C>
279 static void rotate(C& c, int src, int count, int dst) {
280 auto& container = QRangeModelDetails::refTo(c);
281 using Container = std::remove_reference_t<decltype(container)>;
282
283 const auto srcBegin = QRangeModelDetails::pos(container, src);
284 const auto srcEnd = std::next(srcBegin, count);
285 const auto dstBegin = QRangeModelDetails::pos(container, dst);
286
287 if constexpr (test_splice<Container>::value) {
288 if (dst > src && dst < src + count) // dst must be out of the source range
289 container.splice(srcBegin, container, dstBegin, srcEnd);
290 else if (dst != src) // otherwise, std::list gets corrupted
291 container.splice(dstBegin, container, srcBegin, srcEnd);
292 } else {
293 if (src < dst) // moving right
294 std::rotate(srcBegin, srcEnd, dstBegin);
295 else // moving left
296 std::rotate(dstBegin, srcBegin, srcEnd);
297 }
298 }
299
300 // Test if a type is an associative container that we can use for multi-role
301 // data, i.e. has a key_type and a mapped_type typedef, and maps from int,
302 // Qt::ItemDataRole, or QString to QVariant. This excludes std::set (and
303 // unordered_set), which are not useful for us anyway even though they are
304 // considered associative containers.
305 template <typename C, typename = void> struct is_multi_role : std::false_type
306 {
307 static constexpr bool int_key = false;
308 };
309 template <typename C> // Qt::ItemDataRole -> QVariant, or QString -> QVariant, int -> QVariant
310 struct is_multi_role<C, std::void_t<typename C::key_type, typename C::mapped_type>>
311 : std::conjunction<std::disjunction<std::is_same<typename C::key_type, int>,
312 std::is_same<typename C::key_type, Qt::ItemDataRole>,
313 std::is_same<typename C::key_type, QString>>,
314 std::is_same<typename C::mapped_type, QVariant>>
315 {
316 static constexpr bool int_key = !std::is_same_v<typename C::key_type, QString>;
317 };
318 template <typename C>
319 [[maybe_unused]]
320 static constexpr bool is_multi_role_v = is_multi_role<C>::value;
321
322 using std::size;
323 template <typename C, typename = void>
325 template <typename C>
326 struct test_size<C, std::void_t<decltype(size(std::declval<C&>()))>> : std::true_type {};
327
328 template <typename C, typename = void>
330 template <typename C>
331 struct test_cbegin<C, std::void_t<decltype(QRangeModelDetails::adl_begin(std::declval<const C&>()))>>
332 : std::true_type
333 {};
334
335 template <typename C, typename = void>
337 static constexpr bool is_mutable = !std::is_const_v<C>;
338 static constexpr bool has_insert = false;
339 static constexpr bool has_insert_range = false;
340 static constexpr bool has_erase = false;
341 static constexpr bool has_resize = false;
342 static constexpr bool has_rotate = false;
343 static constexpr bool has_splice = false;
344 static constexpr bool has_cbegin = false;
345 };
346 template <typename C>
348 decltype(QRangeModelDetails::adl_end(std::declval<C&>())),
350 >> : std::true_type
351 {
354 static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
355 static constexpr bool has_insert = test_insert<C>();
356 static constexpr bool has_insert_range = test_insert_range<C>();
357 static constexpr bool has_erase = test_erase<C>();
358 static constexpr bool has_resize = test_resize<C>();
359 static constexpr bool has_rotate = test_rotate<iterator>();
360 static constexpr bool has_splice = test_splice<C>();
361 static constexpr bool has_cbegin = test_cbegin<C>::value;
362 };
363
364 // Specializations for types that look like ranges, but should be
365 // treated as values.
366 enum class Mutable { Yes, No };
367 template <Mutable IsMutable>
369 static constexpr bool is_mutable = IsMutable == Mutable::Yes;
370 static constexpr bool has_insert = false;
371 static constexpr bool has_erase = false;
372 static constexpr bool has_resize = false;
373 static constexpr bool has_rotate = false;
374 static constexpr bool has_splice = false;
375 static constexpr bool has_cbegin = true;
376 };
378 template <> struct range_traits<QString> : iterable_value<Mutable::Yes> {};
379 template <class CharT, class Traits, class Allocator>
382
383 // const T * and views are read-only
384 template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
386
387 template <typename C>
389 template <typename C>
390 [[maybe_unused]] static constexpr bool is_range_v = is_range<C>();
391
392 // Detect an ItemAccess specialization with static read/writeRole members
393 template <typename T> struct QRangeModelItemAccess;
394
395 template <typename T, typename = void>
397
398 template <typename T>
401 Qt::DisplayRole)),
403 std::declval<QVariant>(),
404 Qt::DisplayRole))
405 >
406 > : std::true_type
407 {
409 static_assert(std::is_invocable_r_v<bool,
411 "The return type of the ItemAccess::writeRole implementation "
412 "needs to be convertible to a bool!");
413 static_assert(std::is_invocable_r_v<QVariant,
414 decltype(ItemAccess::readRole), const std::remove_pointer_t<T>&, Qt::ItemDataRole>,
415 "The return type of the ItemAccess::readRole implementation "
416 "needs to be convertible to QVariant!");
417 };
418
419 // Detect which options are set to override default heuristics. Since
420 // QRangeModel is not yet defined we need to delay the evaluation.
421 template <typename T> struct QRangeModelRowOptions;
422
423 template <typename T, typename = void>
425 {
426 static constexpr bool isMultiRole = item_access<std::remove_pointer_t<T>>::value;
427 };
428
429 template <typename T>
431 : std::true_type
432 {
434 using RowCategory = decltype(rowCategory);
436 };
437
438 // Find out how many fixed elements can be retrieved from a row element.
439 // main template for simple values and ranges. Specializing for ranges
440 // is ambiguous with arrays, as they are also ranges
441 template <typename T, typename = void>
442 struct row_traits {
443 static constexpr bool is_range = is_range_v<q20::remove_cvref_t<T>>
445 // A static size of -1 indicates dynamically sized range
446 // A static size of 0 indicates that the specified type doesn't
447 // represent static or dynamic range.
448 static constexpr int static_size = is_range ? -1 : 0;
450 static constexpr int fixed_size() { return 1; }
451 static constexpr bool hasMetaObject = false;
452
454 {
455 return {};
456 }
457
458 template <typename C, typename Fn>
459 static void for_element_at(C &&container, std::size_t idx, Fn &&fn)
460 {
461 if constexpr (is_range)
462 std::forward<Fn>(fn)(*QRangeModelDetails::pos(std::forward<C>(container), idx));
463 else
464 std::forward<Fn>(fn)(std::forward<C>(container));
465 }
466
467 template <typename Fn>
468 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
469 {
470 if constexpr (static_size == 0) {
471 return std::forward<Fn>(fn)(firstIndex, QRangeModelDetails::pointerTo(row));
472 } else {
473 int columnIndex = -1;
474 return std::all_of(QRangeModelDetails::adl_begin(row),
475 QRangeModelDetails::adl_end(row), [&](const auto &item) {
476 return std::forward<Fn>(fn)(firstIndex.siblingAtColumn(++columnIndex),
477 QRangeModelDetails::pointerTo(item));
478 });
479 }
480 }
481 };
482
483 // Specialization for tuple-like semantics (prioritized over metaobject)
484 template <typename T>
486 {
487 static constexpr std::size_t size64 = std::tuple_size_v<T>;
488 static_assert(q20::in_range<int>(size64));
489 static constexpr int static_size = int(size64);
490
491 // are the types in a tuple all the same
492 template <std::size_t ...I>
493 static constexpr bool allSameTypes(std::index_sequence<I...>)
494 {
495 return (std::is_same_v<std::tuple_element_t<0, T>,
496 std::tuple_element_t<I, T>> && ...);
497 }
498
500 std::tuple_element_t<0, T>, void>;
501 static constexpr int fixed_size() { return 0; }
502 static constexpr bool hasMetaObject = false;
503
504 template <typename C, typename F>
514
516 {
517 constexpr auto size = std::tuple_size_v<T>;
519
525 >();
526 if (metaType.isValid())
528 });
529 return result;
530 }
531
532 template <typename Fn, std::size_t ...Is>
533 static bool forEachTupleElement(const T &row, Fn &&fn, std::index_sequence<Is...>)
534 {
535 using std::get;
536 return (std::forward<Fn>(fn)(QRangeModelDetails::pointerTo(get<Is>(row))) && ...);
537 }
538
539 template <typename Fn>
540 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
541 {
542 int column = -1;
543 return forEachTupleElement(row, [&column, &fn, &firstIndex](const QObject *item){
546 }
547 };
548
549 // Specialization for C arrays and std::array
550 template <typename T, std::size_t N>
551 struct row_traits<std::array<T, N>>
552 {
553 static_assert(q20::in_range<int>(N));
554 static constexpr int static_size = int(N);
555 using item_type = T;
556 static constexpr int fixed_size() { return 0; }
557 static constexpr bool hasMetaObject = false;
558
559 template <typename C, typename F>
565
567 {
568 return section;
569 }
570
571 template <typename Fn>
572 static bool for_each_element(const std::array<T, N> &row, const QModelIndex &firstIndex, Fn &&fn)
573 {
574 int columnIndex = -1;
576 QRangeModelDetails::adl_end(row), [&](const auto &item) {
579 });
580 }
581 };
582
583 template <typename T, std::size_t N>
584 struct row_traits<T[N]> : row_traits<std::array<T, N>> {};
585
586 // prioritize tuple-like over metaobject
587 template <typename T>
589 {
590 static constexpr int static_size = 0;
592 static int fixed_size() {
593 if constexpr (row_category<T>::isMultiRole) {
594 return 1;
595 } else {
596 // Interpret a gadget in a list as a multi-column row item. To make
597 // a list of multi-role items, wrap it into SingleColumn.
598 static const int columnCount = []{
600 return mo.propertyCount() - mo.propertyOffset();
601 }();
602 return columnCount;
603 }
604 }
605
606 static constexpr bool hasMetaObject = true;
607
608 template <typename C, typename F>
610 {
612 }
613
615 {
617 if (fixed_size() == 1) {
620 } else if (section <= fixed_size()) {
624 }
625 return result;
626 }
627
628 template <typename Fn>
629 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
630 {
632 }
633 };
634
635 template <typename T, typename = void>
637 {
638 template <typename That>
639 static QHash<int, QByteArray> roleNames(That *)
640 {
641 return That::roleNamesForSimpleType();
642 }
643 };
644
645 template <>
646 struct item_traits<void>
647 {
648 template <typename That>
650 {
652 }
653 };
654
655 template <typename T>
660
661 template <typename T>
663 {
664 template <typename That>
669 };
670
671 template <typename T>
672 [[maybe_unused]] static constexpr int static_size_v =
674
675 template <typename Range>
677 {
679
680 template <typename R = row_type>
681 auto newRow() -> decltype(R{}) { return R{}; }
682 };
683
684 template <typename Range>
686 {
688
689 template <typename R = row_type,
694 >,
695 bool> = true>
696 auto newRow() -> decltype(R(new QRangeModelDetails::wrapped_t<R>)) {
697 if constexpr (is_any_of<R, std::shared_ptr>())
699 else
700 return R(new QRangeModelDetails::wrapped_t<R>);
701 }
702
703 template <typename R = row_type,
705 auto newRow() -> decltype(R{}) { return R{}; }
706
707 template <typename R = row_type,
709 auto deleteRow(R&& row) -> decltype(delete row) { delete row; }
710 };
711
712 template <typename Range,
716
717 // Default tree traversal protocol implementation for row types that have
718 // the respective member functions. The trailing return type implicitly
719 // removes those functions that are not available.
720 template <typename Range>
722 {
723 template <typename R /*wrapped_row_type*/>
724 auto parentRow(const R& row) const -> decltype(row.parentRow())
725 {
726 return row.parentRow();
727 }
728
729 template <typename R /* = wrapped_row_type*/>
730 auto setParentRow(R &row, R* parent) -> decltype(row.setParentRow(parent))
731 {
732 row.setParentRow(parent);
733 }
734
735 template <typename R /* = wrapped_row_type*/>
736 auto childRows(const R &row) const -> decltype(row.childRows())
737 {
738 return row.childRows();
739 }
740
741 template <typename R /* = wrapped_row_type*/>
742 auto childRows(R &row) -> decltype(row.childRows())
743 {
744 return row.childRows();
745 }
746 };
747
748 template <typename P, typename R>
749 using protocol_parentRow_test = decltype(std::declval<P&>()
751 template <typename P, typename R>
753
754 template <typename P, typename R>
755 using protocol_childRows_test = decltype(std::declval<P&>()
757 template <typename P, typename R>
759
760 template <typename P, typename R>
764 template <typename P, typename R>
766
767 template <typename P, typename R>
770 template <typename P, typename R>
772
773 template <typename P, typename = void>
775 template <typename P>
776 struct protocol_newRow<P, std::void_t<decltype(std::declval<P&>().newRow())>>
777 : std::true_type {};
778
779 template <typename P, typename R, typename = void>
781 template <typename P, typename R>
783 std::void_t<decltype(std::declval<P&>().deleteRow(std::declval<R&&>()))>>
784 : std::true_type {};
785
786 template <typename Range,
787 typename Protocol = DefaultTreeProtocol<Range>,
788 typename R = typename range_traits<Range>::value_type,
789 typename = void>
791
792 template <typename Range, typename Protocol, typename R>
797
798 template <typename Range>
802 >, bool>;
803
804 template <typename Range, typename Protocol = DefaultTreeProtocol<Range>>
809 >, bool>;
810
811 template <typename Range, typename Protocol>
829
846
847 template <bool cacheProperties, bool itemsAreQObjects>
849 static constexpr bool cachesProperties = false;
850
852 };
853
855 {
856 static constexpr bool cachesProperties = true;
858
860 {
861 properties.clear();
862 }
863 protected:
864 ~PropertyCache() = default;
865 };
866
867 template <>
868 struct PropertyData<true, false> : PropertyCache
869 {};
870
872 {
873 struct Connection {
875 int role;
876
877 friend bool operator==(const Connection &lhs, const Connection &rhs) noexcept
878 {
879 return lhs.sender == rhs.sender && lhs.role == rhs.role;
880 }
881 friend size_t qHash(const Connection &c, size_t seed) noexcept
882 {
883 return qHashMulti(seed, c.sender, c.role);
884 }
885 };
886
889
890 protected:
891 ~ConnectionStorage() = default;
892 };
893
894 template <>
896 {};
897
898 template <>
899 struct PropertyData<false, true> : PropertyData<false, false>, ConnectionStorage
900 {
902 };
903
904 // The storage of the model data. We might store it as a pointer, or as a
905 // (copied- or moved-into) value (or smart pointer). But we always return a
906 // raw pointer.
907 template <typename ModelStorage, typename = void>
915
916 template <typename ModelStorage>
925
926 template <typename ModelStorage, typename PropertyStorage>
928 PropertyStorage
929 {
933
934 auto model() { return QRangeModelDetails::pointerTo(this->m_model); }
935 auto model() const { return QRangeModelDetails::pointerTo(this->m_model); }
936
937 template <typename Model = ModelStorage>
938 ModelData(Model &&model)
940 {}
941 };
942} // namespace QRangeModelDetails
943
944class QRangeModel;
945// forward declare so that we can declare friends
946template <typename, typename, typename> class QRangeModelAdapter;
947
949{
950 using Self = QRangeModelImplBase;
952
953public:
954 // keep in sync with QRangeModel::AutoConnectPolicy
960
961 // overridable prototypes (quasi-pure-virtual methods)
963 bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
964 bool setData(const QModelIndex &index, const QVariant &data, int role);
965 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data);
966 bool clearItemData(const QModelIndex &index);
967 bool insertColumns(int column, int count, const QModelIndex &parent);
968 bool removeColumns(int column, int count, const QModelIndex &parent);
969 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn);
970 bool insertRows(int row, int count, const QModelIndex &parent);
971 bool removeRows(int row, int count, const QModelIndex &parent);
972 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow);
973
974 QModelIndex index(int row, int column, const QModelIndex &parent) const;
975 QModelIndex sibling(int row, int column, const QModelIndex &index) const;
976 int rowCount(const QModelIndex &parent) const;
977 int columnCount(const QModelIndex &parent) const;
978 Qt::ItemFlags flags(const QModelIndex &index) const;
979 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
980 QVariant data(const QModelIndex &index, int role) const;
981 QMap<int, QVariant> itemData(const QModelIndex &index) const;
982 inline QHash<int, QByteArray> roleNames() const;
983 QModelIndex parent(const QModelIndex &child) const;
984
985 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
987
988 // bindings for overriding
989
1001
1008 using Data = Method<&Self::data>;
1012
1013 // 6.11
1016
1017 template <typename C>
1018 using MethodTemplates = std::tuple<
1019 typename C::Destroy,
1020 typename C::InvalidateCaches,
1021 typename C::SetHeaderData,
1022 typename C::SetData,
1023 typename C::SetItemData,
1024 typename C::ClearItemData,
1025 typename C::InsertColumns,
1026 typename C::RemoveColumns,
1027 typename C::MoveColumns,
1028 typename C::InsertRows,
1029 typename C::RemoveRows,
1030 typename C::MoveRows,
1031 typename C::Index,
1032 typename C::Parent,
1033 typename C::Sibling,
1034 typename C::RowCount,
1035 typename C::ColumnCount,
1036 typename C::Flags,
1037 typename C::HeaderData,
1038 typename C::Data,
1039 typename C::ItemData,
1040 typename C::RoleNames,
1041 typename C::MultiData,
1042 typename C::SetAutoConnectPolicy
1043 >;
1044
1045 static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
1046 static Q_CORE_EXPORT const QRangeModelImplBase *getImplementation(const QRangeModel *model);
1047
1048private:
1049 friend class QRangeModelPrivate;
1051
1052 QRangeModel *m_rangeModel;
1053
1054protected:
1055 explicit QRangeModelImplBase(QRangeModel *itemModel)
1056 : m_rangeModel(itemModel)
1057 {}
1058
1059 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
1060 inline void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to);
1061 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
1062 const QList<int> &roles);
1063 inline void beginResetModel();
1064 inline void endResetModel();
1065 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
1066 inline void endInsertColumns();
1067 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
1068 inline void endRemoveColumns();
1069 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1070 const QModelIndex &destParent, int destRow);
1071 inline void endMoveColumns();
1072 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
1073 inline void endInsertRows();
1074 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
1075 inline void endRemoveRows();
1076 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1077 const QModelIndex &destParent, int destRow);
1078 inline void endMoveRows();
1079 inline AutoConnectPolicy autoConnectPolicy() const;
1080
1081public:
1082 inline QAbstractItemModel &itemModel();
1083 inline const QAbstractItemModel &itemModel() const;
1084
1085 // implemented in qrangemodel.cpp
1087 const QMetaObject &metaObject);
1089
1090protected:
1091 Q_CORE_EXPORT QScopedValueRollback<bool> blockDataChangedDispatch();
1092
1094 const QMetaObject &metaObject);
1096 Q_CORE_EXPORT static bool connectProperty(const QModelIndex &index, const QObject *item,
1097 QRangeModelDetails::AutoConnectContext *context,
1098 int role, const QMetaProperty &property);
1099 Q_CORE_EXPORT static bool connectPropertyConst(const QModelIndex &index, const QObject *item,
1100 QRangeModelDetails::AutoConnectContext *context,
1101 int role, const QMetaProperty &property);
1102 Q_CORE_EXPORT static bool connectProperties(const QModelIndex &index, const QObject *item,
1103 QRangeModelDetails::AutoConnectContext *context,
1104 const QHash<int, QMetaProperty> &properties);
1105 Q_CORE_EXPORT static bool connectPropertiesConst(const QModelIndex &index, const QObject *item,
1106 QRangeModelDetails::AutoConnectContext *context,
1107 const QHash<int, QMetaProperty> &properties);
1108};
1109
1110template <typename Structure, typename Range,
1111 typename Protocol = QRangeModelDetails::table_protocol_t<Range>>
1116{
1117public:
1127
1129 typename row_traits::item_type>>;
1131 && row_traits::hasMetaObject; // not treated as tuple
1132
1136 >,
1139 >
1140 >;
1142
1143 using const_row_reference = decltype(*std::declval<typename ModelData::const_iterator&>());
1144
1145 static_assert(!QRangeModelDetails::is_any_of<range_type, std::optional>() &&
1147 "Currently, std::optional is not supported for ranges and rows, as "
1148 "it has range semantics in c++26. Once the required behavior is clarified, "
1149 "std::optional for ranges and rows will be supported.");
1150
1151protected:
1152
1153 using Self = QRangeModelImpl<Structure, Range, Protocol>;
1155
1156 Structure& that() { return static_cast<Structure &>(*this); }
1157 const Structure& that() const { return static_cast<const Structure &>(*this); }
1158
1159 template <typename C>
1160 static constexpr int size(const C &c)
1161 {
1162 if (!QRangeModelDetails::isValid(c))
1163 return 0;
1164
1165 if constexpr (QRangeModelDetails::test_size<C>()) {
1166 using std::size;
1167 return int(size(c));
1168 } else {
1169#if defined(__cpp_lib_ranges)
1170 using std::ranges::distance;
1171#else
1172 using std::distance;
1173#endif
1174 using container_type = std::conditional_t<QRangeModelDetails::range_traits<C>::has_cbegin,
1175 const QRangeModelDetails::wrapped_t<C>,
1176 QRangeModelDetails::wrapped_t<C>>;
1177 container_type& container = const_cast<container_type &>(QRangeModelDetails::refTo(c));
1178 return int(distance(QRangeModelDetails::adl_begin(container),
1179 QRangeModelDetails::adl_end(container)));
1180 }
1181 }
1182
1185 static constexpr bool rows_are_owning_or_raw_pointers =
1188 static constexpr bool one_dimensional_range = static_column_count == 0;
1189
1191 {
1192 if constexpr (itemsAreQObjects || rowsAreQObjects)
1193 return this->blockDataChangedDispatch();
1194 else
1195 return false;
1196 }
1197
1198 // A row might be a value (or range of values), or a pointer.
1199 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
1202
1203 template <typename T>
1206
1207 // A iterator type to use as the input iterator with the
1208 // range_type::insert(pos, start, end) overload if available (it is in
1209 // std::vector, but not in QList). Generates a prvalue when dereferenced,
1210 // which then gets moved into the newly constructed row, which allows us to
1211 // implement insertRows() for move-only row types.
1213 {
1217 using iterator_category = std::input_iterator_tag;
1218 using difference_type = int;
1219
1220 value_type operator*() { return impl->makeEmptyRow(parentRow); }
1221 EmptyRowGenerator &operator++() { ++n; return *this; }
1222 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1223 { return lhs.n == rhs.n; }
1224 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1225 { return !(lhs == rhs); }
1226
1228 Structure *impl = nullptr;
1229 const row_ptr parentRow = nullptr;
1230 };
1231
1232 // If we have a move-only row_type and can add/remove rows, then the range
1233 // must have an insert-from-range overload.
1236 "The range holding a move-only row-type must support insert(pos, start, end)");
1237
1239
1240public:
1241 static constexpr bool isMutable()
1242 {
1243 return range_features::is_mutable && row_features::is_mutable
1244 && std::is_reference_v<row_reference>
1245 && Structure::is_mutable_impl;
1246 }
1247 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
1248 static constexpr bool dynamicColumns() { return static_column_count < 0; }
1249
1250 explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
1254 {
1255 }
1256
1257
1258 // static interface, called by QRangeModelImplBase
1259
1260 void invalidateCaches() { m_data.invalidateCaches(); }
1261
1262 // Not implemented
1263 bool setHeaderData(int , Qt::Orientation , const QVariant &, int ) { return false; }
1264
1265 // actual implementations
1266 QModelIndex index(int row, int column, const QModelIndex &parent) const
1267 {
1268 if (row < 0 || column < 0 || column >= columnCount(parent)
1269 || row >= rowCount(parent)) {
1270 return {};
1271 }
1272
1273 return that().indexImpl(row, column, parent);
1274 }
1275
1276 QModelIndex sibling(int row, int column, const QModelIndex &index) const
1277 {
1278 if (row == index.row() && column == index.column())
1279 return index;
1280
1281 if (column < 0 || column >= this->columnCount({}))
1282 return {};
1283
1284 if (row == index.row())
1285 return this->createIndex(row, column, index.constInternalPointer());
1286
1287 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1288 const auto siblingCount = size(that().childrenOf(parentRow));
1289 if (row < 0 || row >= int(siblingCount))
1290 return {};
1291 return this->createIndex(row, column, parentRow);
1292 }
1293
1294 Qt::ItemFlags flags(const QModelIndex &index) const
1295 {
1296 if (!index.isValid())
1297 return Qt::NoItemFlags;
1298
1299 Qt::ItemFlags f = Structure::defaultFlags();
1300
1301 if constexpr (isMutable()) {
1302 if constexpr (row_traits::hasMetaObject) {
1303 if (index.column() < row_traits::fixed_size()) {
1304 const QMetaObject mo = wrapped_row_type::staticMetaObject;
1305 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
1306 if (prop.isWritable())
1307 f |= Qt::ItemIsEditable;
1308 }
1309 } else if constexpr (static_column_count <= 0) {
1310 f |= Qt::ItemIsEditable;
1311 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
1312 // we want to know if the elements in the tuple are const; they'd always be, if
1313 // we didn't remove the const of the range first.
1314 const_row_reference row = rowData(index);
1315 row_reference mutableRow = const_cast<row_reference>(row);
1316 if (QRangeModelDetails::isValid(mutableRow)) {
1317 row_traits::for_element_at(mutableRow, index.column(), [&f](auto &&ref){
1318 using target_type = decltype(ref);
1319 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
1320 f &= ~Qt::ItemIsEditable;
1321 else if constexpr (std::is_lvalue_reference_v<target_type>)
1322 f |= Qt::ItemIsEditable;
1323 });
1324 } else {
1325 // If there's no usable value stored in the row, then we can't
1326 // do anything with this item.
1327 f &= ~Qt::ItemIsEditable;
1328 }
1329 }
1330 }
1331 return f;
1332 }
1333
1334 QVariant headerData(int section, Qt::Orientation orientation, int role) const
1335 {
1336 QVariant result;
1337 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
1338 || section < 0 || section >= columnCount({})) {
1339 return this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1340 }
1341
1342 result = row_traits::column_name(section);
1343 if (!result.isValid())
1344 result = this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1345 return result;
1346 }
1347
1348 QVariant data(const QModelIndex &index, int role) const
1349 {
1350 if (!index.isValid())
1351 return {};
1352
1353 QModelRoleData result(role);
1354 multiData(index, result);
1355 return std::move(result.data());
1356 }
1357
1358 static constexpr bool isRangeModelRole(int role)
1359 {
1360 return role == Qt::RangeModelDataRole
1361 || role == Qt::RangeModelAdapterRole;
1362 }
1363
1364 static constexpr bool isPrimaryRole(int role)
1365 {
1366 return role == Qt::DisplayRole || role == Qt::EditRole;
1367 }
1368
1369 QMap<int, QVariant> itemData(const QModelIndex &index) const
1370 {
1371 QMap<int, QVariant> result;
1372
1373 if (index.isValid()) {
1374 bool tried = false;
1375
1376 // optimisation for items backed by a QMap<int, QVariant> or equivalent
1377 readAt(index, [&result, &tried](const auto &value) {
1378 if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) {
1379 tried = true;
1380 result = value;
1381 }
1382 });
1383 if (!tried) {
1384 const auto roles = this->itemModel().roleNames().keys();
1385 QVarLengthArray<QModelRoleData, 16> roleDataArray;
1386 roleDataArray.reserve(roles.size());
1387 for (auto role : roles) {
1388 if (isRangeModelRole(role))
1389 continue;
1390 roleDataArray.emplace_back(role);
1391 }
1392 QModelRoleDataSpan roleDataSpan(roleDataArray);
1393 multiData(index, roleDataSpan);
1394
1395 for (auto &&roleData : std::move(roleDataSpan)) {
1396 QVariant data = roleData.data();
1397 if (data.isValid())
1398 result[roleData.role()] = std::move(data);
1399 }
1400 }
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 const auto readModelData = [&value](QModelRoleData &roleData){
1416 const int role = roleData.role();
1417 if (role == Qt::RangeModelDataRole) {
1418 // Qt QML support: "modelData" role returns the entire multi-role item.
1419 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1420 // only by value (so we take the reference).
1421 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1422 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1423 else
1424 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1425 } else if (role == Qt::RangeModelAdapterRole) {
1426 // for QRangeModelAdapter however, we want to respect smart pointer wrappers
1427 if constexpr (std::is_copy_assignable_v<value_type>)
1428 roleData.setData(QVariant::fromValue(value));
1429 else
1430 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1431 } else {
1432 return false;
1433 }
1434 return true;
1435 };
1436
1437 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1438 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1439 tried = true;
1440 for (auto &roleData : roleDataSpan) {
1441 if (!readModelData(roleData)) {
1442 roleData.setData(ItemAccess::readRole(QRangeModelDetails::refTo(value),
1443 roleData.role()));
1444 }
1445 }
1446 } else if constexpr (multi_role()) {
1447 tried = true;
1448 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1449 Q_UNUSED(this);
1450 if constexpr (!multi_role::int_key)
1451 return this->itemModel().roleNames();
1452 else
1453 return {};
1454 }();
1455 using key_type = typename value_type::key_type;
1456 for (auto &roleData : roleDataSpan) {
1457 const auto &it = [&roleNames, &value, role = roleData.role()]{
1458 Q_UNUSED(roleNames);
1459 if constexpr (multi_role::int_key)
1460 return value.find(key_type(role));
1461 else
1462 return value.find(roleNames.value(role));
1463 }();
1464 if (it != QRangeModelDetails::adl_end(value))
1465 roleData.setData(QRangeModelDetails::value(it));
1466 else
1467 roleData.clearData();
1468 }
1469 } else if constexpr (has_metaobject<value_type>) {
1470 if (row_traits::fixed_size() <= 1) {
1471 tried = true;
1472 for (auto &roleData : roleDataSpan) {
1473 if (!readModelData(roleData)) {
1474 roleData.setData(readRole(index, roleData.role(),
1475 QRangeModelDetails::pointerTo(value)));
1476 }
1477 }
1478 } else if (index.column() <= row_traits::fixed_size()) {
1479 tried = true;
1480 for (auto &roleData : roleDataSpan) {
1481 const int role = roleData.role();
1482 if (isPrimaryRole(role)) {
1483 roleData.setData(readProperty(index,
1484 QRangeModelDetails::pointerTo(value)));
1485 } else {
1486 roleData.clearData();
1487 }
1488 }
1489 }
1490 } else {
1491 tried = true;
1492 for (auto &roleData : roleDataSpan) {
1493 const int role = roleData.role();
1494 if (isPrimaryRole(role) || isRangeModelRole(role))
1495 roleData.setData(read(value));
1496 else
1497 roleData.clearData();
1498 }
1499 }
1500 });
1501
1502 Q_ASSERT(tried);
1503 }
1504
1505 bool setData(const QModelIndex &index, const QVariant &data, int role)
1506 {
1507 if (!index.isValid())
1508 return false;
1509
1510 bool success = false;
1511 if constexpr (isMutable()) {
1512 auto emitDataChanged = qScopeGuard([&success, this, &index, role]{
1513 if (success) {
1514 Q_EMIT this->dataChanged(index, index,
1515 role == Qt::EditRole || role == Qt::RangeModelDataRole
1516 || role == Qt::RangeModelAdapterRole
1517 ? QList<int>{} : QList<int>{role});
1518 }
1519 });
1520 // we emit dataChanged at the end, block dispatches from auto-connected properties
1521 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1522
1523 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1524 using value_type = q20::remove_cvref_t<decltype(target)>;
1525 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1526 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1527
1528 auto setRangeModelDataRole = [&target, &data]{
1529 constexpr auto targetMetaType = QMetaType::fromType<value_type>();
1530 const auto dataMetaType = data.metaType();
1531 constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
1532 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
1533 // we don't support replacing objects that are stored as raw pointers,
1534 // as this makes object ownership very messy. But we can replace objects
1535 // stored in smart pointers, and we can initialize raw nullptr objects.
1536 if constexpr (isWrapped) {
1537 constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
1538 if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
1539 if (data.canConvert(targetMetaType)) {
1540 target = data.value<value_type>();
1541 return true;
1542 }
1543 } else if constexpr (is_raw_pointer) {
1544 if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
1545 target = data.value<value_type>();
1546 return true;
1547 }
1548 } else {
1549 Q_UNUSED(target);
1550 }
1551 }
1552 // Otherwise we have a move-only or polymorph type. fall through to
1553 // error handling.
1554 } else if constexpr (isWrapped) {
1555 if (QRangeModelDetails::isValid(target)) {
1556 auto &targetRef = QRangeModelDetails::refTo(target);
1557 // we need to get a wrapped value type out of the QVariant, which
1558 // might carry a pointer. We have to try all alternatives.
1559 if (const auto mt = QMetaType::fromType<wrapped_value_type>();
1560 data.canConvert(mt)) {
1561 targetRef = data.value<wrapped_value_type>();
1562 return true;
1563 } else if (const auto mtp = QMetaType::fromType<wrapped_value_type *>();
1564 data.canConvert(mtp)) {
1565 targetRef = *data.value<wrapped_value_type *>();
1566 return true;
1567 }
1568 }
1569 } else if (targetMetaType == dataMetaType) {
1570 QRangeModelDetails::refTo(target) = data.value<value_type>();
1571 return true;
1572 } else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1573 QRangeModelDetails::refTo(target) = *data.value<value_type *>();
1574 return true;
1575 }
1576#ifndef QT_NO_DEBUG
1577 qCritical("Not able to assign %s to %s",
1578 qPrintable(QDebug::toString(data)), targetMetaType.name());
1579#endif
1580 return false;
1581 };
1582
1583 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1584 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1585 if (isRangeModelRole(role))
1586 return setRangeModelDataRole();
1587 return ItemAccess::writeRole(QRangeModelDetails::refTo(target), data, role);
1588 } else if constexpr (has_metaobject<value_type>) {
1589 if (row_traits::fixed_size() <= 1) { // multi-role value
1590 if (isRangeModelRole(role))
1591 return setRangeModelDataRole();
1592 return writeRole(role, QRangeModelDetails::pointerTo(target), data);
1593 } else if (column <= row_traits::fixed_size() // multi-column
1594 && (isPrimaryRole(role) || isRangeModelRole(role))) {
1595 return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
1596 }
1597 } else if constexpr (multi_role::value) {
1598 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1599 // If there is an entry for EditRole, overwrite that; otherwise,
1600 // set the entry for DisplayRole.
1601 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1602 Q_UNUSED(this);
1603 if constexpr (!multi_role::int_key)
1604 return this->itemModel().roleNames();
1605 else
1606 return {};
1607 }();
1608 if (role == Qt::EditRole) {
1609 if constexpr (multi_role::int_key) {
1610 if (target.find(roleToSet) == target.end())
1611 roleToSet = Qt::DisplayRole;
1612 } else {
1613 if (target.find(roleNames.value(roleToSet)) == target.end())
1614 roleToSet = Qt::DisplayRole;
1615 }
1616 }
1617 if constexpr (multi_role::int_key)
1618 return write(target[roleToSet], data);
1619 else
1620 return write(target[roleNames.value(roleToSet)], data);
1621 } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
1622 return write(target, data);
1623 }
1624 return false;
1625 };
1626
1627 success = writeAt(index, writeData);
1628
1629 if constexpr (itemsAreQObjects || rowsAreQObjects) {
1630 if (success && isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
1631 if (QObject *item = data.value<QObject *>())
1632 Self::connectProperties(index, item, m_data.context, m_data.properties);
1633 }
1634 }
1635 }
1636 return success;
1637 }
1638
1639 template <typename LHS, typename RHS>
1640 void updateTarget(LHS &org, RHS &&copy) noexcept
1641 {
1642 if constexpr (std::is_pointer_v<RHS>) {
1643 return;
1644 } else {
1645 using std::swap;
1646 if constexpr (std::is_assignable_v<LHS, RHS>)
1647 org = std::forward<RHS>(copy);
1648 else
1649 qSwap(org, copy);
1650 }
1651 }
1652 template <typename LHS, typename RHS>
1653 void updateTarget(LHS *org, RHS &&copy) noexcept
1654 {
1655 updateTarget(*org, std::forward<RHS>(copy));
1656 }
1657
1658 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1659 {
1660 if (!index.isValid() || data.isEmpty())
1661 return false;
1662
1663 bool success = false;
1664 if constexpr (isMutable()) {
1665 auto emitDataChanged = qScopeGuard([&success, this, &index, &data]{
1666 if (success)
1667 Q_EMIT this->dataChanged(index, index, data.keys());
1668 });
1669 // we emit dataChanged at the end, block dispatches from auto-connected properties
1670 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1671
1672 bool tried = false;
1673 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
1674 Q_UNUSED(this);
1675 using value_type = q20::remove_cvref_t<decltype(target)>;
1676 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1677 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1678
1679 // transactional: if possible, modify a copy and only
1680 // update target if all values from data could be stored.
1681 auto makeCopy = [](const value_type &original){
1682 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>)
1683 return QRangeModelDetails::pointerTo(original); // no transaction support
1684 else if constexpr (std::is_pointer_v<decltype(original)>)
1685 return *original;
1686 else if constexpr (std::is_copy_assignable_v<value_type>)
1687 return original;
1688 else
1689 return QRangeModelDetails::pointerTo(original);
1690 };
1691
1692 const auto roleNames = this->itemModel().roleNames();
1693
1694 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1695 tried = true;
1696 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1697 const auto roles = roleNames.keys();
1698 auto targetCopy = makeCopy(target);
1699 for (int role : roles) {
1700 if (!ItemAccess::writeRole(QRangeModelDetails::refTo(targetCopy),
1701 data.value(role), role)) {
1702 return false;
1703 }
1704 }
1705 updateTarget(target, std::move(targetCopy));
1706 return true;
1707 } else if constexpr (multi_role()) {
1708 using key_type = typename value_type::key_type;
1709 tried = true;
1710 const auto roleName = [&roleNames](int role) {
1711 return roleNames.value(role);
1712 };
1713
1714 // transactional: only update target if all values from data
1715 // can be stored. Storing never fails with int-keys.
1716 if constexpr (!multi_role::int_key)
1717 {
1718 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
1719 [&roleName](int role) { return roleName(role).isEmpty(); }
1720 );
1721
1722 if (invalid != data.keyEnd()) {
1723#ifndef QT_NO_DEBUG
1724 qWarning("No role name set for %d", *invalid);
1725#endif
1726 return false;
1727 }
1728 }
1729
1730 for (auto &&[role, value] : data.asKeyValueRange()) {
1731 if constexpr (multi_role::int_key)
1732 target[static_cast<key_type>(role)] = value;
1733 else
1734 target[QString::fromUtf8(roleName(role))] = value;
1735 }
1736 return true;
1737 } else if constexpr (has_metaobject<value_type>) {
1738 if (row_traits::fixed_size() <= 1) {
1739 tried = true;
1740 auto targetCopy = makeCopy(target);
1741 for (auto &&[role, value] : data.asKeyValueRange()) {
1742 if (isRangeModelRole(role))
1743 continue;
1744 if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
1745 const QByteArray roleName = roleNames.value(role);
1746#ifndef QT_NO_DEBUG
1747 qWarning("Failed to write value '%s' to role '%s'",
1748 qPrintable(QDebug::toString(value)), roleName.data());
1749#endif
1750 return false;
1751 }
1752 }
1753 updateTarget(target, std::move(targetCopy));
1754 return true;
1755 }
1756 }
1757 return false;
1758 };
1759
1760 success = writeAt(index, writeItemData);
1761
1762 if (!tried) {
1763 // setItemData will emit the dataChanged signal
1764 Q_ASSERT(!success);
1765 emitDataChanged.dismiss();
1766 success = this->itemModel().QAbstractItemModel::setItemData(index, data);
1767 }
1768 }
1769 return success;
1770 }
1771
1772 bool clearItemData(const QModelIndex &index)
1773 {
1774 if (!index.isValid())
1775 return false;
1776
1777 bool success = false;
1778 if constexpr (isMutable()) {
1779 auto emitDataChanged = qScopeGuard([&success, this, &index]{
1780 if (success)
1781 Q_EMIT this->dataChanged(index, index, {});
1782 });
1783
1784 auto clearData = [column = index.column()](auto &&target) {
1785 if constexpr (row_traits::hasMetaObject) {
1786 if (row_traits::fixed_size() <= 1) {
1787 // multi-role object/gadget: reset all properties
1788 return resetProperty(-1, QRangeModelDetails::pointerTo(target));
1789 } else if (column <= row_traits::fixed_size()) {
1790 return resetProperty(column, QRangeModelDetails::pointerTo(target));
1791 }
1792 } else { // normal structs, values, associative containers
1793 target = {};
1794 return true;
1795 }
1796 return false;
1797 };
1798
1799 success = writeAt(index, clearData);
1800 }
1801 return success;
1802 }
1803
1805 {
1806 // will be 'void' if columns don't all have the same type
1807 using item_type = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
1808 using item_traits = typename QRangeModelDetails::item_traits<item_type>;
1809 return item_traits::roleNames(this);
1810 }
1811
1812 bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
1813 {
1814 if (!QRangeModelDetails::isValid(row))
1815 return true; // nothing to do
1816 return row_traits::for_each_element(QRangeModelDetails::refTo(row),
1817 this->itemModel().index(rowIndex, 0, parent),
1818 [this](const QModelIndex &index, const QObject *item) {
1819 if constexpr (isMutable())
1820 return Self::connectProperties(index, item, m_data.context, m_data.properties);
1821 else
1822 return Self::connectPropertiesConst(index, item, m_data.context, m_data.properties);
1823 });
1824 }
1825
1826 void clearConnectionInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
1827 {
1828 if (!QRangeModelDetails::isValid(row))
1829 return;
1830 row_traits::for_each_element(QRangeModelDetails::refTo(row),
1831 this->itemModel().index(rowIndex, 0, parent),
1832 [this](const QModelIndex &, const QObject *item) {
1833 m_data.connections.removeIf([item](const auto &connection) {
1834 return connection.sender == item;
1835 });
1836 return true;
1837 });
1838 }
1839
1841 {
1842 if constexpr (itemsAreQObjects || rowsAreQObjects) {
1843 using item_type = std::remove_pointer_t<typename row_traits::item_type>;
1844 using Mapping = QRangeModelDetails::AutoConnectContext::AutoConnectMapping;
1845
1846 delete m_data.context;
1847 m_data.connections = {};
1848 switch (this->autoConnectPolicy()) {
1849 case AutoConnectPolicy::None:
1850 m_data.context = nullptr;
1851 break;
1852 case AutoConnectPolicy::Full:
1853 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
1854 if constexpr (itemsAreQObjects) {
1855 m_data.properties = QRangeModelImplBase::roleProperties(this->itemModel(),
1856 item_type::staticMetaObject);
1857 m_data.context->mapping = Mapping::Roles;
1858 } else {
1859 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
1860 m_data.context->mapping = Mapping::Columns;
1861 }
1862 if (!m_data.properties.isEmpty())
1863 that().autoConnectPropertiesImpl();
1864 break;
1865 case AutoConnectPolicy::OnRead:
1866 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
1867 if constexpr (itemsAreQObjects) {
1868 m_data.context->mapping = Mapping::Roles;
1869 } else {
1870 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
1871 m_data.context->mapping = Mapping::Columns;
1872 }
1873 break;
1874 }
1875 } else {
1876#ifndef QT_NO_DEBUG
1877 qWarning("All items in the range must be QObject subclasses");
1878#endif
1879 }
1880 }
1881
1882 template <typename InsertFn>
1883 bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
1884 {
1885 if (count == 0)
1886 return false;
1887 range_type * const children = childRange(parent);
1888 if (!children)
1889 return false;
1890
1891 this->beginInsertColumns(parent, column, column + count - 1);
1892
1893 for (auto &child : *children) {
1894 auto it = QRangeModelDetails::pos(child, column);
1895 (void)insertFn(QRangeModelDetails::refTo(child), it, count);
1896 }
1897
1898 this->endInsertColumns();
1899
1900 // endInsertColumns emits columnsInserted, at which point clients might
1901 // have populated the new columns with objects (if the columns aren't objects
1902 // themselves).
1903 if constexpr (itemsAreQObjects) {
1904 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
1905 for (int r = 0; r < that().rowCount(parent); ++r) {
1906 for (int c = column; c < column + count; ++c) {
1907 const QModelIndex index = that().index(r, c, parent);
1908 writeAt(index, [this, &index](QObject *item){
1909 return Self::connectProperties(index, item,
1910 m_data.context, m_data.properties);
1911 });
1912 }
1913 }
1914 }
1915 }
1916
1917 return true;
1918 }
1919
1920 bool insertColumns(int column, int count, const QModelIndex &parent)
1921 {
1922 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
1923 return doInsertColumns(column, count, parent, [](auto &row, auto it, int n){
1924 row.insert(it, n, {});
1925 return true;
1926 });
1927 } else {
1928 return false;
1929 }
1930 }
1931
1932 bool removeColumns(int column, int count, const QModelIndex &parent)
1933 {
1934 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
1935 if (column < 0 || column + count > columnCount(parent))
1936 return false;
1937
1938 range_type * const children = childRange(parent);
1939 if (!children)
1940 return false;
1941
1942 if constexpr (itemsAreQObjects) {
1943 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
1944 for (int r = 0; r < that().rowCount(parent); ++r) {
1945 for (int c = column; c < column + count; ++c) {
1946 const QModelIndex index = that().index(r, c, parent);
1947 writeAt(index, [this](QObject *item){
1948 m_data.connections.removeIf([item](const auto &connection) {
1949 return connection.sender == item;
1950 });
1951 return true;
1952 });
1953 }
1954 }
1955 }
1956 }
1957
1958 this->beginRemoveColumns(parent, column, column + count - 1);
1959 for (auto &child : *children) {
1960 const auto start = QRangeModelDetails::pos(child, column);
1961 QRangeModelDetails::refTo(child).erase(start, std::next(start, count));
1962 }
1963 this->endRemoveColumns();
1964 return true;
1965 }
1966 return false;
1967 }
1968
1969 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1970 const QModelIndex &destParent, int destColumn)
1971 {
1972 // we only support moving columns within the same parent
1973 if (sourceParent != destParent)
1974 return false;
1975 if constexpr (isMutable() && (row_features::has_rotate || row_features::has_splice)) {
1976 if (!Structure::canMoveColumns(sourceParent, destParent))
1977 return false;
1978
1979 if constexpr (dynamicColumns()) {
1980 // we only support ranges as columns, as other types might
1981 // not have the same data type across all columns
1982 range_type * const children = childRange(sourceParent);
1983 if (!children)
1984 return false;
1985
1986 if (!this->beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
1987 destParent, destColumn)) {
1988 return false;
1989 }
1990
1991 for (auto &child : *children)
1992 QRangeModelDetails::rotate(child, sourceColumn, count, destColumn);
1993
1994 this->endMoveColumns();
1995 return true;
1996 }
1997 }
1998 return false;
1999 }
2000
2001 template <typename InsertFn>
2002 bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
2003 {
2004 range_type *children = childRange(parent);
2005 if (!children)
2006 return false;
2007
2008 this->beginInsertRows(parent, row, row + count - 1);
2009
2010 row_ptr parentRow = parent.isValid()
2011 ? QRangeModelDetails::pointerTo(this->rowData(parent))
2012 : nullptr;
2013 (void)std::forward<InsertFn>(insertFn)(*children, parentRow, row, count);
2014
2015 // fix the parent in all children of the modified row, as the
2016 // references back to the parent might have become invalid.
2017 that().resetParentInChildren(children);
2018
2019 this->endInsertRows();
2020
2021 // endInsertRows emits rowsInserted, at which point clients might
2022 // have populated the new row with objects (if the rows aren't objects
2023 // themselves).
2024 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2025 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2026 const auto begin = QRangeModelDetails::pos(children, row);
2027 const auto end = std::next(begin, count);
2028 int rowIndex = row;
2029 for (auto it = begin; it != end; ++it, ++rowIndex)
2030 autoConnectPropertiesInRow(*it, rowIndex, parent);
2031 }
2032 }
2033
2034 return true;
2035 }
2036
2037 bool insertRows(int row, int count, const QModelIndex &parent)
2038 {
2039 if constexpr (canInsertRows()) {
2040 return doInsertRows(row, count, parent,
2041 [this](range_type &children, row_ptr parentRow, int r, int n){
2042 EmptyRowGenerator generator{0, &that(), parentRow};
2043
2044 const auto pos = QRangeModelDetails::pos(children, r);
2045 if constexpr (range_features::has_insert_range) {
2046 children.insert(pos, std::move(generator), EmptyRowGenerator{n});
2047 } else if constexpr (rows_are_owning_or_raw_pointers) {
2048 auto start = children.insert(pos, n, nullptr); // MSVC doesn't like row_type{}
2049 std::copy(std::move(generator), EmptyRowGenerator{n}, start);
2050 } else {
2051 children.insert(pos, n, std::move(*generator));
2052 }
2053 return true;
2054 });
2055 } else {
2056 return false;
2057 }
2058 }
2059
2060 bool removeRows(int row, int count, const QModelIndex &parent = {})
2061 {
2062 if constexpr (canRemoveRows()) {
2063 const int prevRowCount = rowCount(parent);
2064 if (row < 0 || row + count > prevRowCount)
2065 return false;
2066
2067 range_type *children = childRange(parent);
2068 if (!children)
2069 return false;
2070
2071 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2072 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2073 const auto begin = QRangeModelDetails::pos(children, row);
2074 const auto end = std::next(begin, count);
2075 int rowIndex = row;
2076 for (auto it = begin; it != end; ++it, ++rowIndex)
2077 clearConnectionInRow(*it, rowIndex, parent);
2078 }
2079 }
2080
2081 this->beginRemoveRows(parent, row, row + count - 1);
2082 [[maybe_unused]] bool callEndRemoveColumns = false;
2083 if constexpr (dynamicColumns()) {
2084 // if we remove the last row in a dynamic model, then we no longer
2085 // know how many columns we should have, so they will be reported as 0.
2086 if (prevRowCount == count) {
2087 if (const int columns = columnCount(parent)) {
2088 callEndRemoveColumns = true;
2089 this->beginRemoveColumns(parent, 0, columns - 1);
2090 }
2091 }
2092 }
2093 { // erase invalidates iterators
2094 const auto begin = QRangeModelDetails::pos(children, row);
2095 const auto end = std::next(begin, count);
2096 that().deleteRemovedRows(begin, end);
2097 children->erase(begin, end);
2098 }
2099 // fix the parent in all children of the modified row, as the
2100 // references back to the parent might have become invalid.
2101 that().resetParentInChildren(children);
2102
2103 if constexpr (dynamicColumns()) {
2104 if (callEndRemoveColumns) {
2105 Q_ASSERT(columnCount(parent) == 0);
2106 this->endRemoveColumns();
2107 }
2108 }
2109 this->endRemoveRows();
2110 return true;
2111 } else {
2112 return false;
2113 }
2114 }
2115
2116 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
2117 const QModelIndex &destParent, int destRow)
2118 {
2119 if constexpr (isMutable() && (range_features::has_rotate || range_features::has_splice)) {
2120 if (!Structure::canMoveRows(sourceParent, destParent))
2121 return false;
2122
2123 if (sourceParent != destParent) {
2124 return that().moveRowsAcross(sourceParent, sourceRow, count,
2125 destParent, destRow);
2126 }
2127
2128 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
2129 || sourceRow < 0 || sourceRow + count - 1 >= this->rowCount(sourceParent)
2130 || destRow < 0 || destRow > this->rowCount(destParent)) {
2131 return false;
2132 }
2133
2134 range_type *source = childRange(sourceParent);
2135 // moving within the same range
2136 if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
2137 return false;
2138
2139 QRangeModelDetails::rotate(source, sourceRow, count, destRow);
2140
2141 that().resetParentInChildren(source);
2142
2143 this->endMoveRows();
2144 return true;
2145 } else {
2146 return false;
2147 }
2148 }
2149
2150 const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2151 protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2152
2153 QModelIndex parent(const QModelIndex &child) const { return that().parentImpl(child); }
2154
2155 int rowCount(const QModelIndex &parent) const { return that().rowCountImpl(parent); }
2156
2157 static constexpr int fixedColumnCount()
2158 {
2159 if constexpr (one_dimensional_range)
2160 return row_traits::fixed_size();
2161 else
2162 return static_column_count;
2163 }
2164 int columnCount(const QModelIndex &parent) const { return that().columnCountImpl(parent); }
2165
2166 void destroy() { delete std::addressof(that()); }
2167
2168 template <typename BaseMethod, typename BaseMethod::template Overridden<Self> overridden>
2169 using Override = typename Ancestor::template Override<BaseMethod, overridden>;
2170
2179
2194
2198
2199protected:
2201 {
2203 }
2204
2206 {
2207 // We delete row objects if we are not operating on a reference or pointer
2208 // to a range, as in that case, the owner of the referenced/pointed to
2209 // range also owns the row entries.
2210 // ### Problem: if we get a copy of a range (no matter if shared or not),
2211 // then adding rows will create row objects in the model's copy, and the
2212 // client can never delete those. But copied rows will be the same pointer,
2213 // which we must not delete (as we didn't create them).
2214
2215 static constexpr bool modelCopied = !QRangeModelDetails::is_wrapped<Range>() &&
2216 (std::is_reference_v<Range> || std::is_const_v<std::remove_reference_t<Range>>);
2217
2218 static constexpr bool modelShared = QRangeModelDetails::is_any_shared_ptr<Range>();
2219
2220 static constexpr bool default_row_deleter = protocol_traits::is_default &&
2221 protocol_traits::has_deleteRow;
2222
2223 static constexpr bool ambiguousRowOwnership = (modelCopied || modelShared) &&
2224 rows_are_raw_pointers && default_row_deleter;
2225
2226 static_assert(!ambiguousRowOwnership,
2227 "Using of copied and shared tree and table models with rows as raw pointers, "
2228 "and the default protocol is not allowed due to ambiguity of rows ownership. "
2229 "Move the model in, use another row type, or implement a custom tree protocol.");
2230
2231 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
2232 && !QRangeModelDetails::is_any_of<Range, std::reference_wrapper>()) {
2233 const auto begin = QRangeModelDetails::adl_begin(*m_data.model());
2234 const auto end = QRangeModelDetails::adl_end(*m_data.model());
2235 that().deleteRemovedRows(begin, end);
2236 }
2237 }
2238
2239 static constexpr bool canInsertRows()
2240 {
2241 if constexpr (dynamicColumns() && !row_features::has_resize) {
2242 // If we operate on dynamic columns and cannot resize a newly
2243 // constructed row, then we cannot insert.
2244 return false;
2245 } else if constexpr (!protocol_traits::has_newRow) {
2246 // We also cannot insert if we cannot create a new row element
2247 return false;
2248 } else if constexpr (!range_features::has_insert_range
2249 && !std::is_copy_constructible_v<row_type>) {
2250 // And if the row is a move-only type, then the range needs to be
2251 // backed by a container that can move-insert default-constructed
2252 // row elements.
2253 return false;
2254 } else {
2255 return Structure::canInsertRowsImpl();
2256 }
2257 }
2258
2259 static constexpr bool canRemoveRows()
2260 {
2261 return Structure::canRemoveRowsImpl();
2262 }
2263
2264 template <typename F>
2265 bool writeAt(const QModelIndex &index, F&& writer)
2266 {
2267 bool result = false;
2268 row_reference row = rowData(index);
2269
2270 if (QRangeModelDetails::isValid(row)) {
2271 row_traits::for_element_at(row, index.column(), [&writer, &result](auto &&target) {
2272 using target_type = decltype(target);
2273 // we can only assign to an lvalue reference
2274 if constexpr (std::is_lvalue_reference_v<target_type>
2275 && !std::is_const_v<std::remove_reference_t<target_type>>) {
2276 result = writer(std::forward<target_type>(target));
2277 }
2278 });
2279 }
2280
2281 return result;
2282 }
2283
2284 template <typename F>
2285 void readAt(const QModelIndex &index, F&& reader) const {
2286 const_row_reference row = rowData(index);
2287 if (QRangeModelDetails::isValid(row))
2288 row_traits::for_element_at(row, index.column(), std::forward<F>(reader));
2289 }
2290
2291 template <typename Value>
2292 static QVariant read(const Value &value)
2293 {
2294 if constexpr (std::is_constructible_v<QVariant, Value>)
2295 return QVariant(value);
2296 else
2297 return QVariant::fromValue(value);
2298 }
2299 template <typename Value>
2300 static QVariant read(Value *value)
2301 {
2302 if (value) {
2303 if constexpr (std::is_constructible_v<QVariant, Value *>)
2304 return QVariant(value);
2305 else
2306 return read(*value);
2307 }
2308 return {};
2309 }
2310
2311 template <typename Target>
2312 static bool write(Target &target, const QVariant &value)
2313 {
2314 using Type = std::remove_reference_t<Target>;
2315 if constexpr (std::is_constructible_v<Target, QVariant>) {
2316 target = value;
2317 return true;
2318 } else if (value.canConvert<Type>()) {
2319 target = value.value<Type>();
2320 return true;
2321 }
2322 return false;
2323 }
2324 template <typename Target>
2325 static bool write(Target *target, const QVariant &value)
2326 {
2327 if (target)
2328 return write(*target, value);
2329 return false;
2330 }
2331
2332 template <typename ItemType>
2334 {
2335 struct {
2336 operator QMetaProperty() const {
2337 const QByteArray roleName = that.itemModel().roleNames().value(role);
2338 const QMetaObject &mo = ItemType::staticMetaObject;
2339 if (const int index = mo.indexOfProperty(roleName.data());
2340 index >= 0) {
2341 return mo.property(index);
2342 }
2343 return {};
2344 }
2345 const QRangeModelImpl &that;
2346 const int role;
2347 } findProperty{*this, role};
2348
2349 if constexpr (ModelData::cachesProperties)
2350 return *m_data.properties.tryEmplace(role, findProperty).iterator;
2351 else
2352 return findProperty;
2353 }
2354
2355 void connectPropertyOnRead(const QModelIndex &index, int role,
2356 const QObject *gadget, const QMetaProperty &prop) const
2357 {
2358 const typename ModelData::Connection connection = {gadget, role};
2359 if (prop.hasNotifySignal() && this->autoConnectPolicy() == AutoConnectPolicy::OnRead
2360 && !m_data.connections.contains(connection)) {
2361 if constexpr (isMutable())
2362 Self::connectProperty(index, gadget, m_data.context, role, prop);
2363 else
2364 Self::connectPropertyConst(index, gadget, m_data.context, role, prop);
2365 m_data.connections.insert(connection);
2366 }
2367 }
2368
2369 template <typename ItemType>
2370 QVariant readRole(const QModelIndex &index, int role, ItemType *gadget) const
2371 {
2372 using item_type = std::remove_pointer_t<ItemType>;
2373 QVariant result;
2374 QMetaProperty prop = roleProperty<item_type>(role);
2375 if (!prop.isValid() && role == Qt::EditRole) {
2376 role = Qt::DisplayRole;
2377 prop = roleProperty<item_type>(Qt::DisplayRole);
2378 }
2379
2380 if (prop.isValid()) {
2381 if constexpr (itemsAreQObjects)
2382 connectPropertyOnRead(index, role, gadget, prop);
2383 result = readProperty(prop, gadget);
2384 }
2385 return result;
2386 }
2387
2388 template <typename ItemType>
2389 QVariant readRole(const QModelIndex &index, int role, const ItemType &gadget) const
2390 {
2391 return readRole(index, role, &gadget);
2392 }
2393
2394 template <typename ItemType>
2395 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
2396 {
2397 if constexpr (std::is_base_of_v<QObject, ItemType>)
2398 return prop.read(gadget);
2399 else
2400 return prop.readOnGadget(gadget);
2401 }
2402
2403 template <typename ItemType>
2404 QVariant readProperty(const QModelIndex &index, ItemType *gadget) const
2405 {
2406 using item_type = std::remove_pointer_t<ItemType>;
2407 const QMetaObject &mo = item_type::staticMetaObject;
2408 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
2409
2410 if constexpr (rowsAreQObjects)
2411 connectPropertyOnRead(index, Qt::DisplayRole, gadget, prop);
2412
2413 return readProperty(prop, gadget);
2414 }
2415
2416 template <typename ItemType>
2417 QVariant readProperty(const QModelIndex &index, const ItemType &gadget) const
2418 {
2419 return readProperty(index, &gadget);
2420 }
2421
2422 template <typename ItemType>
2423 bool writeRole(int role, ItemType *gadget, const QVariant &data)
2424 {
2425 using item_type = std::remove_pointer_t<ItemType>;
2426 auto prop = roleProperty<item_type>(role);
2427 if (!prop.isValid() && role == Qt::EditRole)
2428 prop = roleProperty<item_type>(Qt::DisplayRole);
2429
2430 return prop.isValid() ? writeProperty(prop, gadget, data) : false;
2431 }
2432
2433 template <typename ItemType>
2434 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
2435 {
2436 return writeRole(role, &gadget, data);
2437 }
2438
2439 template <typename ItemType>
2440 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
2441 {
2442 if constexpr (std::is_base_of_v<QObject, ItemType>)
2443 return prop.write(gadget, data);
2444 else
2445 return prop.writeOnGadget(gadget, data);
2446 }
2447 template <typename ItemType>
2448 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
2449 {
2450 using item_type = std::remove_pointer_t<ItemType>;
2451 const QMetaObject &mo = item_type::staticMetaObject;
2452 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
2453 }
2454
2455 template <typename ItemType>
2456 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
2457 {
2458 return writeProperty(property, &gadget, data);
2459 }
2460
2461 template <typename ItemType>
2462 static bool resetProperty(int property, ItemType *object)
2463 {
2464 using item_type = std::remove_pointer_t<ItemType>;
2465 const QMetaObject &mo = item_type::staticMetaObject;
2466 bool success = true;
2467 if (property == -1) {
2468 // reset all properties
2469 if constexpr (std::is_base_of_v<QObject, item_type>) {
2470 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
2471 success = writeProperty(mo.property(p), object, {}) && success;
2472 } else { // reset a gadget by assigning a default-constructed
2473 *object = {};
2474 }
2475 } else {
2476 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
2477 }
2478 return success;
2479 }
2480
2481 template <typename ItemType>
2482 static bool resetProperty(int property, ItemType &&object)
2483 {
2484 return resetProperty(property, &object);
2485 }
2486
2487 // helpers
2488 const_row_reference rowData(const QModelIndex &index) const
2489 {
2490 Q_ASSERT(index.isValid());
2491 return that().rowDataImpl(index);
2492 }
2493
2494 row_reference rowData(const QModelIndex &index)
2495 {
2496 Q_ASSERT(index.isValid());
2497 return that().rowDataImpl(index);
2498 }
2499
2500 const range_type *childRange(const QModelIndex &index) const
2501 {
2502 if (!index.isValid())
2503 return m_data.model();
2504 if (index.column()) // only items at column 0 can have children
2505 return nullptr;
2506 return that().childRangeImpl(index);
2507 }
2508
2509 range_type *childRange(const QModelIndex &index)
2510 {
2511 if (!index.isValid())
2512 return m_data.model();
2513 if (index.column()) // only items at column 0 can have children
2514 return nullptr;
2515 return that().childRangeImpl(index);
2516 }
2517
2518 template <typename, typename, typename> friend class QRangeModelAdapter;
2519
2521};
2522
2523// Implementations that depends on the model structure (flat vs tree) that will
2524// be specialized based on a protocol type. The main template implements tree
2525// support through a protocol type.
2526template <typename Range, typename Protocol>
2528 : public QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
2529{
2530 using Base = QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2531 friend class QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2532
2533 using range_type = typename Base::range_type;
2534 using range_features = typename Base::range_features;
2535 using row_type = typename Base::row_type;
2536 using row_ptr = typename Base::row_ptr;
2537 using const_row_ptr = typename Base::const_row_ptr;
2538
2539 using tree_traits = typename Base::protocol_traits;
2540 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
2541
2542 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
2543 QRangeModelDetails::is_smart_ptr<row_type>() ||
2544 QRangeModelDetails::is_any_of<row_type, std::reference_wrapper>();
2545 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
2546
2547public:
2548 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
2549 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
2550 {};
2551
2552 void setParentRow(range_type &children, row_ptr parent)
2553 {
2554 for (auto &&child : children)
2555 this->protocol().setParentRow(QRangeModelDetails::refTo(child), parent);
2556 resetParentInChildren(&children);
2557 }
2558
2559 void deleteRemovedRows(range_type &range)
2560 {
2561 deleteRemovedRows(QRangeModelDetails::adl_begin(range), QRangeModelDetails::adl_end(range));
2562 }
2563
2564 bool autoConnectProperties(const QModelIndex &parent) const
2565 {
2566 auto *children = this->childRange(parent);
2567 if (!children)
2568 return true;
2569 return autoConnectPropertiesRange(QRangeModelDetails::refTo(children), parent);
2570 }
2571
2572protected:
2573 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
2574 {
2575 if (!parent.isValid())
2576 return this->createIndex(row, column);
2577 // only items at column 0 can have children
2578 if (parent.column())
2579 return QModelIndex();
2580
2581 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
2582 const auto &parentSiblings = childrenOf(grandParent);
2583 const auto it = QRangeModelDetails::pos(parentSiblings, parent.row());
2584 return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
2585 }
2586
2587 QModelIndex parentImpl(const QModelIndex &child) const
2588 {
2589 if (!child.isValid())
2590 return {};
2591
2592 // no pointer to parent row - no parent
2593 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
2594 if (!parentRow)
2595 return {};
2596
2597 // get the siblings of the parent via the grand parent
2598 auto &&grandParent = this->protocol().parentRow(QRangeModelDetails::refTo(parentRow));
2599 const range_type &parentSiblings = childrenOf(QRangeModelDetails::pointerTo(grandParent));
2600 // find the index of parentRow
2601 const auto begin = QRangeModelDetails::adl_begin(parentSiblings);
2602 const auto end = QRangeModelDetails::adl_end(parentSiblings);
2603 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
2604 return QRangeModelDetails::pointerTo(std::forward<decltype(s)>(s)) == parentRow;
2605 });
2606 if (it != end)
2607 return this->createIndex(std::distance(begin, it), 0,
2608 QRangeModelDetails::pointerTo(grandParent));
2609 return {};
2610 }
2611
2612 int rowCountImpl(const QModelIndex &parent) const
2613 {
2614 return Base::size(this->childRange(parent));
2615 }
2616
2617 int columnCountImpl(const QModelIndex &) const
2618 {
2619 // All levels of a tree have to have the same, fixed, column count.
2620 // If static_column_count is -1 for a tree, static assert fires
2621 return Base::fixedColumnCount();
2622 }
2623
2624 static constexpr Qt::ItemFlags defaultFlags()
2625 {
2626 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2627 }
2628
2629 static constexpr bool canInsertRowsImpl()
2630 {
2631 // We must not insert rows if we cannot adjust the parents of the
2632 // children of the following rows. We don't have to do that if the
2633 // range operates on pointers.
2634 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2635 && Base::dynamicRows() && range_features::has_insert;
2636 }
2637
2638 static constexpr bool canRemoveRowsImpl()
2639 {
2640 // We must not remove rows if we cannot adjust the parents of the
2641 // children of the following rows. We don't have to do that if the
2642 // range operates on pointers.
2643 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2644 && Base::dynamicRows() && range_features::has_erase;
2645 }
2646
2647 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
2648 {
2649 return true;
2650 }
2651
2652 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
2653 {
2654 return true;
2655 }
2656
2657 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
2658 const QModelIndex &destParent, int destRow)
2659 {
2660 // If rows are pointers, then reference to the parent row don't
2661 // change, so we can move them around freely. Otherwise we need to
2662 // be able to explicitly update the parent pointer.
2663 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
2664 return false;
2665 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
2666 return false;
2667 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
2668 destParent, destRow)) {
2669 return false;
2670 }
2671
2672 range_type *source = this->childRange(sourceParent);
2673 range_type *destination = this->childRange(destParent);
2674
2675 // If we can insert data from another range into, then
2676 // use that to move the old data over.
2677 const auto destStart = QRangeModelDetails::pos(destination, destRow);
2678 if constexpr (range_features::has_insert_range) {
2679 const auto sourceStart = QRangeModelDetails::pos(*source, sourceRow);
2680 const auto sourceEnd = std::next(sourceStart, count);
2681
2682 destination->insert(destStart, std::move_iterator(sourceStart),
2683 std::move_iterator(sourceEnd));
2684 } else if constexpr (std::is_copy_constructible_v<row_type>) {
2685 // otherwise we have to make space first, and copy later.
2686 destination->insert(destStart, count, row_type{});
2687 }
2688
2689 row_ptr parentRow = destParent.isValid()
2690 ? QRangeModelDetails::pointerTo(this->rowData(destParent))
2691 : nullptr;
2692
2693 // if the source's parent was already inside the new parent row,
2694 // then the source row might have become invalid, so reset it.
2695 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
2696 if (sourceParent.row() < destRow) {
2697 source = this->childRange(sourceParent);
2698 } else {
2699 // the source parent moved down within destination
2700 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
2701 sourceParent.internalPointer()));
2702 }
2703 }
2704
2705 // move the data over and update the parent pointer
2706 {
2707 const auto writeStart = QRangeModelDetails::pos(destination, destRow);
2708 const auto writeEnd = std::next(writeStart, count);
2709 const auto sourceStart = QRangeModelDetails::pos(source, sourceRow);
2710 const auto sourceEnd = std::next(sourceStart, count);
2711
2712 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
2713 // move data over if not already done, otherwise
2714 // only fix the parent pointer
2715 if constexpr (!range_features::has_insert_range)
2716 *write = std::move(*read);
2717 this->protocol().setParentRow(QRangeModelDetails::refTo(*write), parentRow);
2718 }
2719 // remove the old rows from the source parent
2720 source->erase(sourceStart, sourceEnd);
2721 }
2722
2723 // Fix the parent pointers in children of both source and destination
2724 // ranges, as the references to the entries might have become invalid.
2725 // We don't have to do that if the rows are pointers, as in that case
2726 // the references to the entries are stable.
2727 resetParentInChildren(destination);
2729
2730 this->endMoveRows();
2731 return true;
2732 }
2733
2734 auto makeEmptyRow(row_ptr parentRow)
2735 {
2736 // tree traversal protocol: if we are here, then it must be possible
2737 // to change the parent of a row.
2738 static_assert(tree_traits::has_setParentRow);
2739 row_type empty_row = this->protocol().newRow();
2740 if (QRangeModelDetails::isValid(empty_row) && parentRow)
2741 this->protocol().setParentRow(QRangeModelDetails::refTo(empty_row), parentRow);
2742 return empty_row;
2743 }
2744
2745 template <typename It, typename Sentinel>
2746 void deleteRemovedRows(It &&begin, Sentinel &&end)
2747 {
2748 if constexpr (tree_traits::has_deleteRow) {
2749 for (auto it = begin; it != end; ++it) {
2750 if constexpr (Base::isMutable()) {
2751 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
2752 if (QRangeModelDetails::isValid(children)) {
2753 deleteRemovedRows(QRangeModelDetails::adl_begin(children),
2754 QRangeModelDetails::adl_end(children));
2755 QRangeModelDetails::refTo(children) = range_type{ };
2756 }
2757 }
2758
2759 this->protocol().deleteRow(std::move(*it));
2760 }
2761 }
2762 }
2763
2764 void resetParentInChildren(range_type *children)
2765 {
2766 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
2767 const auto begin = QRangeModelDetails::adl_begin(*children);
2768 const auto end = QRangeModelDetails::adl_end(*children);
2769 for (auto it = begin; it != end; ++it) {
2770 decltype(auto) maybeChildren = this->protocol().childRows(*it);
2771 if (QRangeModelDetails::isValid(maybeChildren)) {
2772 auto &childrenRef = QRangeModelDetails::refTo(maybeChildren);
2773 QModelIndexList fromIndexes;
2774 QModelIndexList toIndexes;
2775 fromIndexes.reserve(Base::size(childrenRef));
2776 toIndexes.reserve(Base::size(childrenRef));
2777 auto *parentRow = QRangeModelDetails::pointerTo(*it);
2778
2779 int row = 0;
2780 for (auto &child : childrenRef) {
2781 const_row_ptr oldParent = this->protocol().parentRow(child);
2782 if (oldParent != parentRow) {
2783 fromIndexes.append(this->createIndex(row, 0, oldParent));
2784 toIndexes.append(this->createIndex(row, 0, parentRow));
2785 this->protocol().setParentRow(child, parentRow);
2786 }
2787 ++row;
2788 }
2789 this->changePersistentIndexList(fromIndexes, toIndexes);
2790 resetParentInChildren(&childrenRef);
2791 }
2792 }
2793 }
2794 }
2795
2796 bool autoConnectPropertiesRange(const range_type &range, const QModelIndex &parent) const
2797 {
2798 int rowIndex = 0;
2799 for (const auto &row : range) {
2800 if (!this->autoConnectPropertiesInRow(row, rowIndex, parent))
2801 return false;
2802 Q_ASSERT(QRangeModelDetails::isValid(row));
2803 const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2804 if (QRangeModelDetails::isValid(children)) {
2805 if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
2806 this->itemModel().index(rowIndex, 0, parent))) {
2807 return false;
2808 }
2809 }
2810 ++rowIndex;
2811 }
2812 return true;
2813 }
2814
2816 {
2817 return autoConnectPropertiesRange(*this->m_data.model(), {});
2818 }
2819
2820 decltype(auto) rowDataImpl(const QModelIndex &index) const
2821 {
2822 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
2823 const range_type &siblings = childrenOf(parentRow);
2824 Q_ASSERT(index.row() < int(Base::size(siblings)));
2825 return *QRangeModelDetails::pos(siblings, index.row());
2826 }
2827
2828 decltype(auto) rowDataImpl(const QModelIndex &index)
2829 {
2830 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
2831 range_type &siblings = childrenOf(parentRow);
2832 Q_ASSERT(index.row() < int(Base::size(siblings)));
2833 return *QRangeModelDetails::pos(siblings, index.row());
2834 }
2835
2836 const range_type *childRangeImpl(const QModelIndex &index) const
2837 {
2838 const auto &row = this->rowData(index);
2839 if (!QRangeModelDetails::isValid(row))
2840 return static_cast<const range_type *>(nullptr);
2841
2842 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2843 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2844 }
2845
2846 range_type *childRangeImpl(const QModelIndex &index)
2847 {
2848 auto &row = this->rowData(index);
2849 if (!QRangeModelDetails::isValid(row))
2850 return static_cast<range_type *>(nullptr);
2851
2852 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2853 using Children = std::remove_reference_t<decltype(children)>;
2854
2855 if constexpr (QRangeModelDetails::is_any_of<Children, std::optional>())
2856 if constexpr (std::is_default_constructible<typename Children::value_type>()) {
2857 if (!children)
2858 children.emplace(range_type{});
2859 }
2860
2861 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2862 }
2863
2864 const range_type &childrenOf(const_row_ptr row) const
2865 {
2866 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2867 : *this->m_data.model();
2868 }
2869
2870private:
2871 range_type &childrenOf(row_ptr row)
2872 {
2873 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2874 : *this->m_data.model();
2875 }
2876};
2877
2878// specialization for flat models without protocol
2879template <typename Range>
2882{
2885
2886 static constexpr bool is_mutable_impl = true;
2887
2888public:
2889 using range_type = typename Base::range_type;
2891 using row_type = typename Base::row_type;
2893 using row_traits = typename Base::row_traits;
2894 using row_features = typename Base::row_features;
2895
2896 explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
2897 : Base(std::forward<Range>(model), {}, itemModel)
2898 {}
2899
2900protected:
2901 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
2902 {
2903 if constexpr (Base::dynamicColumns()) {
2904 if (column < int(Base::size(*QRangeModelDetails::pos(*this->m_data.model(), row))))
2905 return this->createIndex(row, column);
2906#ifndef QT_NO_DEBUG
2907 // if we got here, then column < columnCount(), but this row is too short
2908 qCritical("QRangeModel: Column-range at row %d is not large enough!", row);
2909#endif
2910 return {};
2911 } else {
2912 return this->createIndex(row, column);
2913 }
2914 }
2915
2916 QModelIndex parentImpl(const QModelIndex &) const
2917 {
2918 return {};
2919 }
2920
2921 int rowCountImpl(const QModelIndex &parent) const
2922 {
2923 if (parent.isValid())
2924 return 0;
2925 return int(Base::size(*this->m_data.model()));
2926 }
2927
2928 int columnCountImpl(const QModelIndex &parent) const
2929 {
2930 if (parent.isValid())
2931 return 0;
2932
2933 // in a table, all rows have the same number of columns (as the first row)
2934 if constexpr (Base::dynamicColumns()) {
2935 return int(Base::size(*this->m_data.model()) == 0
2936 ? 0
2937 : Base::size(*QRangeModelDetails::adl_begin(*this->m_data.model())));
2938 } else {
2939 return Base::fixedColumnCount();
2940 }
2941 }
2942
2943 static constexpr Qt::ItemFlags defaultFlags()
2944 {
2945 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
2946 }
2947
2948 static constexpr bool canInsertRowsImpl()
2949 {
2950 return Base::dynamicRows() && range_features::has_insert;
2951 }
2952
2953 static constexpr bool canRemoveRowsImpl()
2954 {
2955 return Base::dynamicRows() && range_features::has_erase;
2956 }
2957
2958 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
2959 {
2960 return !source.isValid() && !destination.isValid();
2961 }
2962
2963 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
2964 {
2965 return !source.isValid() && !destination.isValid();
2966 }
2967
2968 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
2969 const QModelIndex &, int) noexcept
2970 {
2971 // table/flat model: can't move rows between different parents
2972 return false;
2973 }
2974
2975 auto makeEmptyRow(typename Base::row_ptr)
2976 {
2977 row_type empty_row = this->protocol().newRow();
2978
2979 // dynamically sized rows all have to have the same column count
2980 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
2981 if (QRangeModelDetails::isValid(empty_row))
2982 QRangeModelDetails::refTo(empty_row).resize(this->columnCount({}));
2983 }
2984
2985 return empty_row;
2986 }
2987
2988 template <typename It, typename Sentinel>
2989 void deleteRemovedRows(It &&begin, Sentinel &&end)
2990 {
2991 if constexpr (Base::protocol_traits::has_deleteRow) {
2992 for (auto it = begin; it != end; ++it)
2993 this->protocol().deleteRow(std::move(*it));
2994 }
2995 }
2996
2997 decltype(auto) rowDataImpl(const QModelIndex &index) const
2998 {
2999 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
3000 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
3001 }
3002
3003 decltype(auto) rowDataImpl(const QModelIndex &index)
3004 {
3005 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
3006 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
3007 }
3008
3009 const range_type *childRangeImpl(const QModelIndex &) const
3010 {
3011 return nullptr;
3012 }
3013
3014 range_type *childRangeImpl(const QModelIndex &)
3015 {
3016 return nullptr;
3017 }
3018
3019 const range_type &childrenOf(const_row_ptr row) const
3020 {
3021 Q_ASSERT(!row);
3022 return *this->m_data.model();
3023 }
3024
3025 void resetParentInChildren(range_type *)
3026 {
3027 }
3028
3030 {
3031 bool result = true;
3032 int rowIndex = 0;
3033 for (const auto &row : *this->m_data.model()) {
3034 result &= this->autoConnectPropertiesInRow(row, rowIndex, {});
3035 ++rowIndex;
3036 }
3037 return result;
3038 }
3039};
3040
3041QT_END_NAMESPACE
3042
3043#endif // Q_QDOC
3044
3045#endif // QRANGEMODEL_IMPL_H
QModelIndex parentImpl(const QModelIndex &) const
static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
void deleteRemovedRows(It &&begin, Sentinel &&end)
const range_type & childrenOf(const_row_ptr row) const
static constexpr bool canRemoveRowsImpl()
QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
range_type * childRangeImpl(const QModelIndex &)
int columnCountImpl(const QModelIndex &parent) const
constexpr bool moveRowsAcross(const QModelIndex &, int, int, const QModelIndex &, int) noexcept
decltype(auto) rowDataImpl(const QModelIndex &index) const
static constexpr bool canInsertRowsImpl()
const range_type * childRangeImpl(const QModelIndex &) const
int rowCountImpl(const QModelIndex &parent) const
void resetParentInChildren(range_type *)
static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
decltype(auto) rowDataImpl(const QModelIndex &index)
QModelIndex indexImpl(int row, int column, const QModelIndex &) const
static constexpr Qt::ItemFlags defaultFlags()
auto makeEmptyRow(typename Base::row_ptr)
static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
auto makeEmptyRow(row_ptr parentRow)
void setParentRow(range_type &children, row_ptr parent)
range_type * childRangeImpl(const QModelIndex &index)
bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
void resetParentInChildren(range_type *children)
int rowCountImpl(const QModelIndex &parent) const
static constexpr bool canRemoveRowsImpl()
int columnCountImpl(const QModelIndex &) const
static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
const range_type & childrenOf(const_row_ptr row) const
decltype(auto) rowDataImpl(const QModelIndex &index)
QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
bool autoConnectPropertiesRange(const range_type &range, const QModelIndex &parent) const
QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
void deleteRemovedRows(It &&begin, Sentinel &&end)
bool autoConnectProperties(const QModelIndex &parent) const
const range_type * childRangeImpl(const QModelIndex &index) const
static constexpr bool canInsertRowsImpl()
decltype(auto) rowDataImpl(const QModelIndex &index) const
void deleteRemovedRows(range_type &range)
static constexpr Qt::ItemFlags defaultFlags()
QModelIndex parentImpl(const QModelIndex &child) const
const QAbstractItemModel & itemModel() const
void beginInsertRows(const QModelIndex &parent, int start, int count)
void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destParent, int destRow)
bool setItemData(const QModelIndex &index, const QMap< int, QVariant > &data)
QMap< int, QVariant > itemData(const QModelIndex &index) const
bool insertRows(int row, int count, const QModelIndex &parent)
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
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, typename C::SetAutoConnectPolicy > MethodTemplates
QRangeModelImplBase(QRangeModel *itemModel)
void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to)
int columnCount(const QModelIndex &parent) const
static Q_CORE_EXPORT bool connectProperties(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, const QHash< int, QMetaProperty > &properties)
QModelIndex sibling(int row, int column, const QModelIndex &index) const
void beginInsertColumns(const QModelIndex &parent, int start, int count)
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
static Q_CORE_EXPORT bool connectPropertiesConst(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, const QHash< int, QMetaProperty > &properties)
AutoConnectPolicy autoConnectPolicy() const
bool removeColumns(int column, int count, const QModelIndex &parent)
static Q_CORE_EXPORT bool connectPropertyConst(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, int role, const QMetaProperty &property)
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
static Q_CORE_EXPORT bool connectProperty(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, int role, const QMetaProperty &property)
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)
auto maybeBlockDataChangedDispatch()
static constexpr bool has_metaobject
QMetaProperty roleProperty(int role) const
int columnCount(const QModelIndex &parent) const
bool writeRole(int role, ItemType &&gadget, const QVariant &data)
static bool write(Target *target, const QVariant &value)
void connectPropertyOnRead(const QModelIndex &index, int role, const QObject *gadget, const QMetaProperty &prop) const
Qt::ItemFlags flags(const QModelIndex &index) const
void readAt(const QModelIndex &index, F &&reader) const
bool insertColumns(int column, int count, const QModelIndex &parent)
QVariant data(const QModelIndex &index, int role) const
QVariant headerData(int section, Qt::Orientation orientation, int role) const
static constexpr bool isRangeModelRole(int role)
bool clearItemData(const QModelIndex &index)
void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
static constexpr int fixedColumnCount()
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)
QVariant readProperty(const QModelIndex &index, ItemType *gadget) const
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)
bool writeRole(int role, ItemType *gadget, const QVariant &data)
QVariant readRole(const QModelIndex &index, int role, const ItemType &gadget) const
QRangeModelImpl< Structure, Range, Protocol > Self
static constexpr bool canRemoveRows()
static constexpr int static_column_count
QVariant readProperty(const QModelIndex &index, const ItemType &gadget) const
protocol_type & protocol()
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
static constexpr bool isPrimaryRole(int role)
void updateTarget(LHS *org, RHS &&copy) noexcept
static constexpr bool itemsAreQObjects
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(const QModelIndex &index, int role, ItemType *gadget) const
static bool resetProperty(int property, ItemType *object)
bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
static constexpr bool rowsAreQObjects
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
void clearConnectionInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
static auto adl_end(C &&c) -> decltype(end(QRangeModelDetails::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
typename QRangeModelDetails::wrapped_helper< T >::type wrapped_t
auto value(It &&it) -> decltype(it.value())
std::conjunction< std::is_swappable< decltype(*std::declval< It >())>, std::is_base_of< std::forward_iterator_tag, typename std::iterator_traits< It >::iterator_category > > test_rotate
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 auto adl_begin(C &&c) -> decltype(begin(QRangeModelDetails::refTo(std::forward< C >(c))))
static constexpr bool is_multi_role_v
friend bool operator==(const Connection &lhs, const Connection &rhs) noexcept
friend size_t qHash(const Connection &c, size_t seed) noexcept
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())
QHash< int, QMetaProperty > properties
static constexpr bool cachesProperties
static constexpr bool cachesProperties
std::remove_const_t< ModelStorage > m_model
static QHash< int, QByteArray > roleNames(That *)
static constexpr bool has_mutable_childRows
static constexpr bool has_insert_range
static constexpr int fixed_size()
static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
static void for_element_at(C &&container, std::size_t idx, Fn &&fn)
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