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