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 template <typename RowOptions>
440 template <typename row_type>
442
443 // Find out how many fixed elements can be retrieved from a row element.
444 // main template for simple values and ranges. Specializing for ranges
445 // is ambiguous with arrays, as they are also ranges
446 template <typename T, typename = void>
447 struct row_traits {
448 static constexpr bool is_range = is_range_v<q20::remove_cvref_t<T>>
450 // A static size of -1 indicates dynamically sized range
451 // A static size of 0 indicates that the specified type doesn't
452 // represent static or dynamic range.
453 static constexpr int static_size = is_range ? -1 : 0;
455 static constexpr int fixed_size() { return 1; }
456 static constexpr bool hasMetaObject = false;
457
459 {
460 return {};
461 }
462
463 template <typename C, typename Fn>
464 static void for_element_at(C &&container, std::size_t idx, Fn &&fn)
465 {
466 if constexpr (is_range)
467 std::forward<Fn>(fn)(*QRangeModelDetails::pos(std::forward<C>(container), idx));
468 else
469 std::forward<Fn>(fn)(std::forward<C>(container));
470 }
471
472 template <typename Fn>
473 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
474 {
475 if constexpr (static_size == 0) {
476 return std::forward<Fn>(fn)(firstIndex, QRangeModelDetails::pointerTo(row));
477 } else {
478 int columnIndex = -1;
479 return std::all_of(QRangeModelDetails::adl_begin(row),
480 QRangeModelDetails::adl_end(row), [&](const auto &item) {
481 return std::forward<Fn>(fn)(firstIndex.siblingAtColumn(++columnIndex),
482 QRangeModelDetails::pointerTo(item));
483 });
484 }
485 }
486 };
487
488 // Specialization for tuple-like semantics (prioritized over metaobject)
489 template <typename T>
491 {
492 static constexpr std::size_t size64 = std::tuple_size_v<T>;
493 static_assert(q20::in_range<int>(size64));
494 static constexpr int static_size = int(size64);
495
496 // are the types in a tuple all the same
497 template <std::size_t ...I>
498 static constexpr bool allSameTypes(std::index_sequence<I...>)
499 {
500 return (std::is_same_v<std::tuple_element_t<0, T>,
501 std::tuple_element_t<I, T>> && ...);
502 }
503
505 std::tuple_element_t<0, T>, void>;
506 static constexpr int fixed_size() { return 0; }
507 static constexpr bool hasMetaObject = false;
508
509 template <typename C, typename F>
519
521 {
522 constexpr auto size = std::tuple_size_v<T>;
524
530 >();
531 if (metaType.isValid())
533 });
534 return result;
535 }
536
537 template <typename Fn, std::size_t ...Is>
538 static bool forEachTupleElement(const T &row, Fn &&fn, std::index_sequence<Is...>)
539 {
540 using std::get;
541 return (std::forward<Fn>(fn)(QRangeModelDetails::pointerTo(get<Is>(row))) && ...);
542 }
543
544 template <typename Fn>
545 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
546 {
547 int column = -1;
548 return forEachTupleElement(row, [&column, &fn, &firstIndex](const QObject *item){
551 }
552 };
553
554 // Specialization for C arrays and std::array
555 template <typename T, std::size_t N>
556 struct row_traits<std::array<T, N>>
557 {
558 static_assert(q20::in_range<int>(N));
559 static constexpr int static_size = int(N);
560 using item_type = T;
561 static constexpr int fixed_size() { return 0; }
562 static constexpr bool hasMetaObject = false;
563
564 template <typename C, typename F>
570
572 {
573 return section;
574 }
575
576 template <typename Fn>
577 static bool for_each_element(const std::array<T, N> &row, const QModelIndex &firstIndex, Fn &&fn)
578 {
579 int columnIndex = -1;
581 QRangeModelDetails::adl_end(row), [&](const auto &item) {
584 });
585 }
586 };
587
588 template <typename T, std::size_t N>
589 struct row_traits<T[N]> : row_traits<std::array<T, N>> {};
590
591 // prioritize tuple-like over metaobject
592 template <typename T>
594 {
595 static constexpr int static_size = 0;
597 static int fixed_size() {
598 if constexpr (row_category<T>::isMultiRole) {
599 return 1;
600 } else {
601 // Interpret a gadget in a list as a multi-column row item. To make
602 // a list of multi-role items, wrap it into SingleColumn.
603 static const int columnCount = []{
605 return mo.propertyCount() - mo.propertyOffset();
606 }();
607 return columnCount;
608 }
609 }
610
611 static constexpr bool hasMetaObject = true;
612
613 template <typename C, typename F>
615 {
617 }
618
620 {
622 if (fixed_size() == 1) {
625 } else if (section <= fixed_size()) {
629 }
630 return result;
631 }
632
633 template <typename Fn>
634 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
635 {
637 }
638 };
639
640 template <typename T, typename = void>
642 {
643 template <typename That>
644 static QHash<int, QByteArray> roleNames(That *)
645 {
646 return That::roleNamesForSimpleType();
647 }
648 };
649
650 template <>
651 struct item_traits<void>
652 {
653 template <typename That>
655 {
657 }
658 };
659
660 template <typename T>
665
666 template <typename T>
668 {
669 template <typename That>
674 };
675
676 template <typename T>
677 [[maybe_unused]] static constexpr int static_size_v =
679
680 template <typename Range>
682 {
684
685 template <typename R = row_type>
686 auto newRow() -> decltype(R{}) { return R{}; }
687 };
688
689 template <typename Range>
691 {
693
694 template <typename R = row_type,
699 >,
700 bool> = true>
701 auto newRow() -> decltype(R(new QRangeModelDetails::wrapped_t<R>)) {
702 if constexpr (is_any_of<R, std::shared_ptr>())
704 else
705 return R(new QRangeModelDetails::wrapped_t<R>);
706 }
707
708 template <typename R = row_type,
710 auto newRow() -> decltype(R{}) { return R{}; }
711
712 template <typename R = row_type,
714 auto deleteRow(R&& row) -> decltype(delete row) { delete row; }
715 };
716
717 template <typename Range,
721
722 // Default tree traversal protocol implementation for row types that have
723 // the respective member functions. The trailing return type implicitly
724 // removes those functions that are not available.
725 template <typename Range>
727 {
728 template <typename R /*wrapped_row_type*/>
729 auto parentRow(const R& row) const -> decltype(row.parentRow())
730 {
731 return row.parentRow();
732 }
733
734 template <typename R /* = wrapped_row_type*/>
735 auto setParentRow(R &row, R* parent) -> decltype(row.setParentRow(parent))
736 {
737 row.setParentRow(parent);
738 }
739
740 template <typename R /* = wrapped_row_type*/>
741 auto childRows(const R &row) const -> decltype(row.childRows())
742 {
743 return row.childRows();
744 }
745
746 template <typename R /* = wrapped_row_type*/>
747 auto childRows(R &row) -> decltype(row.childRows())
748 {
749 return row.childRows();
750 }
751 };
752
753 template <typename P, typename R>
754 using protocol_parentRow_test = decltype(std::declval<P&>()
756 template <typename P, typename R>
758
759 template <typename P, typename R>
760 using protocol_childRows_test = decltype(std::declval<P&>()
762 template <typename P, typename R>
764
765 template <typename P, typename R>
769 template <typename P, typename R>
771
772 template <typename P, typename R>
775 template <typename P, typename R>
777
778 template <typename P, typename = void>
780 template <typename P>
781 struct protocol_newRow<P, std::void_t<decltype(std::declval<P&>().newRow())>>
782 : std::true_type {};
783
784 template <typename P, typename R, typename = void>
786 template <typename P, typename R>
788 std::void_t<decltype(std::declval<P&>().deleteRow(std::declval<R&&>()))>>
789 : std::true_type {};
790
791 template <typename Range,
792 typename Protocol = DefaultTreeProtocol<Range>,
793 typename R = typename range_traits<Range>::value_type,
794 typename = void>
796
797 template <typename Range, typename Protocol, typename R>
802
803 template <typename Range>
807 >, bool>;
808
809 template <typename Range, typename Protocol = DefaultTreeProtocol<Range>>
814 >, bool>;
815
816 template <typename Range, typename Protocol>
834
851
852 template <bool cacheProperties, bool itemsAreQObjects>
854 static constexpr bool cachesProperties = false;
855
857 };
858
860 {
861 static constexpr bool cachesProperties = true;
863
865 {
866 properties.clear();
867 }
868 protected:
869 ~PropertyCache() = default;
870 };
871
872 template <>
873 struct PropertyData<true, false> : PropertyCache
874 {};
875
877 {
878 struct Connection {
880 int role;
881
882 friend bool operator==(const Connection &lhs, const Connection &rhs) noexcept
883 {
884 return lhs.sender == rhs.sender && lhs.role == rhs.role;
885 }
886 friend size_t qHash(const Connection &c, size_t seed) noexcept
887 {
888 return qHashMulti(seed, c.sender, c.role);
889 }
890 };
891
894
895 protected:
896 ~ConnectionStorage() = default;
897 };
898
899 template <>
901 {};
902
903 template <>
904 struct PropertyData<false, true> : PropertyData<false, false>, ConnectionStorage
905 {
907 };
908
909 // The storage of the model data. We might store it as a pointer, or as a
910 // (copied- or moved-into) value (or smart pointer). But we always return a
911 // raw pointer.
912 template <typename ModelStorage, typename = void>
920
921 template <typename ModelStorage>
930
931 template <typename ModelStorage, typename PropertyStorage>
933 PropertyStorage
934 {
938
939 auto model() { return QRangeModelDetails::pointerTo(this->m_model); }
940 auto model() const { return QRangeModelDetails::pointerTo(this->m_model); }
941
942 template <typename Model = ModelStorage>
943 ModelData(Model &&model)
945 {}
946 };
947} // namespace QRangeModelDetails
948
949class QRangeModel;
950// forward declare so that we can declare friends
951template <typename, typename, typename> class QRangeModelAdapter;
952
954{
955 using Self = QRangeModelImplBase;
957
958public:
959 // keep in sync with QRangeModel::AutoConnectPolicy
965
966 // overridable prototypes (quasi-pure-virtual methods)
968 bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
969 bool setData(const QModelIndex &index, const QVariant &data, int role);
970 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data);
971 bool clearItemData(const QModelIndex &index);
972 bool insertColumns(int column, int count, const QModelIndex &parent);
973 bool removeColumns(int column, int count, const QModelIndex &parent);
974 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn);
975 bool insertRows(int row, int count, const QModelIndex &parent);
976 bool removeRows(int row, int count, const QModelIndex &parent);
977 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow);
978
979 QModelIndex index(int row, int column, const QModelIndex &parent) const;
980 QModelIndex sibling(int row, int column, const QModelIndex &index) const;
981 int rowCount(const QModelIndex &parent) const;
982 int columnCount(const QModelIndex &parent) const;
983 Qt::ItemFlags flags(const QModelIndex &index) const;
984 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
985 QVariant data(const QModelIndex &index, int role) const;
986 QMap<int, QVariant> itemData(const QModelIndex &index) const;
987 inline QHash<int, QByteArray> roleNames() const;
988 QModelIndex parent(const QModelIndex &child) const;
989
990 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
992
993 // bindings for overriding
994
1006
1013 using Data = Method<&Self::data>;
1017
1018 // 6.11
1021
1022 template <typename C>
1023 using MethodTemplates = std::tuple<
1024 typename C::Destroy,
1025 typename C::InvalidateCaches,
1026 typename C::SetHeaderData,
1027 typename C::SetData,
1028 typename C::SetItemData,
1029 typename C::ClearItemData,
1030 typename C::InsertColumns,
1031 typename C::RemoveColumns,
1032 typename C::MoveColumns,
1033 typename C::InsertRows,
1034 typename C::RemoveRows,
1035 typename C::MoveRows,
1036 typename C::Index,
1037 typename C::Parent,
1038 typename C::Sibling,
1039 typename C::RowCount,
1040 typename C::ColumnCount,
1041 typename C::Flags,
1042 typename C::HeaderData,
1043 typename C::Data,
1044 typename C::ItemData,
1045 typename C::RoleNames,
1046 typename C::MultiData,
1047 typename C::SetAutoConnectPolicy
1048 >;
1049
1050 static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
1051 static Q_CORE_EXPORT const QRangeModelImplBase *getImplementation(const QRangeModel *model);
1052
1053private:
1054 friend class QRangeModelPrivate;
1056
1057 QRangeModel *m_rangeModel;
1058
1059protected:
1060 explicit QRangeModelImplBase(QRangeModel *itemModel)
1061 : m_rangeModel(itemModel)
1062 {}
1063
1064 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
1065 inline void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to);
1066 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
1067 const QList<int> &roles);
1068 inline void beginResetModel();
1069 inline void endResetModel();
1070 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
1071 inline void endInsertColumns();
1072 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
1073 inline void endRemoveColumns();
1074 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1075 const QModelIndex &destParent, int destRow);
1076 inline void endMoveColumns();
1077 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
1078 inline void endInsertRows();
1079 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
1080 inline void endRemoveRows();
1081 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1082 const QModelIndex &destParent, int destRow);
1083 inline void endMoveRows();
1084 inline AutoConnectPolicy autoConnectPolicy() const;
1085
1086public:
1087 inline QAbstractItemModel &itemModel();
1088 inline const QAbstractItemModel &itemModel() const;
1089
1090 // implemented in qrangemodel.cpp
1092 const QMetaObject &metaObject);
1094
1095protected:
1096 Q_CORE_EXPORT QScopedValueRollback<bool> blockDataChangedDispatch();
1097
1099 const QMetaObject &metaObject);
1101 Q_CORE_EXPORT static bool connectProperty(const QModelIndex &index, const QObject *item,
1102 QRangeModelDetails::AutoConnectContext *context,
1103 int role, const QMetaProperty &property);
1104 Q_CORE_EXPORT static bool connectPropertyConst(const QModelIndex &index, const QObject *item,
1105 QRangeModelDetails::AutoConnectContext *context,
1106 int role, const QMetaProperty &property);
1107 Q_CORE_EXPORT static bool connectProperties(const QModelIndex &index, const QObject *item,
1108 QRangeModelDetails::AutoConnectContext *context,
1109 const QHash<int, QMetaProperty> &properties);
1110 Q_CORE_EXPORT static bool connectPropertiesConst(const QModelIndex &index, const QObject *item,
1111 QRangeModelDetails::AutoConnectContext *context,
1112 const QHash<int, QMetaProperty> &properties);
1113};
1114
1115template <typename Structure, typename Range,
1116 typename Protocol = QRangeModelDetails::table_protocol_t<Range>>
1121{
1122public:
1132
1134 typename row_traits::item_type>>;
1136 && row_traits::hasMetaObject; // not treated as tuple
1137
1141 >,
1144 >
1145 >;
1147
1148 using const_row_reference = decltype(*std::declval<typename ModelData::const_iterator&>());
1149
1150 static_assert(!QRangeModelDetails::is_any_of<range_type, std::optional>() &&
1152 "Currently, std::optional is not supported for ranges and rows, as "
1153 "it has range semantics in c++26. Once the required behavior is clarified, "
1154 "std::optional for ranges and rows will be supported.");
1155
1156protected:
1157
1158 using Self = QRangeModelImpl<Structure, Range, Protocol>;
1160
1161 Structure& that() { return static_cast<Structure &>(*this); }
1162 const Structure& that() const { return static_cast<const Structure &>(*this); }
1163
1164 template <typename C>
1165 static constexpr int size(const C &c)
1166 {
1167 if (!QRangeModelDetails::isValid(c))
1168 return 0;
1169
1170 if constexpr (QRangeModelDetails::test_size<C>()) {
1171 using std::size;
1172 return int(size(c));
1173 } else {
1174#if defined(__cpp_lib_ranges)
1175 using std::ranges::distance;
1176#else
1177 using std::distance;
1178#endif
1179 using container_type = std::conditional_t<QRangeModelDetails::range_traits<C>::has_cbegin,
1180 const QRangeModelDetails::wrapped_t<C>,
1181 QRangeModelDetails::wrapped_t<C>>;
1182 container_type& container = const_cast<container_type &>(QRangeModelDetails::refTo(c));
1183 return int(distance(QRangeModelDetails::adl_begin(container),
1184 QRangeModelDetails::adl_end(container)));
1185 }
1186 }
1187
1190 static constexpr bool rows_are_owning_or_raw_pointers =
1193 static constexpr bool one_dimensional_range = static_column_count == 0;
1194
1196 {
1197 if constexpr (itemsAreQObjects || rowsAreQObjects)
1198 return this->blockDataChangedDispatch();
1199 else
1200 return false;
1201 }
1202
1203 // A row might be a value (or range of values), or a pointer.
1204 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
1207
1208 template <typename T>
1211
1212 // A iterator type to use as the input iterator with the
1213 // range_type::insert(pos, start, end) overload if available (it is in
1214 // std::vector, but not in QList). Generates a prvalue when dereferenced,
1215 // which then gets moved into the newly constructed row, which allows us to
1216 // implement insertRows() for move-only row types.
1218 {
1222 using iterator_category = std::input_iterator_tag;
1223 using difference_type = int;
1224
1225 value_type operator*() { return impl->makeEmptyRow(parentRow); }
1226 EmptyRowGenerator &operator++() { ++n; return *this; }
1227 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1228 { return lhs.n == rhs.n; }
1229 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1230 { return !(lhs == rhs); }
1231
1233 Structure *impl = nullptr;
1234 const row_ptr parentRow = nullptr;
1235 };
1236
1237 // If we have a move-only row_type and can add/remove rows, then the range
1238 // must have an insert-from-range overload.
1241 "The range holding a move-only row-type must support insert(pos, start, end)");
1242
1244
1245public:
1246 static constexpr bool isMutable()
1247 {
1248 return range_features::is_mutable && row_features::is_mutable
1249 && std::is_reference_v<row_reference>
1250 && Structure::is_mutable_impl;
1251 }
1252 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
1253 static constexpr bool dynamicColumns() { return static_column_count < 0; }
1254
1255 explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
1259 {
1260 }
1261
1262
1263 // static interface, called by QRangeModelImplBase
1264
1265 void invalidateCaches() { m_data.invalidateCaches(); }
1266
1267 // Not implemented
1268 bool setHeaderData(int , Qt::Orientation , const QVariant &, int ) { return false; }
1269
1270 // actual implementations
1271 QModelIndex index(int row, int column, const QModelIndex &parent) const
1272 {
1273 if (row < 0 || column < 0 || column >= columnCount(parent)
1274 || row >= rowCount(parent)) {
1275 return {};
1276 }
1277
1278 return that().indexImpl(row, column, parent);
1279 }
1280
1281 QModelIndex sibling(int row, int column, const QModelIndex &index) const
1282 {
1283 if (row == index.row() && column == index.column())
1284 return index;
1285
1286 if (column < 0 || column >= this->columnCount({}))
1287 return {};
1288
1289 if (row == index.row())
1290 return this->createIndex(row, column, index.constInternalPointer());
1291
1292 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1293 const auto siblingCount = size(that().childrenOf(parentRow));
1294 if (row < 0 || row >= int(siblingCount))
1295 return {};
1296 return this->createIndex(row, column, parentRow);
1297 }
1298
1299 Qt::ItemFlags flags(const QModelIndex &index) const
1300 {
1301 if (!index.isValid())
1302 return Qt::NoItemFlags;
1303
1304 Qt::ItemFlags f = Structure::defaultFlags();
1305
1306 if constexpr (isMutable()) {
1307 if constexpr (row_traits::hasMetaObject) {
1308 if (index.column() < row_traits::fixed_size()) {
1309 const QMetaObject mo = wrapped_row_type::staticMetaObject;
1310 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
1311 if (prop.isWritable())
1312 f |= Qt::ItemIsEditable;
1313 }
1314 } else if constexpr (static_column_count <= 0) {
1315 f |= Qt::ItemIsEditable;
1316 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
1317 // we want to know if the elements in the tuple are const; they'd always be, if
1318 // we didn't remove the const of the range first.
1319 const_row_reference row = rowData(index);
1320 row_reference mutableRow = const_cast<row_reference>(row);
1321 if (QRangeModelDetails::isValid(mutableRow)) {
1322 row_traits::for_element_at(mutableRow, index.column(), [&f](auto &&ref){
1323 using target_type = decltype(ref);
1324 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
1325 f &= ~Qt::ItemIsEditable;
1326 else if constexpr (std::is_lvalue_reference_v<target_type>)
1327 f |= Qt::ItemIsEditable;
1328 });
1329 } else {
1330 // If there's no usable value stored in the row, then we can't
1331 // do anything with this item.
1332 f &= ~Qt::ItemIsEditable;
1333 }
1334 }
1335 }
1336 return f;
1337 }
1338
1339 QVariant headerData(int section, Qt::Orientation orientation, int role) const
1340 {
1341 QVariant result;
1342 if constexpr (QRangeModelDetails::hasHeaderData<wrapped_row_type>) {
1343 if (orientation == Qt::Horizontal) {
1344 result = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::headerData(
1345 section, role
1346 );
1347 if (result.isValid())
1348 return result;
1349 }
1350 }
1351
1352 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
1353 || section < 0 || section >= columnCount({})) {
1354 return this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1355 }
1356
1357 result = row_traits::column_name(section);
1358 if (!result.isValid())
1359 result = this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1360 return result;
1361 }
1362
1363 QVariant data(const QModelIndex &index, int role) const
1364 {
1365 if (!index.isValid())
1366 return {};
1367
1368 QModelRoleData result(role);
1369 multiData(index, result);
1370 return std::move(result.data());
1371 }
1372
1373 static constexpr bool isRangeModelRole(int role)
1374 {
1375 return role == Qt::RangeModelDataRole
1376 || role == Qt::RangeModelAdapterRole;
1377 }
1378
1379 static constexpr bool isPrimaryRole(int role)
1380 {
1381 return role == Qt::DisplayRole || role == Qt::EditRole;
1382 }
1383
1384 QMap<int, QVariant> itemData(const QModelIndex &index) const
1385 {
1386 QMap<int, QVariant> result;
1387
1388 if (index.isValid()) {
1389 bool tried = false;
1390
1391 // optimisation for items backed by a QMap<int, QVariant> or equivalent
1392 readAt(index, [&result, &tried](const auto &value) {
1393 if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) {
1394 tried = true;
1395 result = value;
1396 }
1397 });
1398 if (!tried) {
1399 const auto roles = this->itemModel().roleNames().keys();
1400 QVarLengthArray<QModelRoleData, 16> roleDataArray;
1401 roleDataArray.reserve(roles.size());
1402 for (auto role : roles) {
1403 if (isRangeModelRole(role))
1404 continue;
1405 roleDataArray.emplace_back(role);
1406 }
1407 QModelRoleDataSpan roleDataSpan(roleDataArray);
1408 multiData(index, roleDataSpan);
1409
1410 for (QModelRoleData &roleData : roleDataSpan) {
1411 if (roleData.data().isValid())
1412 result[roleData.role()] = std::move(roleData.data());
1413 }
1414 }
1415 }
1416 return result;
1417 }
1418
1419 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1420 {
1421 bool tried = false;
1422 readAt(index, [this, &index, roleDataSpan, &tried](const auto &value) {
1423 Q_UNUSED(this);
1424 Q_UNUSED(index);
1425 using value_type = q20::remove_cvref_t<decltype(value)>;
1426 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1427 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1428
1429 const auto readModelData = [&value](QModelRoleData &roleData){
1430 const int role = roleData.role();
1431 if (role == Qt::RangeModelDataRole) {
1432 // Qt QML support: "modelData" role returns the entire multi-role item.
1433 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1434 // only by value (so we take the reference).
1435 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1436 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1437 else
1438 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1439 } else if (role == Qt::RangeModelAdapterRole) {
1440 // for QRangeModelAdapter however, we want to respect smart pointer wrappers
1441 if constexpr (std::is_copy_assignable_v<value_type>)
1442 roleData.setData(QVariant::fromValue(value));
1443 else
1444 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1445 } else {
1446 return false;
1447 }
1448 return true;
1449 };
1450
1451 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1452 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1453 tried = true;
1454 for (auto &roleData : roleDataSpan) {
1455 if (!readModelData(roleData)) {
1456 roleData.setData(ItemAccess::readRole(QRangeModelDetails::refTo(value),
1457 roleData.role()));
1458 }
1459 }
1460 } else if constexpr (multi_role()) {
1461 tried = true;
1462 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1463 Q_UNUSED(this);
1464 if constexpr (!multi_role::int_key)
1465 return this->itemModel().roleNames();
1466 else
1467 return {};
1468 }();
1469 using key_type = typename value_type::key_type;
1470 for (auto &roleData : roleDataSpan) {
1471 const auto &it = [&roleNames, &value, role = roleData.role()]{
1472 Q_UNUSED(roleNames);
1473 if constexpr (multi_role::int_key)
1474 return value.find(key_type(role));
1475 else
1476 return value.find(roleNames.value(role));
1477 }();
1478 if (it != QRangeModelDetails::adl_end(value))
1479 roleData.setData(QRangeModelDetails::value(it));
1480 else
1481 roleData.clearData();
1482 }
1483 } else if constexpr (has_metaobject<value_type>) {
1484 if (row_traits::fixed_size() <= 1) {
1485 tried = true;
1486 for (auto &roleData : roleDataSpan) {
1487 if (!readModelData(roleData)) {
1488 roleData.setData(readRole(index, roleData.role(),
1489 QRangeModelDetails::pointerTo(value)));
1490 }
1491 }
1492 } else if (index.column() <= row_traits::fixed_size()) {
1493 tried = true;
1494 for (auto &roleData : roleDataSpan) {
1495 const int role = roleData.role();
1496 if (isPrimaryRole(role)) {
1497 roleData.setData(readProperty(index,
1498 QRangeModelDetails::pointerTo(value)));
1499 } else {
1500 roleData.clearData();
1501 }
1502 }
1503 }
1504 } else {
1505 tried = true;
1506 for (auto &roleData : roleDataSpan) {
1507 const int role = roleData.role();
1508 if (isPrimaryRole(role) || isRangeModelRole(role))
1509 roleData.setData(read(value));
1510 else
1511 roleData.clearData();
1512 }
1513 }
1514 });
1515
1516 Q_ASSERT(tried);
1517 }
1518
1519 bool setData(const QModelIndex &index, const QVariant &data, int role)
1520 {
1521 if (!index.isValid())
1522 return false;
1523
1524 bool success = false;
1525 if constexpr (isMutable()) {
1526 auto emitDataChanged = qScopeGuard([&success, this, &index, role]{
1527 if (success) {
1528 Q_EMIT this->dataChanged(index, index,
1529 role == Qt::EditRole || role == Qt::RangeModelDataRole
1530 || role == Qt::RangeModelAdapterRole
1531 ? QList<int>{} : QList<int>{role});
1532 }
1533 });
1534 // we emit dataChanged at the end, block dispatches from auto-connected properties
1535 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1536
1537 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1538 using value_type = q20::remove_cvref_t<decltype(target)>;
1539 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1540 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1541
1542 auto setRangeModelDataRole = [&target, &data]{
1543 constexpr auto targetMetaType = QMetaType::fromType<value_type>();
1544 const auto dataMetaType = data.metaType();
1545 constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
1546 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
1547 // we don't support replacing objects that are stored as raw pointers,
1548 // as this makes object ownership very messy. But we can replace objects
1549 // stored in smart pointers, and we can initialize raw nullptr objects.
1550 if constexpr (isWrapped) {
1551 constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
1552 if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
1553 if (data.canConvert(targetMetaType)) {
1554 target = data.value<value_type>();
1555 return true;
1556 }
1557 } else if constexpr (is_raw_pointer) {
1558 if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
1559 target = data.value<value_type>();
1560 return true;
1561 }
1562 } else {
1563 Q_UNUSED(target);
1564 }
1565 }
1566 // Otherwise we have a move-only or polymorph type. fall through to
1567 // error handling.
1568 } else if constexpr (isWrapped) {
1569 if (QRangeModelDetails::isValid(target)) {
1570 auto &targetRef = QRangeModelDetails::refTo(target);
1571 // we need to get a wrapped value type out of the QVariant, which
1572 // might carry a pointer. We have to try all alternatives.
1573 if (const auto mt = QMetaType::fromType<wrapped_value_type>();
1574 data.canConvert(mt)) {
1575 targetRef = data.value<wrapped_value_type>();
1576 return true;
1577 } else if (const auto mtp = QMetaType::fromType<wrapped_value_type *>();
1578 data.canConvert(mtp)) {
1579 targetRef = *data.value<wrapped_value_type *>();
1580 return true;
1581 }
1582 }
1583 } else if (targetMetaType == dataMetaType) {
1584 QRangeModelDetails::refTo(target) = data.value<value_type>();
1585 return true;
1586 } else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1587 QRangeModelDetails::refTo(target) = *data.value<value_type *>();
1588 return true;
1589 }
1590#ifndef QT_NO_DEBUG
1591 qCritical("Not able to assign %s to %s",
1592 qPrintable(QDebug::toString(data)), targetMetaType.name());
1593#endif
1594 return false;
1595 };
1596
1597 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1598 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1599 if (isRangeModelRole(role))
1600 return setRangeModelDataRole();
1601 return ItemAccess::writeRole(QRangeModelDetails::refTo(target), data, role);
1602 } else if constexpr (has_metaobject<value_type>) {
1603 if (row_traits::fixed_size() <= 1) { // multi-role value
1604 if (isRangeModelRole(role))
1605 return setRangeModelDataRole();
1606 return writeRole(role, QRangeModelDetails::pointerTo(target), data);
1607 } else if (column <= row_traits::fixed_size() // multi-column
1608 && (isPrimaryRole(role) || isRangeModelRole(role))) {
1609 return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
1610 }
1611 } else if constexpr (multi_role::value) {
1612 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1613 // If there is an entry for EditRole, overwrite that; otherwise,
1614 // set the entry for DisplayRole.
1615 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1616 Q_UNUSED(this);
1617 if constexpr (!multi_role::int_key)
1618 return this->itemModel().roleNames();
1619 else
1620 return {};
1621 }();
1622 if (role == Qt::EditRole) {
1623 if constexpr (multi_role::int_key) {
1624 if (target.find(roleToSet) == target.end())
1625 roleToSet = Qt::DisplayRole;
1626 } else {
1627 if (target.find(roleNames.value(roleToSet)) == target.end())
1628 roleToSet = Qt::DisplayRole;
1629 }
1630 }
1631 if constexpr (multi_role::int_key)
1632 return write(target[roleToSet], data);
1633 else
1634 return write(target[roleNames.value(roleToSet)], data);
1635 } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
1636 return write(target, data);
1637 }
1638 return false;
1639 };
1640
1641 success = writeAt(index, writeData);
1642
1643 if constexpr (itemsAreQObjects || rowsAreQObjects) {
1644 if (success && isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
1645 if (QObject *item = data.value<QObject *>())
1646 Self::connectProperties(index, item, m_data.context, m_data.properties);
1647 }
1648 }
1649 }
1650 return success;
1651 }
1652
1653 template <typename LHS, typename RHS>
1654 void updateTarget(LHS &org, RHS &&copy) noexcept
1655 {
1656 if constexpr (std::is_pointer_v<RHS>)
1657 return;
1658 else if constexpr (std::is_assignable_v<LHS, RHS>)
1659 org = std::forward<RHS>(copy);
1660 else
1661 qSwap(org, copy);
1662 }
1663 template <typename LHS, typename RHS>
1664 void updateTarget(LHS *org, RHS &&copy) noexcept
1665 {
1666 updateTarget(*org, std::forward<RHS>(copy));
1667 }
1668
1669 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1670 {
1671 if (!index.isValid() || data.isEmpty())
1672 return false;
1673
1674 bool success = false;
1675 if constexpr (isMutable()) {
1676 auto emitDataChanged = qScopeGuard([&success, this, &index, &data]{
1677 if (success)
1678 Q_EMIT this->dataChanged(index, index, data.keys());
1679 });
1680 // we emit dataChanged at the end, block dispatches from auto-connected properties
1681 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1682
1683 bool tried = false;
1684 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
1685 Q_UNUSED(this);
1686 using value_type = q20::remove_cvref_t<decltype(target)>;
1687 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1688 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1689
1690 // transactional: if possible, modify a copy and only
1691 // update target if all values from data could be stored.
1692 auto makeCopy = [](const value_type &original){
1693 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>)
1694 return QRangeModelDetails::pointerTo(original); // no transaction support
1695 else if constexpr (std::is_pointer_v<decltype(original)>)
1696 return *original;
1697 else if constexpr (std::is_copy_assignable_v<value_type>)
1698 return original;
1699 else
1700 return QRangeModelDetails::pointerTo(original);
1701 };
1702
1703 const auto roleNames = this->itemModel().roleNames();
1704
1705 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
1706 tried = true;
1707 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1708 const auto roles = roleNames.keys();
1709 auto targetCopy = makeCopy(target);
1710 for (int role : roles) {
1711 if (!ItemAccess::writeRole(QRangeModelDetails::refTo(targetCopy),
1712 data.value(role), role)) {
1713 return false;
1714 }
1715 }
1716 updateTarget(target, std::move(targetCopy));
1717 return true;
1718 } else if constexpr (multi_role()) {
1719 using key_type = typename value_type::key_type;
1720 tried = true;
1721 const auto roleName = [&roleNames](int role) {
1722 return roleNames.value(role);
1723 };
1724
1725 // transactional: only update target if all values from data
1726 // can be stored. Storing never fails with int-keys.
1727 if constexpr (!multi_role::int_key)
1728 {
1729 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
1730 [&roleName](int role) { return roleName(role).isEmpty(); }
1731 );
1732
1733 if (invalid != data.keyEnd()) {
1734#ifndef QT_NO_DEBUG
1735 qWarning("No role name set for %d", *invalid);
1736#endif
1737 return false;
1738 }
1739 }
1740
1741 for (auto &&[role, value] : data.asKeyValueRange()) {
1742 if constexpr (multi_role::int_key)
1743 target[static_cast<key_type>(role)] = value;
1744 else
1745 target[QString::fromUtf8(roleName(role))] = value;
1746 }
1747 return true;
1748 } else if constexpr (has_metaobject<value_type>) {
1749 if (row_traits::fixed_size() <= 1) {
1750 tried = true;
1751 auto targetCopy = makeCopy(target);
1752 for (auto &&[role, value] : data.asKeyValueRange()) {
1753 if (isRangeModelRole(role))
1754 continue;
1755 if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
1756 const QByteArray roleName = roleNames.value(role);
1757#ifndef QT_NO_DEBUG
1758 qWarning("Failed to write value '%s' to role '%s'",
1759 qPrintable(QDebug::toString(value)), roleName.data());
1760#endif
1761 return false;
1762 }
1763 }
1764 updateTarget(target, std::move(targetCopy));
1765 return true;
1766 }
1767 }
1768 return false;
1769 };
1770
1771 success = writeAt(index, writeItemData);
1772
1773 if (!tried) {
1774 // setItemData will emit the dataChanged signal
1775 Q_ASSERT(!success);
1776 emitDataChanged.dismiss();
1777 success = this->itemModel().QAbstractItemModel::setItemData(index, data);
1778 }
1779 }
1780 return success;
1781 }
1782
1783 bool clearItemData(const QModelIndex &index)
1784 {
1785 if (!index.isValid())
1786 return false;
1787
1788 bool success = false;
1789 if constexpr (isMutable()) {
1790 auto emitDataChanged = qScopeGuard([&success, this, &index]{
1791 if (success)
1792 Q_EMIT this->dataChanged(index, index, {});
1793 });
1794
1795 auto clearData = [column = index.column()](auto &&target) {
1796 if constexpr (row_traits::hasMetaObject) {
1797 if (row_traits::fixed_size() <= 1) {
1798 // multi-role object/gadget: reset all properties
1799 return resetProperty(-1, QRangeModelDetails::pointerTo(target));
1800 } else if (column <= row_traits::fixed_size()) {
1801 return resetProperty(column, QRangeModelDetails::pointerTo(target));
1802 }
1803 } else { // normal structs, values, associative containers
1804 target = {};
1805 return true;
1806 }
1807 return false;
1808 };
1809
1810 success = writeAt(index, clearData);
1811 }
1812 return success;
1813 }
1814
1816 {
1817 // will be 'void' if columns don't all have the same type
1818 using item_type = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
1819 using item_traits = typename QRangeModelDetails::item_traits<item_type>;
1820 return item_traits::roleNames(this);
1821 }
1822
1823 bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
1824 {
1825 if (!QRangeModelDetails::isValid(row))
1826 return true; // nothing to do
1827 return row_traits::for_each_element(QRangeModelDetails::refTo(row),
1828 this->itemModel().index(rowIndex, 0, parent),
1829 [this](const QModelIndex &index, const QObject *item) {
1830 if constexpr (isMutable())
1831 return Self::connectProperties(index, item, m_data.context, m_data.properties);
1832 else
1833 return Self::connectPropertiesConst(index, item, m_data.context, m_data.properties);
1834 });
1835 }
1836
1837 void clearConnectionInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
1838 {
1839 if (!QRangeModelDetails::isValid(row))
1840 return;
1841 row_traits::for_each_element(QRangeModelDetails::refTo(row),
1842 this->itemModel().index(rowIndex, 0, parent),
1843 [this](const QModelIndex &, const QObject *item) {
1844 m_data.connections.removeIf([item](const auto &connection) {
1845 return connection.sender == item;
1846 });
1847 return true;
1848 });
1849 }
1850
1852 {
1853 if constexpr (itemsAreQObjects || rowsAreQObjects) {
1854 using item_type = std::remove_pointer_t<typename row_traits::item_type>;
1855 using Mapping = QRangeModelDetails::AutoConnectContext::AutoConnectMapping;
1856
1857 delete m_data.context;
1858 m_data.connections = {};
1859 switch (this->autoConnectPolicy()) {
1860 case AutoConnectPolicy::None:
1861 m_data.context = nullptr;
1862 break;
1863 case AutoConnectPolicy::Full:
1864 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
1865 if constexpr (itemsAreQObjects) {
1866 m_data.properties = QRangeModelImplBase::roleProperties(this->itemModel(),
1867 item_type::staticMetaObject);
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 if (!m_data.properties.isEmpty())
1874 that().autoConnectPropertiesImpl();
1875 break;
1876 case AutoConnectPolicy::OnRead:
1877 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
1878 if constexpr (itemsAreQObjects) {
1879 m_data.context->mapping = Mapping::Roles;
1880 } else {
1881 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
1882 m_data.context->mapping = Mapping::Columns;
1883 }
1884 break;
1885 }
1886 } else {
1887#ifndef QT_NO_DEBUG
1888 qWarning("All items in the range must be QObject subclasses");
1889#endif
1890 }
1891 }
1892
1893 template <typename InsertFn>
1894 bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
1895 {
1896 if (count == 0)
1897 return false;
1898 range_type * const children = childRange(parent);
1899 if (!children)
1900 return false;
1901
1902 this->beginInsertColumns(parent, column, column + count - 1);
1903
1904 for (auto &child : *children) {
1905 auto it = QRangeModelDetails::pos(child, column);
1906 (void)insertFn(QRangeModelDetails::refTo(child), it, count);
1907 }
1908
1909 this->endInsertColumns();
1910
1911 // endInsertColumns emits columnsInserted, at which point clients might
1912 // have populated the new columns with objects (if the columns aren't objects
1913 // themselves).
1914 if constexpr (itemsAreQObjects) {
1915 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
1916 for (int r = 0; r < that().rowCount(parent); ++r) {
1917 for (int c = column; c < column + count; ++c) {
1918 const QModelIndex index = that().index(r, c, parent);
1919 writeAt(index, [this, &index](QObject *item){
1920 return Self::connectProperties(index, item,
1921 m_data.context, m_data.properties);
1922 });
1923 }
1924 }
1925 }
1926 }
1927
1928 return true;
1929 }
1930
1931 bool insertColumns(int column, int count, const QModelIndex &parent)
1932 {
1933 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
1934 return doInsertColumns(column, count, parent, [](auto &row, auto it, int n){
1935 row.insert(it, n, {});
1936 return true;
1937 });
1938 } else {
1939 return false;
1940 }
1941 }
1942
1943 bool removeColumns(int column, int count, const QModelIndex &parent)
1944 {
1945 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
1946 if (column < 0 || column + count > columnCount(parent))
1947 return false;
1948
1949 range_type * const children = childRange(parent);
1950 if (!children)
1951 return false;
1952
1953 if constexpr (itemsAreQObjects) {
1954 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
1955 for (int r = 0; r < that().rowCount(parent); ++r) {
1956 for (int c = column; c < column + count; ++c) {
1957 const QModelIndex index = that().index(r, c, parent);
1958 writeAt(index, [this](QObject *item){
1959 m_data.connections.removeIf([item](const auto &connection) {
1960 return connection.sender == item;
1961 });
1962 return true;
1963 });
1964 }
1965 }
1966 }
1967 }
1968
1969 this->beginRemoveColumns(parent, column, column + count - 1);
1970 for (auto &child : *children) {
1971 const auto start = QRangeModelDetails::pos(child, column);
1972 QRangeModelDetails::refTo(child).erase(start, std::next(start, count));
1973 }
1974 this->endRemoveColumns();
1975 return true;
1976 }
1977 return false;
1978 }
1979
1980 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1981 const QModelIndex &destParent, int destColumn)
1982 {
1983 // we only support moving columns within the same parent
1984 if (sourceParent != destParent)
1985 return false;
1986 if constexpr (isMutable() && (row_features::has_rotate || row_features::has_splice)) {
1987 if (!Structure::canMoveColumns(sourceParent, destParent))
1988 return false;
1989
1990 if constexpr (dynamicColumns()) {
1991 // we only support ranges as columns, as other types might
1992 // not have the same data type across all columns
1993 range_type * const children = childRange(sourceParent);
1994 if (!children)
1995 return false;
1996
1997 if (!this->beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
1998 destParent, destColumn)) {
1999 return false;
2000 }
2001
2002 for (auto &child : *children)
2003 QRangeModelDetails::rotate(child, sourceColumn, count, destColumn);
2004
2005 this->endMoveColumns();
2006 return true;
2007 }
2008 }
2009 return false;
2010 }
2011
2012 template <typename InsertFn>
2013 bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
2014 {
2015 range_type *children = childRange(parent);
2016 if (!children)
2017 return false;
2018
2019 this->beginInsertRows(parent, row, row + count - 1);
2020
2021 row_ptr parentRow = parent.isValid()
2022 ? QRangeModelDetails::pointerTo(this->rowData(parent))
2023 : nullptr;
2024 (void)std::forward<InsertFn>(insertFn)(*children, parentRow, row, count);
2025
2026 // fix the parent in all children of the modified row, as the
2027 // references back to the parent might have become invalid.
2028 that().resetParentInChildren(children);
2029
2030 this->endInsertRows();
2031
2032 // endInsertRows emits rowsInserted, at which point clients might
2033 // have populated the new row with objects (if the rows aren't objects
2034 // themselves).
2035 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2036 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2037 const auto begin = QRangeModelDetails::pos(children, row);
2038 const auto end = std::next(begin, count);
2039 int rowIndex = row;
2040 for (auto it = begin; it != end; ++it, ++rowIndex)
2041 autoConnectPropertiesInRow(*it, rowIndex, parent);
2042 }
2043 }
2044
2045 return true;
2046 }
2047
2048 bool insertRows(int row, int count, const QModelIndex &parent)
2049 {
2050 if constexpr (canInsertRows()) {
2051 return doInsertRows(row, count, parent,
2052 [this](range_type &children, row_ptr parentRow, int r, int n){
2053 EmptyRowGenerator generator{0, &that(), parentRow};
2054
2055 const auto pos = QRangeModelDetails::pos(children, r);
2056 if constexpr (range_features::has_insert_range) {
2057 children.insert(pos, std::move(generator), EmptyRowGenerator{n});
2058 } else if constexpr (rows_are_owning_or_raw_pointers) {
2059 auto start = children.insert(pos, n, nullptr); // MSVC doesn't like row_type{}
2060 std::copy(std::move(generator), EmptyRowGenerator{n}, start);
2061 } else {
2062 children.insert(pos, n, std::move(*generator));
2063 }
2064 return true;
2065 });
2066 } else {
2067 return false;
2068 }
2069 }
2070
2071 bool removeRows(int row, int count, const QModelIndex &parent = {})
2072 {
2073 if constexpr (canRemoveRows()) {
2074 const int prevRowCount = rowCount(parent);
2075 if (row < 0 || row + count > prevRowCount)
2076 return false;
2077
2078 range_type *children = childRange(parent);
2079 if (!children)
2080 return false;
2081
2082 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2083 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2084 const auto begin = QRangeModelDetails::pos(children, row);
2085 const auto end = std::next(begin, count);
2086 int rowIndex = row;
2087 for (auto it = begin; it != end; ++it, ++rowIndex)
2088 clearConnectionInRow(*it, rowIndex, parent);
2089 }
2090 }
2091
2092 this->beginRemoveRows(parent, row, row + count - 1);
2093 [[maybe_unused]] bool callEndRemoveColumns = false;
2094 if constexpr (dynamicColumns()) {
2095 // if we remove the last row in a dynamic model, then we no longer
2096 // know how many columns we should have, so they will be reported as 0.
2097 if (prevRowCount == count) {
2098 if (const int columns = columnCount(parent)) {
2099 callEndRemoveColumns = true;
2100 this->beginRemoveColumns(parent, 0, columns - 1);
2101 }
2102 }
2103 }
2104 { // erase invalidates iterators
2105 const auto begin = QRangeModelDetails::pos(children, row);
2106 const auto end = std::next(begin, count);
2107 that().deleteRemovedRows(begin, end);
2108 children->erase(begin, end);
2109 }
2110 // fix the parent in all children of the modified row, as the
2111 // references back to the parent might have become invalid.
2112 that().resetParentInChildren(children);
2113
2114 if constexpr (dynamicColumns()) {
2115 if (callEndRemoveColumns) {
2116 Q_ASSERT(columnCount(parent) == 0);
2117 this->endRemoveColumns();
2118 }
2119 }
2120 this->endRemoveRows();
2121 return true;
2122 } else {
2123 return false;
2124 }
2125 }
2126
2127 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
2128 const QModelIndex &destParent, int destRow)
2129 {
2130 if constexpr (isMutable() && (range_features::has_rotate || range_features::has_splice)) {
2131 if (!Structure::canMoveRows(sourceParent, destParent))
2132 return false;
2133
2134 if (sourceParent != destParent) {
2135 return that().moveRowsAcross(sourceParent, sourceRow, count,
2136 destParent, destRow);
2137 }
2138
2139 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
2140 || sourceRow < 0 || sourceRow + count - 1 >= this->rowCount(sourceParent)
2141 || destRow < 0 || destRow > this->rowCount(destParent)) {
2142 return false;
2143 }
2144
2145 range_type *source = childRange(sourceParent);
2146 // moving within the same range
2147 if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
2148 return false;
2149
2150 QRangeModelDetails::rotate(source, sourceRow, count, destRow);
2151
2152 that().resetParentInChildren(source);
2153
2154 this->endMoveRows();
2155 return true;
2156 } else {
2157 return false;
2158 }
2159 }
2160
2161 const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2162 protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2163
2164 QModelIndex parent(const QModelIndex &child) const { return that().parentImpl(child); }
2165
2166 int rowCount(const QModelIndex &parent) const { return that().rowCountImpl(parent); }
2167
2168 static constexpr int fixedColumnCount()
2169 {
2170 if constexpr (one_dimensional_range)
2171 return row_traits::fixed_size();
2172 else
2173 return static_column_count;
2174 }
2175 int columnCount(const QModelIndex &parent) const { return that().columnCountImpl(parent); }
2176
2177 void destroy() { delete std::addressof(that()); }
2178
2179 template <typename BaseMethod, typename BaseMethod::template Overridden<Self> overridden>
2180 using Override = typename Ancestor::template Override<BaseMethod, overridden>;
2181
2190
2205
2209
2210protected:
2212 {
2214 }
2215
2217 {
2218 // We delete row objects if we are not operating on a reference or pointer
2219 // to a range, as in that case, the owner of the referenced/pointed to
2220 // range also owns the row entries.
2221 // ### Problem: if we get a copy of a range (no matter if shared or not),
2222 // then adding rows will create row objects in the model's copy, and the
2223 // client can never delete those. But copied rows will be the same pointer,
2224 // which we must not delete (as we didn't create them).
2225
2226 static constexpr bool modelCopied = !QRangeModelDetails::is_wrapped<Range>() &&
2227 (std::is_reference_v<Range> || std::is_const_v<std::remove_reference_t<Range>>);
2228
2229 static constexpr bool modelShared = QRangeModelDetails::is_any_shared_ptr<Range>();
2230
2231 static constexpr bool default_row_deleter = protocol_traits::is_default &&
2232 protocol_traits::has_deleteRow;
2233
2234 static constexpr bool ambiguousRowOwnership = (modelCopied || modelShared) &&
2235 rows_are_raw_pointers && default_row_deleter;
2236
2237 static_assert(!ambiguousRowOwnership,
2238 "Using of copied and shared tree and table models with rows as raw pointers, "
2239 "and the default protocol is not allowed due to ambiguity of rows ownership. "
2240 "Move the model in, use another row type, or implement a custom tree protocol.");
2241
2242 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
2243 && !QRangeModelDetails::is_any_of<Range, std::reference_wrapper>()) {
2244 const auto begin = QRangeModelDetails::adl_begin(*m_data.model());
2245 const auto end = QRangeModelDetails::adl_end(*m_data.model());
2246 that().deleteRemovedRows(begin, end);
2247 }
2248 }
2249
2250 static constexpr bool canInsertRows()
2251 {
2252 if constexpr (dynamicColumns() && !row_features::has_resize) {
2253 // If we operate on dynamic columns and cannot resize a newly
2254 // constructed row, then we cannot insert.
2255 return false;
2256 } else if constexpr (!protocol_traits::has_newRow) {
2257 // We also cannot insert if we cannot create a new row element
2258 return false;
2259 } else if constexpr (!range_features::has_insert_range
2260 && !std::is_copy_constructible_v<row_type>) {
2261 // And if the row is a move-only type, then the range needs to be
2262 // backed by a container that can move-insert default-constructed
2263 // row elements.
2264 return false;
2265 } else {
2266 return Structure::canInsertRowsImpl();
2267 }
2268 }
2269
2270 static constexpr bool canRemoveRows()
2271 {
2272 return Structure::canRemoveRowsImpl();
2273 }
2274
2275 template <typename F>
2276 bool writeAt(const QModelIndex &index, F&& writer)
2277 {
2278 bool result = false;
2279 row_reference row = rowData(index);
2280
2281 if (QRangeModelDetails::isValid(row)) {
2282 row_traits::for_element_at(row, index.column(), [&writer, &result](auto &&target) {
2283 using target_type = decltype(target);
2284 // we can only assign to an lvalue reference
2285 if constexpr (std::is_lvalue_reference_v<target_type>
2286 && !std::is_const_v<std::remove_reference_t<target_type>>) {
2287 result = writer(std::forward<target_type>(target));
2288 }
2289 });
2290 }
2291
2292 return result;
2293 }
2294
2295 template <typename F>
2296 void readAt(const QModelIndex &index, F&& reader) const {
2297 const_row_reference row = rowData(index);
2298 if (QRangeModelDetails::isValid(row))
2299 row_traits::for_element_at(row, index.column(), std::forward<F>(reader));
2300 }
2301
2302 template <typename Value>
2303 static QVariant read(const Value &value)
2304 {
2305 if constexpr (std::is_constructible_v<QVariant, Value>)
2306 return QVariant(value);
2307 else
2308 return QVariant::fromValue(value);
2309 }
2310 template <typename Value>
2311 static QVariant read(Value *value)
2312 {
2313 if (value) {
2314 if constexpr (std::is_constructible_v<QVariant, Value *>)
2315 return QVariant(value);
2316 else
2317 return read(*value);
2318 }
2319 return {};
2320 }
2321
2322 template <typename Target>
2323 static bool write(Target &target, const QVariant &value)
2324 {
2325 using Type = std::remove_reference_t<Target>;
2326 if constexpr (std::is_constructible_v<Target, QVariant>) {
2327 target = value;
2328 return true;
2329 } else if (value.canConvert<Type>()) {
2330 target = value.value<Type>();
2331 return true;
2332 }
2333 return false;
2334 }
2335 template <typename Target>
2336 static bool write(Target *target, const QVariant &value)
2337 {
2338 if (target)
2339 return write(*target, value);
2340 return false;
2341 }
2342
2343 template <typename ItemType>
2345 {
2346 struct {
2347 operator QMetaProperty() const {
2348 const QByteArray roleName = that.itemModel().roleNames().value(role);
2349 const QMetaObject &mo = ItemType::staticMetaObject;
2350 if (const int index = mo.indexOfProperty(roleName.data());
2351 index >= 0) {
2352 return mo.property(index);
2353 }
2354 return {};
2355 }
2356 const QRangeModelImpl &that;
2357 const int role;
2358 } findProperty{*this, role};
2359
2360 if constexpr (ModelData::cachesProperties)
2361 return *m_data.properties.tryEmplace(role, findProperty).iterator;
2362 else
2363 return findProperty;
2364 }
2365
2366 void connectPropertyOnRead(const QModelIndex &index, int role,
2367 const QObject *gadget, const QMetaProperty &prop) const
2368 {
2369 const typename ModelData::Connection connection = {gadget, role};
2370 if (prop.hasNotifySignal() && this->autoConnectPolicy() == AutoConnectPolicy::OnRead
2371 && !m_data.connections.contains(connection)) {
2372 if constexpr (isMutable())
2373 Self::connectProperty(index, gadget, m_data.context, role, prop);
2374 else
2375 Self::connectPropertyConst(index, gadget, m_data.context, role, prop);
2376 m_data.connections.insert(connection);
2377 }
2378 }
2379
2380 template <typename ItemType>
2381 QVariant readRole(const QModelIndex &index, int role, ItemType *gadget) const
2382 {
2383 using item_type = std::remove_pointer_t<ItemType>;
2384 QVariant result;
2385 QMetaProperty prop = roleProperty<item_type>(role);
2386 if (!prop.isValid() && role == Qt::EditRole) {
2387 role = Qt::DisplayRole;
2388 prop = roleProperty<item_type>(Qt::DisplayRole);
2389 }
2390
2391 if (prop.isValid()) {
2392 if constexpr (itemsAreQObjects)
2393 connectPropertyOnRead(index, role, gadget, prop);
2394 result = readProperty(prop, gadget);
2395 }
2396 return result;
2397 }
2398
2399 template <typename ItemType>
2400 QVariant readRole(const QModelIndex &index, int role, const ItemType &gadget) const
2401 {
2402 return readRole(index, role, &gadget);
2403 }
2404
2405 template <typename ItemType>
2406 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
2407 {
2408 if constexpr (std::is_base_of_v<QObject, ItemType>)
2409 return prop.read(gadget);
2410 else
2411 return prop.readOnGadget(gadget);
2412 }
2413
2414 template <typename ItemType>
2415 QVariant readProperty(const QModelIndex &index, ItemType *gadget) const
2416 {
2417 using item_type = std::remove_pointer_t<ItemType>;
2418 const QMetaObject &mo = item_type::staticMetaObject;
2419 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
2420
2421 if constexpr (rowsAreQObjects)
2422 connectPropertyOnRead(index, Qt::DisplayRole, gadget, prop);
2423
2424 return readProperty(prop, gadget);
2425 }
2426
2427 template <typename ItemType>
2428 QVariant readProperty(const QModelIndex &index, const ItemType &gadget) const
2429 {
2430 return readProperty(index, &gadget);
2431 }
2432
2433 template <typename ItemType>
2434 bool writeRole(int role, ItemType *gadget, const QVariant &data)
2435 {
2436 using item_type = std::remove_pointer_t<ItemType>;
2437 auto prop = roleProperty<item_type>(role);
2438 if (!prop.isValid() && role == Qt::EditRole)
2439 prop = roleProperty<item_type>(Qt::DisplayRole);
2440
2441 return prop.isValid() ? writeProperty(prop, gadget, data) : false;
2442 }
2443
2444 template <typename ItemType>
2445 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
2446 {
2447 return writeRole(role, &gadget, data);
2448 }
2449
2450 template <typename ItemType>
2451 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
2452 {
2453 if constexpr (std::is_base_of_v<QObject, ItemType>)
2454 return prop.write(gadget, data);
2455 else
2456 return prop.writeOnGadget(gadget, data);
2457 }
2458 template <typename ItemType>
2459 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
2460 {
2461 using item_type = std::remove_pointer_t<ItemType>;
2462 const QMetaObject &mo = item_type::staticMetaObject;
2463 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
2464 }
2465
2466 template <typename ItemType>
2467 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
2468 {
2469 return writeProperty(property, &gadget, data);
2470 }
2471
2472 template <typename ItemType>
2473 static bool resetProperty(int property, ItemType *object)
2474 {
2475 using item_type = std::remove_pointer_t<ItemType>;
2476 const QMetaObject &mo = item_type::staticMetaObject;
2477 bool success = true;
2478 if (property == -1) {
2479 // reset all properties
2480 if constexpr (std::is_base_of_v<QObject, item_type>) {
2481 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
2482 success = writeProperty(mo.property(p), object, {}) && success;
2483 } else { // reset a gadget by assigning a default-constructed
2484 *object = {};
2485 }
2486 } else {
2487 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
2488 }
2489 return success;
2490 }
2491
2492 template <typename ItemType>
2493 static bool resetProperty(int property, ItemType &&object)
2494 {
2495 return resetProperty(property, &object);
2496 }
2497
2498 // helpers
2499 const_row_reference rowData(const QModelIndex &index) const
2500 {
2501 Q_ASSERT(index.isValid());
2502 return that().rowDataImpl(index);
2503 }
2504
2505 row_reference rowData(const QModelIndex &index)
2506 {
2507 Q_ASSERT(index.isValid());
2508 return that().rowDataImpl(index);
2509 }
2510
2511 const range_type *childRange(const QModelIndex &index) const
2512 {
2513 if (!index.isValid())
2514 return m_data.model();
2515 if (index.column()) // only items at column 0 can have children
2516 return nullptr;
2517 return that().childRangeImpl(index);
2518 }
2519
2520 range_type *childRange(const QModelIndex &index)
2521 {
2522 if (!index.isValid())
2523 return m_data.model();
2524 if (index.column()) // only items at column 0 can have children
2525 return nullptr;
2526 return that().childRangeImpl(index);
2527 }
2528
2529 template <typename, typename, typename> friend class QRangeModelAdapter;
2530
2532};
2533
2534// Implementations that depends on the model structure (flat vs tree) that will
2535// be specialized based on a protocol type. The main template implements tree
2536// support through a protocol type.
2537template <typename Range, typename Protocol>
2539 : public QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
2540{
2541 using Base = QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2542 friend class QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2543
2544 using range_type = typename Base::range_type;
2545 using range_features = typename Base::range_features;
2546 using row_type = typename Base::row_type;
2547 using row_ptr = typename Base::row_ptr;
2548 using const_row_ptr = typename Base::const_row_ptr;
2549
2550 using tree_traits = typename Base::protocol_traits;
2551 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
2552
2553 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
2554 QRangeModelDetails::is_smart_ptr<row_type>() ||
2555 QRangeModelDetails::is_any_of<row_type, std::reference_wrapper>();
2556 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
2557
2558public:
2559 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
2560 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
2561 {};
2562
2563 void setParentRow(range_type &children, row_ptr parent)
2564 {
2565 for (auto &&child : children)
2566 this->protocol().setParentRow(QRangeModelDetails::refTo(child), parent);
2567 resetParentInChildren(&children);
2568 }
2569
2570 void deleteRemovedRows(range_type &range)
2571 {
2572 deleteRemovedRows(QRangeModelDetails::adl_begin(range), QRangeModelDetails::adl_end(range));
2573 }
2574
2575 bool autoConnectProperties(const QModelIndex &parent) const
2576 {
2577 auto *children = this->childRange(parent);
2578 if (!children)
2579 return true;
2580 return autoConnectPropertiesRange(QRangeModelDetails::refTo(children), parent);
2581 }
2582
2583protected:
2584 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
2585 {
2586 if (!parent.isValid())
2587 return this->createIndex(row, column);
2588 // only items at column 0 can have children
2589 if (parent.column())
2590 return QModelIndex();
2591
2592 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
2593 const auto &parentSiblings = childrenOf(grandParent);
2594 const auto it = QRangeModelDetails::pos(parentSiblings, parent.row());
2595 return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
2596 }
2597
2598 QModelIndex parentImpl(const QModelIndex &child) const
2599 {
2600 if (!child.isValid())
2601 return {};
2602
2603 // no pointer to parent row - no parent
2604 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
2605 if (!parentRow)
2606 return {};
2607
2608 // get the siblings of the parent via the grand parent
2609 auto &&grandParent = this->protocol().parentRow(QRangeModelDetails::refTo(parentRow));
2610 const range_type &parentSiblings = childrenOf(QRangeModelDetails::pointerTo(grandParent));
2611 // find the index of parentRow
2612 const auto begin = QRangeModelDetails::adl_begin(parentSiblings);
2613 const auto end = QRangeModelDetails::adl_end(parentSiblings);
2614 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
2615 return QRangeModelDetails::pointerTo(std::forward<decltype(s)>(s)) == parentRow;
2616 });
2617 if (it != end)
2618 return this->createIndex(std::distance(begin, it), 0,
2619 QRangeModelDetails::pointerTo(grandParent));
2620 return {};
2621 }
2622
2623 int rowCountImpl(const QModelIndex &parent) const
2624 {
2625 return Base::size(this->childRange(parent));
2626 }
2627
2628 int columnCountImpl(const QModelIndex &) const
2629 {
2630 // All levels of a tree have to have the same, fixed, column count.
2631 // If static_column_count is -1 for a tree, static assert fires
2632 return Base::fixedColumnCount();
2633 }
2634
2635 static constexpr Qt::ItemFlags defaultFlags()
2636 {
2637 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2638 }
2639
2640 static constexpr bool canInsertRowsImpl()
2641 {
2642 // We must not insert rows if we cannot adjust the parents of the
2643 // children of the following rows. We don't have to do that if the
2644 // range operates on pointers.
2645 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2646 && Base::dynamicRows() && range_features::has_insert;
2647 }
2648
2649 static constexpr bool canRemoveRowsImpl()
2650 {
2651 // We must not remove rows if we cannot adjust the parents of the
2652 // children of the following rows. We don't have to do that if the
2653 // range operates on pointers.
2654 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2655 && Base::dynamicRows() && range_features::has_erase;
2656 }
2657
2658 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
2659 {
2660 return true;
2661 }
2662
2663 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
2664 {
2665 return true;
2666 }
2667
2668 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
2669 const QModelIndex &destParent, int destRow)
2670 {
2671 // If rows are pointers, then reference to the parent row don't
2672 // change, so we can move them around freely. Otherwise we need to
2673 // be able to explicitly update the parent pointer.
2674 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
2675 return false;
2676 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
2677 return false;
2678 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
2679 destParent, destRow)) {
2680 return false;
2681 }
2682
2683 range_type *source = this->childRange(sourceParent);
2684 range_type *destination = this->childRange(destParent);
2685
2686 // If we can insert data from another range into, then
2687 // use that to move the old data over.
2688 const auto destStart = QRangeModelDetails::pos(destination, destRow);
2689 if constexpr (range_features::has_insert_range) {
2690 const auto sourceStart = QRangeModelDetails::pos(*source, sourceRow);
2691 const auto sourceEnd = std::next(sourceStart, count);
2692
2693 destination->insert(destStart, std::move_iterator(sourceStart),
2694 std::move_iterator(sourceEnd));
2695 } else if constexpr (std::is_copy_constructible_v<row_type>) {
2696 // otherwise we have to make space first, and copy later.
2697 destination->insert(destStart, count, row_type{});
2698 }
2699
2700 row_ptr parentRow = destParent.isValid()
2701 ? QRangeModelDetails::pointerTo(this->rowData(destParent))
2702 : nullptr;
2703
2704 // if the source's parent was already inside the new parent row,
2705 // then the source row might have become invalid, so reset it.
2706 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
2707 if (sourceParent.row() < destRow) {
2708 source = this->childRange(sourceParent);
2709 } else {
2710 // the source parent moved down within destination
2711 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
2712 sourceParent.internalPointer()));
2713 }
2714 }
2715
2716 // move the data over and update the parent pointer
2717 {
2718 const auto writeStart = QRangeModelDetails::pos(destination, destRow);
2719 const auto writeEnd = std::next(writeStart, count);
2720 const auto sourceStart = QRangeModelDetails::pos(source, sourceRow);
2721 const auto sourceEnd = std::next(sourceStart, count);
2722
2723 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
2724 // move data over if not already done, otherwise
2725 // only fix the parent pointer
2726 if constexpr (!range_features::has_insert_range)
2727 *write = std::move(*read);
2728 this->protocol().setParentRow(QRangeModelDetails::refTo(*write), parentRow);
2729 }
2730 // remove the old rows from the source parent
2731 source->erase(sourceStart, sourceEnd);
2732 }
2733
2734 // Fix the parent pointers in children of both source and destination
2735 // ranges, as the references to the entries might have become invalid.
2736 // We don't have to do that if the rows are pointers, as in that case
2737 // the references to the entries are stable.
2738 resetParentInChildren(destination);
2740
2741 this->endMoveRows();
2742 return true;
2743 }
2744
2745 auto makeEmptyRow(row_ptr parentRow)
2746 {
2747 // tree traversal protocol: if we are here, then it must be possible
2748 // to change the parent of a row.
2749 static_assert(tree_traits::has_setParentRow);
2750 row_type empty_row = this->protocol().newRow();
2751 if (QRangeModelDetails::isValid(empty_row) && parentRow)
2752 this->protocol().setParentRow(QRangeModelDetails::refTo(empty_row), parentRow);
2753 return empty_row;
2754 }
2755
2756 template <typename It, typename Sentinel>
2757 void deleteRemovedRows(It &&begin, Sentinel &&end)
2758 {
2759 if constexpr (tree_traits::has_deleteRow) {
2760 for (auto it = begin; it != end; ++it) {
2761 if constexpr (Base::isMutable()) {
2762 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
2763 if (QRangeModelDetails::isValid(children)) {
2764 deleteRemovedRows(QRangeModelDetails::adl_begin(children),
2765 QRangeModelDetails::adl_end(children));
2766 QRangeModelDetails::refTo(children) = range_type{ };
2767 }
2768 }
2769
2770 this->protocol().deleteRow(std::move(*it));
2771 }
2772 }
2773 }
2774
2775 void resetParentInChildren(range_type *children)
2776 {
2777 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
2778 const auto begin = QRangeModelDetails::adl_begin(*children);
2779 const auto end = QRangeModelDetails::adl_end(*children);
2780 for (auto it = begin; it != end; ++it) {
2781 decltype(auto) maybeChildren = this->protocol().childRows(*it);
2782 if (QRangeModelDetails::isValid(maybeChildren)) {
2783 auto &childrenRef = QRangeModelDetails::refTo(maybeChildren);
2784 QModelIndexList fromIndexes;
2785 QModelIndexList toIndexes;
2786 fromIndexes.reserve(Base::size(childrenRef));
2787 toIndexes.reserve(Base::size(childrenRef));
2788 auto *parentRow = QRangeModelDetails::pointerTo(*it);
2789
2790 int row = 0;
2791 for (auto &child : childrenRef) {
2792 const_row_ptr oldParent = this->protocol().parentRow(child);
2793 if (oldParent != parentRow) {
2794 fromIndexes.append(this->createIndex(row, 0, oldParent));
2795 toIndexes.append(this->createIndex(row, 0, parentRow));
2796 this->protocol().setParentRow(child, parentRow);
2797 }
2798 ++row;
2799 }
2800 this->changePersistentIndexList(fromIndexes, toIndexes);
2801 resetParentInChildren(&childrenRef);
2802 }
2803 }
2804 }
2805 }
2806
2807 bool autoConnectPropertiesRange(const range_type &range, const QModelIndex &parent) const
2808 {
2809 int rowIndex = 0;
2810 for (const auto &row : range) {
2811 if (!this->autoConnectPropertiesInRow(row, rowIndex, parent))
2812 return false;
2813 Q_ASSERT(QRangeModelDetails::isValid(row));
2814 const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2815 if (QRangeModelDetails::isValid(children)) {
2816 if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
2817 this->itemModel().index(rowIndex, 0, parent))) {
2818 return false;
2819 }
2820 }
2821 ++rowIndex;
2822 }
2823 return true;
2824 }
2825
2827 {
2828 return autoConnectPropertiesRange(*this->m_data.model(), {});
2829 }
2830
2831 decltype(auto) rowDataImpl(const QModelIndex &index) const
2832 {
2833 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
2834 const range_type &siblings = childrenOf(parentRow);
2835 Q_ASSERT(index.row() < int(Base::size(siblings)));
2836 return *QRangeModelDetails::pos(siblings, index.row());
2837 }
2838
2839 decltype(auto) rowDataImpl(const QModelIndex &index)
2840 {
2841 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
2842 range_type &siblings = childrenOf(parentRow);
2843 Q_ASSERT(index.row() < int(Base::size(siblings)));
2844 return *QRangeModelDetails::pos(siblings, index.row());
2845 }
2846
2847 const range_type *childRangeImpl(const QModelIndex &index) const
2848 {
2849 const auto &row = this->rowData(index);
2850 if (!QRangeModelDetails::isValid(row))
2851 return static_cast<const range_type *>(nullptr);
2852
2853 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2854 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2855 }
2856
2857 range_type *childRangeImpl(const QModelIndex &index)
2858 {
2859 auto &row = this->rowData(index);
2860 if (!QRangeModelDetails::isValid(row))
2861 return static_cast<range_type *>(nullptr);
2862
2863 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2864 using Children = std::remove_reference_t<decltype(children)>;
2865
2866 if constexpr (QRangeModelDetails::is_any_of<Children, std::optional>())
2867 if constexpr (std::is_default_constructible<typename Children::value_type>()) {
2868 if (!children)
2869 children.emplace(range_type{});
2870 }
2871
2872 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
2873 }
2874
2875 const range_type &childrenOf(const_row_ptr row) const
2876 {
2877 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2878 : *this->m_data.model();
2879 }
2880
2881private:
2882 range_type &childrenOf(row_ptr row)
2883 {
2884 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
2885 : *this->m_data.model();
2886 }
2887};
2888
2889// specialization for flat models without protocol
2890template <typename Range>
2893{
2896
2897 static constexpr bool is_mutable_impl = true;
2898
2899public:
2900 using range_type = typename Base::range_type;
2902 using row_type = typename Base::row_type;
2904 using row_traits = typename Base::row_traits;
2905 using row_features = typename Base::row_features;
2906
2907 explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
2908 : Base(std::forward<Range>(model), {}, itemModel)
2909 {}
2910
2911protected:
2912 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
2913 {
2914 if constexpr (Base::dynamicColumns()) {
2915 if (column < int(Base::size(*QRangeModelDetails::pos(*this->m_data.model(), row))))
2916 return this->createIndex(row, column);
2917#ifndef QT_NO_DEBUG
2918 // if we got here, then column < columnCount(), but this row is too short
2919 qCritical("QRangeModel: Column-range at row %d is not large enough!", row);
2920#endif
2921 return {};
2922 } else {
2923 return this->createIndex(row, column);
2924 }
2925 }
2926
2927 QModelIndex parentImpl(const QModelIndex &) const
2928 {
2929 return {};
2930 }
2931
2932 int rowCountImpl(const QModelIndex &parent) const
2933 {
2934 if (parent.isValid())
2935 return 0;
2936 return int(Base::size(*this->m_data.model()));
2937 }
2938
2939 int columnCountImpl(const QModelIndex &parent) const
2940 {
2941 if (parent.isValid())
2942 return 0;
2943
2944 // in a table, all rows have the same number of columns (as the first row)
2945 if constexpr (Base::dynamicColumns()) {
2946 return int(Base::size(*this->m_data.model()) == 0
2947 ? 0
2948 : Base::size(*QRangeModelDetails::adl_begin(*this->m_data.model())));
2949 } else {
2950 return Base::fixedColumnCount();
2951 }
2952 }
2953
2954 static constexpr Qt::ItemFlags defaultFlags()
2955 {
2956 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
2957 }
2958
2959 static constexpr bool canInsertRowsImpl()
2960 {
2961 return Base::dynamicRows() && range_features::has_insert;
2962 }
2963
2964 static constexpr bool canRemoveRowsImpl()
2965 {
2966 return Base::dynamicRows() && range_features::has_erase;
2967 }
2968
2969 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
2970 {
2971 return !source.isValid() && !destination.isValid();
2972 }
2973
2974 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
2975 {
2976 return !source.isValid() && !destination.isValid();
2977 }
2978
2979 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
2980 const QModelIndex &, int) noexcept
2981 {
2982 // table/flat model: can't move rows between different parents
2983 return false;
2984 }
2985
2986 auto makeEmptyRow(typename Base::row_ptr)
2987 {
2988 row_type empty_row = this->protocol().newRow();
2989
2990 // dynamically sized rows all have to have the same column count
2991 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
2992 if (QRangeModelDetails::isValid(empty_row))
2993 QRangeModelDetails::refTo(empty_row).resize(this->columnCount({}));
2994 }
2995
2996 return empty_row;
2997 }
2998
2999 template <typename It, typename Sentinel>
3000 void deleteRemovedRows(It &&begin, Sentinel &&end)
3001 {
3002 if constexpr (Base::protocol_traits::has_deleteRow) {
3003 for (auto it = begin; it != end; ++it)
3004 this->protocol().deleteRow(std::move(*it));
3005 }
3006 }
3007
3008 decltype(auto) rowDataImpl(const QModelIndex &index) const
3009 {
3010 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
3011 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
3012 }
3013
3014 decltype(auto) rowDataImpl(const QModelIndex &index)
3015 {
3016 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
3017 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
3018 }
3019
3020 const range_type *childRangeImpl(const QModelIndex &) const
3021 {
3022 return nullptr;
3023 }
3024
3025 range_type *childRangeImpl(const QModelIndex &)
3026 {
3027 return nullptr;
3028 }
3029
3030 const range_type &childrenOf(const_row_ptr row) const
3031 {
3032 Q_ASSERT(!row);
3033 return *this->m_data.model();
3034 }
3035
3036 void resetParentInChildren(range_type *)
3037 {
3038 }
3039
3041 {
3042 bool result = true;
3043 int rowIndex = 0;
3044 for (const auto &row : *this->m_data.model()) {
3045 result &= this->autoConnectPropertiesInRow(row, rowIndex, {});
3046 ++rowIndex;
3047 }
3048 return result;
3049 }
3050};
3051
3052QT_END_NAMESPACE
3053
3054#endif // Q_QDOC
3055
3056#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 constexpr bool hasHeaderData
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