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