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/qcollator.h>
21#include <QtCore/qquasivirtual_impl.h>
22#include <QtCore/qmetaobject.h>
23#include <QtCore/qvariant.h>
24#include <QtCore/qmap.h>
25#include <QtCore/qscopedvaluerollback.h>
26#include <QtCore/qset.h>
27#include <QtCore/qregularexpression.h>
28#include <QtCore/qvarlengtharray.h>
29
30#include <algorithm>
31#include <functional>
32#include <iterator>
33#include <type_traits>
34#include <QtCore/qxptype_traits.h>
35#include <tuple>
36#include <QtCore/q23utility.h>
37
38QT_BEGIN_NAMESPACE
39
40namespace QRangeModelDetails
41{
42 template <typename T, template <typename...> typename... Templates>
44
45 template <template <typename...> typename Template,
46 typename... Params,
47 template <typename...> typename... Templates>
49
50 template <typename T,
51 template <typename...> typename Template,
52 template <typename...> typename... Templates>
54
55 template <typename T, template <typename...> typename... Templates>
57
58 template <typename T, typename = void>
60
61 template <typename T>
62 struct is_validatable<T, std::void_t<decltype(*std::declval<T>())>>
63 : std::is_constructible<bool, T> {};
64
65 template <typename T, typename = void>
67
68 template <typename T>
71 std::is_pointer<decltype(std::declval<T&>().get())>,
72 std::is_same<decltype(*std::declval<T&>().get()), decltype(*std::declval<T&>())>,
74 >>>
75 : std::true_type
76 {};
77
78 // TODO: shouldn't we check is_smart_ptr && !is_copy_constructible && !is_copy_assignable
79 // to support users-specific ptrs?
80 template <typename T>
82#ifndef QT_NO_SCOPED_POINTER
84#endif
86 >;
87
88 template <typename T>
91
92 template <typename T>
94 std::is_pointer<T>>;
95
96 template <typename T>
97 static auto pointerTo(T&& t) {
98 using Type = q20::remove_cvref_t<T>;
99 if constexpr (is_any_of<Type, std::optional>())
100 return t ? std::addressof(*std::forward<T>(t)) : nullptr;
101 else if constexpr (std::is_pointer<Type>())
102 return t;
103 else if constexpr (is_smart_ptr<Type>())
104 return t.get();
105 else if constexpr (is_any_of<Type, std::reference_wrapper>())
106 return std::addressof(t.get());
107 else
108 return std::addressof(std::forward<T>(t));
109 }
110
111 template <typename T>
113 {
115 };
116 template <>
117 struct wrapped_helper<void>
118 {
119 using type = void;
120 };
121 template <typename T>
123
124 template <typename T>
127 >>;
128
129 template <typename T, typename = void>
131 template <typename T, std::size_t N>
132 struct tuple_like<std::array<T, N>> : std::false_type {};
133 template <typename T>
136 template <typename T>
137 [[maybe_unused]] static constexpr bool tuple_like_v = tuple_like<T>::value;
138
139 template <typename T, typename = void>
141 template <typename T, std::size_t N>
142 struct array_like<std::array<T, N>> : std::true_type {};
143 template <typename T, std::size_t N>
144 struct array_like<T[N]> : std::true_type {};
145 template <typename T>
146 [[maybe_unused]] static constexpr bool array_like_v = array_like<T>::value;
147
148 template <typename T, typename = void>
150 template <typename T>
153 template <typename T>
154 [[maybe_unused]] static constexpr bool has_metaobject_v = has_metaobject<T>::value;
155
156 template <typename T>
157 static constexpr bool isValid(const T &t) noexcept
158 {
159 if constexpr (std::is_array_v<T>)
160 return true;
161 else if constexpr (is_validatable<T>())
162 return bool(t);
163 else
164 return true;
165 }
166
167 template <typename T>
168 static decltype(auto) refTo(T&& t) {
169 Q_ASSERT(QRangeModelDetails::isValid(t));
170 // it's allowed to move only if the object holds unique ownership of the wrapped data
171 using Type = q20::remove_cvref_t<T>;
172 if constexpr (is_any_of<T, std::optional>())
173 return *std::forward<T>(t); // let std::optional resolve dereferencing
174 if constexpr (!is_wrapped<Type>() || is_any_unique_ptr<Type>())
175 return q23::forward_like<T>(*QRangeModelDetails::pointerTo(t));
176 else
177 return *QRangeModelDetails::pointerTo(t);
178 }
179
180 template <typename It>
181 auto key(It&& it) -> decltype(it.key()) { return std::forward<It>(it).key(); }
182 template <typename It>
183 auto key(It&& it) -> decltype((it->first)) { return std::forward<It>(it)->first; }
184
185 template <typename It>
186 auto value(It&& it) -> decltype(it.value()) { return std::forward<It>(it).value(); }
187 template <typename It>
188 auto value(It&& it) -> decltype((it->second)) { return std::forward<It>(it)->second; }
189
190 // use our own, ADL friendly versions of begin/end so that we can overload
191 // for pointers.
192 using std::begin;
193 using std::end;
194 template <typename C>
195 static auto adl_begin(C &&c) -> decltype(begin(QRangeModelDetails::refTo(std::forward<C>(c))))
196 { return begin(QRangeModelDetails::refTo(std::forward<C>(c))); }
197 template <typename C>
198 static auto adl_end(C &&c) -> decltype(end(QRangeModelDetails::refTo(std::forward<C>(c))))
199 { return end(QRangeModelDetails::refTo(std::forward<C>(c))); }
200 template <typename C>
201 static auto pos(C &&c, int i)
202 { return std::next(QRangeModelDetails::adl_begin(std::forward<C>(c)), i); }
203
204 // Test if a type is a range, and whether we can modify it using the
205 // standard C++ container member functions insert, erase, and resize.
206 // For the sake of QAIM, we cannot modify a range if it holds const data
207 // even if the range itself is not const; we'd need to initialize new rows
208 // and columns, and move old row and column data.
209 template <typename C, typename = void>
211
212 template <typename C>
213 struct test_insert<C, std::void_t<decltype(std::declval<C>().insert(
214 std::declval<typename C::const_iterator>(),
215 std::declval<typename C::size_type>(),
216 std::declval<typename C::value_type>()
217 ))>>
218 : std::true_type
219 {};
220
221 // Can we insert from another (identical) range? Required to support
222 // move-only types
223 template <typename C, typename = void>
225
226 template <typename C>
227 struct test_insert_range<C, std::void_t<decltype(std::declval<C&>().insert(
228 std::declval<typename C::const_iterator&>(),
229 std::declval<std::move_iterator<typename C::iterator>&>(),
230 std::declval<std::move_iterator<typename C::iterator>&>()
231 ))>>
232 : std::true_type
233 {};
234
235 template <typename C, typename = void>
237
238 template <typename C>
239 struct test_erase<C, std::void_t<decltype(std::declval<C>().erase(
240 std::declval<typename C::const_iterator>(),
241 std::declval<typename C::const_iterator>()
242 ))>>
243 : std::true_type
244 {};
245
246 template <typename C, typename = void>
248
249 template <typename C>
250 struct test_resize<C, std::void_t<decltype(std::declval<C>().resize(
251 std::declval<typename C::size_type>(),
252 std::declval<typename C::value_type>()
253 ))>>
254 : std::true_type
255 {};
256
257 // we use std::rotate in moveRows/Columns, which requires the values (which
258 // might be const if we only get a const iterator) to be swappable, and the
259 // iterator type to be at least a forward iterator
260 template <typename It>
261 using test_rotate = std::conjunction<
262 std::is_swappable<decltype(*std::declval<It>())>,
263 std::is_base_of<std::forward_iterator_tag,
264 typename std::iterator_traits<It>::iterator_category>
265 >;
266
267 template <typename C, typename = void>
269
270 template <typename C>
271 struct test_splice<C, std::void_t<decltype(std::declval<C>().splice(
272 std::declval<typename C::const_iterator>(),
273 std::declval<C&>(),
274 std::declval<typename C::const_iterator>(),
275 std::declval<typename C::const_iterator>()
276 ))>>
277 : std::true_type
278 {};
279
280 template <typename C>
281 static void rotate(C& c, int src, int count, int dst) {
282 auto& container = QRangeModelDetails::refTo(c);
283 using Container = std::remove_reference_t<decltype(container)>;
284
285 const auto srcBegin = QRangeModelDetails::pos(container, src);
286 const auto srcEnd = std::next(srcBegin, count);
287 const auto dstBegin = QRangeModelDetails::pos(container, dst);
288
289 if constexpr (test_splice<Container>::value) {
290 if (dst > src && dst < src + count) // dst must be out of the source range
291 container.splice(srcBegin, container, dstBegin, srcEnd);
292 else if (dst != src) // otherwise, std::list gets corrupted
293 container.splice(dstBegin, container, srcBegin, srcEnd);
294 } else {
295 if (src < dst) // moving right
296 std::rotate(srcBegin, srcEnd, dstBegin);
297 else // moving left
298 std::rotate(dstBegin, srcBegin, srcEnd);
299 }
300 }
301
302 // Test if a type is an associative container that we can use for multi-role
303 // data, i.e. has a key_type and a mapped_type typedef, and maps from int,
304 // Qt::ItemDataRole, or QString to QVariant. This excludes std::set (and
305 // unordered_set), which are not useful for us anyway even though they are
306 // considered associative containers.
307 template <typename C, typename = void> struct is_multi_role : std::false_type
308 {
309 static constexpr bool int_key = false;
310 };
311 template <typename C> // Qt::ItemDataRole -> QVariant, or QString -> QVariant, int -> QVariant
312 struct is_multi_role<C, std::void_t<typename C::key_type, typename C::mapped_type>>
313 : std::conjunction<std::disjunction<std::is_same<typename C::key_type, int>,
314 std::is_same<typename C::key_type, Qt::ItemDataRole>,
315 std::is_same<typename C::key_type, QString>>,
316 std::is_same<typename C::mapped_type, QVariant>>
317 {
318 static constexpr bool int_key = !std::is_same_v<typename C::key_type, QString>;
319 };
320 template <typename C>
321 [[maybe_unused]]
322 static constexpr bool is_multi_role_v = is_multi_role<C>::value;
323
324 using std::size;
325 template <typename C, typename = void>
327 template <typename C>
328 struct test_size<C, std::void_t<decltype(size(std::declval<C&>()))>> : std::true_type {};
329
330 template <typename C, typename = void>
332 template <typename C>
333 struct test_cbegin<C, std::void_t<decltype(QRangeModelDetails::adl_begin(std::declval<const C&>()))>>
334 : std::true_type
335 {};
336
337 template <typename C, typename = void>
339 static constexpr bool is_mutable = !std::is_const_v<C>;
340 static constexpr bool has_insert = false;
341 static constexpr bool has_insert_range = false;
342 static constexpr bool has_erase = false;
343 static constexpr bool has_resize = false;
344 static constexpr bool has_rotate = false;
345 static constexpr bool has_splice = false;
346 static constexpr bool has_cbegin = false;
347 };
348 template <typename C>
350 decltype(QRangeModelDetails::adl_end(std::declval<C&>())),
352 >> : std::true_type
353 {
356 static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
357 static constexpr bool has_insert = test_insert<C>();
358 static constexpr bool has_insert_range = test_insert_range<C>();
359 static constexpr bool has_erase = test_erase<C>();
360 static constexpr bool has_resize = test_resize<C>();
361 static constexpr bool has_rotate = test_rotate<iterator>();
362 static constexpr bool has_splice = test_splice<C>();
363 static constexpr bool has_cbegin = test_cbegin<C>::value;
364 };
365
366 // Specializations for types that look like ranges, but should be
367 // treated as values.
368 enum class Mutable { Yes, No };
369 template <Mutable IsMutable>
371 static constexpr bool is_mutable = IsMutable == Mutable::Yes;
372 static constexpr bool has_insert = false;
373 static constexpr bool has_erase = false;
374 static constexpr bool has_resize = false;
375 static constexpr bool has_rotate = false;
376 static constexpr bool has_splice = false;
377 static constexpr bool has_cbegin = true;
378 };
380 template <> struct range_traits<QString> : iterable_value<Mutable::Yes> {};
381 template <class CharT, class Traits, class Allocator>
384
385 // const T * and views are read-only
386 template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
388
389 template <typename C>
391 template <typename C>
392 [[maybe_unused]] static constexpr bool is_range_v = is_range<C>();
393
394 // Detect an ItemAccess specialization with static read/writeRole members
395 template <typename T> struct QRangeModelItemAccess;
396
397 template <typename T>
399 {
402
403 template <typename Access, typename Test>
404 using hasReadRole_test = decltype(Access::readRole(std::declval<const Test &>(),
405 Qt::DisplayRole));
406 static constexpr bool hasReadRole = qxp::is_detected_v<hasReadRole_test, ItemAccess, ItemType>;
407
408 template <typename Access, typename Test>
411 static constexpr bool hasWriteRole = qxp::is_detected_v<hasWriteRole_test, ItemAccess, ItemType>;
412
413 template <typename Access, typename Test>
414 using hasFlags_test = decltype(Access::flags(std::declval<const Test&>()));
415
416 static constexpr bool hasFlags = qxp::is_detected_v<hasFlags_test, ItemAccess, ItemType>;
417 };
418
419 // Detect which options are set to override default heuristics. Since
420 // QRangeModel is not yet defined we need to delay the evaluation.
421 template <typename T> struct QRangeModelRowOptions;
422
423 template <typename T, typename = void>
425 {
427 };
428
429 template <typename T>
431 : std::true_type
432 {
434 using RowCategory = decltype(rowCategory);
436 };
437
438 template <typename RowOptions>
440 template <typename row_type>
442
443 template <typename row_type>
444 using hasRowFlags_test = decltype(QRangeModelRowOptions<row_type>::flags(std::declval<const row_type &>()));
445 template <typename row_type>
446 static constexpr bool hasRowFlags = qxp::is_detected_v<hasRowFlags_test, row_type>;
447
448 // Find out how many fixed elements can be retrieved from a row element.
449 // main template for simple values and ranges. Specializing for ranges
450 // is ambiguous with arrays, as they are also ranges
451 template <typename T, typename = void>
452 struct row_traits {
453 static constexpr bool is_range = is_range_v<q20::remove_cvref_t<T>>
455 // A static size of -1 indicates dynamically sized range
456 // A static size of 0 indicates that the specified type doesn't
457 // represent static or dynamic range.
458 static constexpr int static_size = is_range ? -1 : 0;
460 static constexpr int fixed_size() { return 1; }
461 static constexpr bool hasMetaObject = false;
462
464 {
465 return {};
466 }
467
468 template <typename C, typename Fn>
469 static bool for_element_at(C &&container, std::size_t idx, Fn &&fn)
470 {
471 if constexpr (is_range)
472 return std::forward<Fn>(fn)(*QRangeModelDetails::pos(std::forward<C>(container), idx));
473 else
474 return std::forward<Fn>(fn)(std::forward<C>(container));
475 }
476
477 template <typename Fn>
478 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
479 {
480 if constexpr (static_size == 0) {
481 return std::forward<Fn>(fn)(firstIndex, QRangeModelDetails::pointerTo(row));
482 } else {
483 int columnIndex = -1;
484 return std::all_of(QRangeModelDetails::adl_begin(row),
485 QRangeModelDetails::adl_end(row), [&](const auto &item) {
486 return std::forward<Fn>(fn)(firstIndex.siblingAtColumn(++columnIndex),
487 QRangeModelDetails::pointerTo(item));
488 });
489 }
490 }
491 };
492
493 // Specialization for tuple-like semantics (prioritized over metaobject)
494 template <typename T>
496 {
497 static constexpr std::size_t size64 = std::tuple_size_v<T>;
498 static_assert(q20::in_range<int>(size64));
499 static constexpr int static_size = int(size64);
500
501 // are the types in a tuple all the same
502 template <std::size_t ...I>
503 static constexpr bool allSameTypes(std::index_sequence<I...>)
504 {
505 return (std::is_same_v<std::tuple_element_t<0, T>,
506 std::tuple_element_t<I, T>> && ...);
507 }
508
510 std::tuple_element_t<0, T>, void>;
511 static constexpr int fixed_size() { return 0; }
512 static constexpr bool hasMetaObject = false;
513
514 template <typename C, typename F>
516 {
518 constexpr size_t size = std::tuple_size_v<type>;
519 Q_ASSERT(idx < size);
520 return QtPrivate::applyIndexSwitch<size>(idx, [&](auto idxConstant) {
522 });
523 }
524
526 {
527 constexpr auto size = std::tuple_size_v<T>;
529
535 >();
536 if (metaType.isValid())
538 });
539 return result;
540 }
541
542 template <typename Fn, std::size_t ...Is>
543 static bool forEachTupleElement(const T &row, Fn &&fn, std::index_sequence<Is...>)
544 {
545 using std::get;
546 return (std::forward<Fn>(fn)(QRangeModelDetails::pointerTo(get<Is>(row))) && ...);
547 }
548
549 template <typename Fn>
550 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
551 {
552 int column = -1;
553 return forEachTupleElement(row, [&column, &fn, &firstIndex](const QObject *item){
556 }
557 };
558
559 // Specialization for C arrays and std::array
560 template <typename T, std::size_t N>
561 struct row_traits<std::array<T, N>>
562 {
563 static_assert(q20::in_range<int>(N));
564 static constexpr int static_size = int(N);
565 using item_type = T;
566 static constexpr int fixed_size() { return 0; }
567 static constexpr bool hasMetaObject = false;
568
569 template <typename C, typename F>
575
577 {
578 return section;
579 }
580
581 template <typename Fn>
582 static bool for_each_element(const std::array<T, N> &row, const QModelIndex &firstIndex, Fn &&fn)
583 {
584 int columnIndex = -1;
586 QRangeModelDetails::adl_end(row), [&](const auto &item) {
589 });
590 }
591 };
592
593 template <typename T, std::size_t N>
594 struct row_traits<T[N]> : row_traits<std::array<T, N>> {};
595
596 // prioritize tuple-like over metaobject
597 template <typename T>
599 {
600 static constexpr int static_size = 0;
602 static int fixed_size() {
603 if constexpr (row_category<T>::isMultiRole) {
604 return 1;
605 } else {
606 // Interpret a gadget in a list as a multi-column row item. To make
607 // a list of multi-role items, wrap it into SingleColumn.
608 static const int columnCount = []{
610 return mo.propertyCount() - mo.propertyOffset();
611 }();
612 return columnCount;
613 }
614 }
615
616 static constexpr bool hasMetaObject = true;
617
618 template <typename C, typename F>
620 {
621 return std::forward<F>(function)(std::forward<C>(container));
622 }
623
625 {
627 if (fixed_size() == 1) {
630 } else if (section <= fixed_size()) {
634 }
635 return result;
636 }
637
638 template <typename Fn>
639 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
640 {
642 }
643 };
644
645 template <typename T, typename = void>
647 {
648 template <typename That>
649 static QHash<int, QByteArray> roleNames(That *)
650 {
651 return That::roleNamesForSimpleType();
652 }
653 };
654
655 template <>
656 struct item_traits<void>
657 {
658 template <typename That>
660 {
662 }
663 };
664
665 template <typename T>
670
671 template <typename T>
673 {
674 template <typename That>
679 };
680
681 template <typename T>
682 [[maybe_unused]] static constexpr int static_size_v =
684
685 template <typename Range>
687 {
689
690 template <typename R = row_type>
691 auto newRow() -> decltype(R{}) { return R{}; }
692 };
693
694 template <typename Range>
696 {
698
699 template <typename R = row_type,
704 >,
705 bool> = true>
706 auto newRow() -> decltype(R(new QRangeModelDetails::wrapped_t<R>)) {
707 if constexpr (is_any_of<R, std::shared_ptr>())
709 else
710 return R(new QRangeModelDetails::wrapped_t<R>);
711 }
712
713 template <typename R = row_type,
715 auto newRow() -> decltype(R{}) { return R{}; }
716
717 template <typename R = row_type,
719 auto deleteRow(R&& row) -> decltype(delete row) { delete row; }
720 };
721
722 template <typename Range,
726
727 // Default tree traversal protocol implementation for row types that have
728 // the respective member functions. The trailing return type implicitly
729 // removes those functions that are not available.
730 template <typename Range>
732 {
733 template <typename R /*wrapped_row_type*/>
734 auto parentRow(const R& row) const -> decltype(row.parentRow())
735 {
736 return row.parentRow();
737 }
738
739 template <typename R /* = wrapped_row_type*/>
740 auto setParentRow(R &row, R* parent) -> decltype(row.setParentRow(parent))
741 {
742 row.setParentRow(parent);
743 }
744
745 template <typename R /* = wrapped_row_type*/>
746 auto childRows(const R &row) const -> decltype(row.childRows())
747 {
748 return row.childRows();
749 }
750
751 template <typename R /* = wrapped_row_type*/>
752 auto childRows(R &row) -> decltype(row.childRows())
753 {
754 return row.childRows();
755 }
756 };
757
758 template <typename P, typename R>
759 using protocol_parentRow_test = decltype(std::declval<P&>()
761 template <typename P, typename R>
763
764 template <typename P, typename R>
765 using protocol_childRows_test = decltype(std::declval<P&>()
767 template <typename P, typename R>
769
770 template <typename P, typename R>
774 template <typename P, typename R>
776
777 template <typename P, typename R>
780 template <typename P, typename R>
782
783 template <typename P, typename = void>
785 template <typename P>
786 struct protocol_newRow<P, std::void_t<decltype(std::declval<P&>().newRow())>>
787 : std::true_type {};
788
789 template <typename P, typename R, typename = void>
791 template <typename P, typename R>
793 std::void_t<decltype(std::declval<P&>().deleteRow(std::declval<R&&>()))>>
794 : std::true_type {};
795
796 template <typename Range,
797 typename Protocol = DefaultTreeProtocol<Range>,
798 typename R = typename range_traits<Range>::value_type,
799 typename = void>
801
802 template <typename Range, typename Protocol, typename R>
807
808 template <typename Range>
812 >, bool>;
813
814 template <typename Range, typename Protocol = DefaultTreeProtocol<Range>>
819 >, bool>;
820
821 template <typename Range, typename Protocol>
839
856
857 template <bool cacheProperties, bool itemsAreQObjects>
859 static constexpr bool cachesProperties = false;
860
862 };
863
865 {
866 static constexpr bool cachesProperties = true;
868
870 {
871 properties.clear();
872 }
873 protected:
874 ~PropertyCache() = default;
875 };
876
877 template <>
878 struct PropertyData<true, false> : PropertyCache
879 {};
880
882 {
883 struct Connection {
885 int role;
886
887 friend bool operator==(const Connection &lhs, const Connection &rhs) noexcept
888 {
889 return lhs.sender == rhs.sender && lhs.role == rhs.role;
890 }
891 friend size_t qHash(const Connection &c, size_t seed) noexcept
892 {
893 return qHashMulti(seed, c.sender, c.role);
894 }
895 };
896
899
900 protected:
901 ~ConnectionStorage() = default;
902 };
903
904 template <>
906 {};
907
908 template <>
909 struct PropertyData<false, true> : PropertyData<false, false>, ConnectionStorage
910 {
912 };
913
914 // The storage of the model data. We might store it as a pointer, or as a
915 // (copied- or moved-into) value (or smart pointer). But we always return a
916 // raw pointer.
917 template <typename ModelStorage, typename = void>
925
926 template <typename ModelStorage>
935
936 template <typename ModelStorage, typename PropertyStorage>
938 PropertyStorage
939 {
943
944 auto model() { return QRangeModelDetails::pointerTo(this->m_model); }
945 auto model() const { return QRangeModelDetails::pointerTo(this->m_model); }
946
947 template <typename Model = ModelStorage>
948 ModelData(Model &&model)
950 {}
951 };
952} // namespace QRangeModelDetails
953
954class QRangeModel;
955// forward declare so that we can declare friends
956template <typename, typename, typename> class QRangeModelAdapter;
957
959{
960 using Self = QRangeModelImplBase;
962
963public:
964 // keep in sync with QRangeModel::AutoConnectPolicy
970
971 // overridable prototypes (quasi-pure-virtual methods)
973 bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
974 bool setData(const QModelIndex &index, const QVariant &data, int role);
975 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data);
976 bool clearItemData(const QModelIndex &index);
977 bool insertColumns(int column, int count, const QModelIndex &parent);
978 bool removeColumns(int column, int count, const QModelIndex &parent);
979 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn);
980 bool insertRows(int row, int count, const QModelIndex &parent);
981 bool removeRows(int row, int count, const QModelIndex &parent);
982 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow);
983
984 QModelIndex index(int row, int column, const QModelIndex &parent) const;
985 QModelIndex sibling(int row, int column, const QModelIndex &index) const;
986 int rowCount(const QModelIndex &parent) const;
987 int columnCount(const QModelIndex &parent) const;
988 Qt::ItemFlags flags(const QModelIndex &index) const;
989 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
990 QVariant data(const QModelIndex &index, int role) const;
991 QMap<int, QVariant> itemData(const QModelIndex &index) const;
992 inline QHash<int, QByteArray> roleNames() const;
993 QModelIndex parent(const QModelIndex &child) const;
994
995 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
997
998 void interfaceVersion(int &version) const;
999 void sort(int column, Qt::SortOrder order);
1000 QModelIndexList match(const QModelIndex &start, int role, const QVariant &value,
1001 int hits, Qt::MatchFlags flags) const;
1002
1003 // bindings for overriding
1004
1016
1023 using Data = Method<&Self::data>;
1027
1028 // 6.11
1031
1032 // 6.12
1034 using Sort = Method<&Self::sort>;
1036
1037 template <typename C>
1038 using MethodTemplates = std::tuple<
1039 typename C::Destroy,
1040 typename C::InvalidateCaches,
1041 typename C::SetHeaderData,
1042 typename C::SetData,
1043 typename C::SetItemData,
1044 typename C::ClearItemData,
1045 typename C::InsertColumns,
1046 typename C::RemoveColumns,
1047 typename C::MoveColumns,
1048 typename C::InsertRows,
1049 typename C::RemoveRows,
1050 typename C::MoveRows,
1051 typename C::Index,
1052 typename C::Parent,
1053 typename C::Sibling,
1054 typename C::RowCount,
1055 typename C::ColumnCount,
1056 typename C::Flags,
1057 typename C::HeaderData,
1058 typename C::Data,
1059 typename C::ItemData,
1060 typename C::RoleNames,
1061 typename C::MultiData,
1062 typename C::SetAutoConnectPolicy,
1063 typename C::InterfaceVersion,
1064 typename C::Sort,
1065 typename C::Match
1066 >;
1067
1068 static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
1069 static Q_CORE_EXPORT const QRangeModelImplBase *getImplementation(const QRangeModel *model);
1070
1071private:
1072 friend class QRangeModelPrivate;
1074
1075 QRangeModel *m_rangeModel;
1076
1077protected:
1078 explicit QRangeModelImplBase(QRangeModel *itemModel)
1079 : m_rangeModel(itemModel)
1080 {}
1081
1082 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
1083 inline QModelIndexList persistentIndexList() const;
1084 inline void changePersistentIndex(const QModelIndex &from, const QModelIndex &to);
1085 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
1086 const QList<int> &roles);
1087 inline void beginResetModel();
1088 inline void endResetModel();
1089 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
1090 inline void endInsertColumns();
1091 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
1092 inline void endRemoveColumns();
1093 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1094 const QModelIndex &destParent, int destRow);
1095 inline void endMoveColumns();
1096 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
1097 inline void endInsertRows();
1098 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
1099 inline void endRemoveRows();
1100 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1101 const QModelIndex &destParent, int destRow);
1102 inline void endMoveRows();
1103 inline void beginLayoutChange();
1104 inline void endLayoutChange();
1105 inline AutoConnectPolicy autoConnectPolicy() const;
1106 inline static Qt::partial_ordering compareData(const QVariant &lhs, const QVariant &rhs,
1107 const QCollator *collator);
1108
1109public:
1110 inline QAbstractItemModel &itemModel();
1111 inline const QAbstractItemModel &itemModel() const;
1112
1113 // implemented in qrangemodel.cpp
1115 const QMetaObject &metaObject);
1117
1118protected:
1119 Q_CORE_EXPORT QScopedValueRollback<bool> blockDataChangedDispatch();
1120
1122 const QMetaObject &metaObject);
1124 Q_CORE_EXPORT static bool connectProperty(const QModelIndex &index, const QObject *item,
1125 QRangeModelDetails::AutoConnectContext *context,
1126 int role, const QMetaProperty &property);
1127 Q_CORE_EXPORT static bool connectPropertyConst(const QModelIndex &index, const QObject *item,
1128 QRangeModelDetails::AutoConnectContext *context,
1129 int role, const QMetaProperty &property);
1130 Q_CORE_EXPORT static bool connectProperties(const QModelIndex &index, const QObject *item,
1131 QRangeModelDetails::AutoConnectContext *context,
1132 const QHash<int, QMetaProperty> &properties);
1133 Q_CORE_EXPORT static bool connectPropertiesConst(const QModelIndex &index, const QObject *item,
1134 QRangeModelDetails::AutoConnectContext *context,
1135 const QHash<int, QMetaProperty> &properties);
1136 Q_CORE_EXPORT int sortRole() const;
1137 Q_CORE_EXPORT const QCollator *sortCollator() const;
1138
1139 Q_CORE_EXPORT static QVariant convertMatchValue(const QVariant &value, Qt::MatchFlags flags);
1140 Q_CORE_EXPORT static bool matchValue(const QString &itemData, const QVariant &value,
1141 Qt::MatchFlags flags);
1142 static bool matchValue(const QVariant &itemData, const QVariant &value, Qt::MatchFlags flags)
1143 {
1144 if ((flags & Qt::MatchTypeMask) == Qt::MatchExactly)
1145 return itemData == value;
1146 return matchValue(itemData.toString(), value, flags);
1147 }
1148};
1149
1150template <typename Structure, typename Range,
1151 typename Protocol = QRangeModelDetails::table_protocol_t<Range>>
1156{
1157public:
1167
1169 typename row_traits::item_type>>;
1171 && row_traits::hasMetaObject; // not treated as tuple
1172
1176 >,
1179 >
1180 >;
1182
1183 using const_row_reference = decltype(*std::declval<typename ModelData::const_iterator&>());
1184
1185 static_assert(!QRangeModelDetails::is_any_of<range_type, std::optional>() &&
1187 "Currently, std::optional is not supported for ranges and rows, as "
1188 "it has range semantics in c++26. Once the required behavior is clarified, "
1189 "std::optional for ranges and rows will be supported.");
1190
1191protected:
1192
1193 using Self = QRangeModelImpl<Structure, Range, Protocol>;
1195
1196 Structure& that() { return static_cast<Structure &>(*this); }
1197 const Structure& that() const { return static_cast<const Structure &>(*this); }
1198
1199 template <typename C>
1200 static constexpr int size(const C &c)
1201 {
1202 if (!QRangeModelDetails::isValid(c))
1203 return 0;
1204
1205 if constexpr (QRangeModelDetails::test_size<C>()) {
1206 using std::size;
1207 return int(size(c));
1208 } else {
1209#if defined(__cpp_lib_ranges)
1210 using std::ranges::distance;
1211#else
1212 using std::distance;
1213#endif
1214 using container_type = std::conditional_t<QRangeModelDetails::range_traits<C>::has_cbegin,
1215 const QRangeModelDetails::wrapped_t<C>,
1216 QRangeModelDetails::wrapped_t<C>>;
1217 container_type& container = const_cast<container_type &>(QRangeModelDetails::refTo(c));
1218 return int(distance(QRangeModelDetails::adl_begin(container),
1219 QRangeModelDetails::adl_end(container)));
1220 }
1221 }
1222
1225 static constexpr bool rows_are_owning_or_raw_pointers =
1228 static constexpr bool one_dimensional_range = static_column_count == 0;
1229
1231 {
1232 if constexpr (itemsAreQObjects || rowsAreQObjects)
1233 return this->blockDataChangedDispatch();
1234 else
1235 return false;
1236 }
1237
1238 // A row might be a value (or range of values), or a pointer.
1239 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
1242
1243 template <typename T>
1246
1247 // A iterator type to use as the input iterator with the
1248 // range_type::insert(pos, start, end) overload if available (it is in
1249 // std::vector, but not in QList). Generates a prvalue when dereferenced,
1250 // which then gets moved into the newly constructed row, which allows us to
1251 // implement insertRows() for move-only row types.
1253 {
1257 using iterator_category = std::input_iterator_tag;
1258 using difference_type = int;
1259
1260 value_type operator*() { return impl->makeEmptyRow(parentRow); }
1261 EmptyRowGenerator &operator++() { ++n; return *this; }
1262 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1263 { return lhs.n == rhs.n; }
1264 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1265 { return !(lhs == rhs); }
1266
1268 Structure *impl = nullptr;
1269 const row_ptr parentRow = nullptr;
1270 };
1271
1272 // If we have a move-only row_type and can add/remove rows, then the range
1273 // must have an insert-from-range overload.
1276 "The range holding a move-only row-type must support insert(pos, start, end)");
1277
1279
1280public:
1281 static constexpr bool isMutable()
1282 {
1283 return range_features::is_mutable && row_features::is_mutable
1284 && std::is_reference_v<row_reference>
1285 && Structure::is_mutable_impl;
1286 }
1287 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
1288 static constexpr bool dynamicColumns() { return static_column_count < 0; }
1289
1290 explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
1294 {
1295 }
1296
1297
1298 // static interface, called by QRangeModelImplBase
1299
1300 void interfaceVersion(int &versionNumber) const
1301 {
1302 versionNumber = QT_VERSION;
1303 }
1304
1305 void invalidateCaches() { m_data.invalidateCaches(); }
1306
1307 // Not implemented
1308 bool setHeaderData(int , Qt::Orientation , const QVariant &, int ) { return false; }
1309
1310 // actual implementations
1311 QModelIndex index(int row, int column, const QModelIndex &parent) const
1312 {
1313 if (row < 0 || column < 0 || column >= columnCount(parent)
1314 || row >= rowCount(parent)) {
1315 return {};
1316 }
1317
1318 return that().indexImpl(row, column, parent);
1319 }
1320
1321 QModelIndex sibling(int row, int column, const QModelIndex &index) const
1322 {
1323 if (row == index.row() && column == index.column())
1324 return index;
1325
1326 if (column < 0 || column >= this->columnCount({}))
1327 return {};
1328
1329 if (row == index.row())
1330 return this->createIndex(row, column, index.constInternalPointer());
1331
1332 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1333 const auto siblingCount = size(that().childrenOf(parentRow));
1334 if (row < 0 || row >= int(siblingCount))
1335 return {};
1336 return this->createIndex(row, column, parentRow);
1337 }
1338
1339 Qt::ItemFlags flags(const QModelIndex &index) const
1340 {
1341 if (!index.isValid())
1342 return Qt::NoItemFlags;
1343
1344 // try customization
1345 std::optional<Qt::ItemFlags> customFlags;
1346 if constexpr (QRangeModelDetails::hasRowFlags<wrapped_row_type>) {
1347 const_row_reference row = rowData(index);
1348 customFlags = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::flags(row);
1349 }
1350
1351 readAt(index, [&customFlags](auto &&ref){
1352 Q_UNUSED(ref);
1353 using wrapped_value_type = q20::remove_cvref_t<QRangeModelDetails::wrapped_t<decltype(ref)>>;
1354 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasFlags) {
1355 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1356 customFlags = ItemAccess::flags(ref);
1357 return true;
1358 }
1359 return false;
1360 });
1361
1362 Qt::ItemFlags f = customFlags ? *customFlags : Structure::defaultFlags();
1363 // adjust custom flags based on what is not possible
1364 if constexpr (!isMutable())
1365 f &= ~Qt::ItemIsEditable;
1366 if (index.column())
1367 f |= Qt::ItemNeverHasChildren;
1368 if (customFlags)
1369 return f;
1370
1371 // compute flags ourselves
1372 if constexpr (isMutable()) {
1373 if constexpr (row_traits::hasMetaObject) {
1374 if (index.column() < row_traits::fixed_size()) {
1375 const QMetaObject mo = wrapped_row_type::staticMetaObject;
1376 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
1377 if (prop.isWritable())
1378 f |= Qt::ItemIsEditable;
1379 }
1380 } else if constexpr (static_column_count <= 0) {
1381 f |= Qt::ItemIsEditable;
1382 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
1383 // we want to know if the elements in the tuple are const; they'd always be, if
1384 // we didn't remove the const of the range first.
1385 const_row_reference row = rowData(index);
1386 row_reference mutableRow = const_cast<row_reference>(row);
1387 if (QRangeModelDetails::isValid(mutableRow)) {
1388 row_traits::for_element_at(mutableRow, index.column(), [&f](auto &&ref){
1389 using target_type = decltype(ref);
1390 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
1391 f &= ~Qt::ItemIsEditable;
1392 else if constexpr (std::is_lvalue_reference_v<target_type>)
1393 f |= Qt::ItemIsEditable;
1394 });
1395 } else {
1396 // If there's no usable value stored in the row, then we can't
1397 // do anything with this item.
1398 f &= ~Qt::ItemIsEditable;
1399 }
1400 }
1401 }
1402 return f;
1403 }
1404
1405 QVariant headerData(int section, Qt::Orientation orientation, int role) const
1406 {
1407 QVariant result;
1408 if constexpr (QRangeModelDetails::hasHeaderData<wrapped_row_type>) {
1409 if (orientation == Qt::Horizontal) {
1410 result = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::headerData(
1411 section, role
1412 );
1413 if (result.isValid())
1414 return result;
1415 }
1416 }
1417
1418 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
1419 || section < 0 || section >= columnCount({})) {
1420 return this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1421 }
1422
1423 result = row_traits::column_name(section);
1424 if (!result.isValid())
1425 result = this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1426 return result;
1427 }
1428
1429 QVariant data(const QModelIndex &index, int role) const
1430 {
1431 if (!index.isValid())
1432 return {};
1433
1434 QModelRoleData result(role);
1435 multiData(index, result);
1436 return std::move(result.data());
1437 }
1438
1439 static constexpr bool isRangeModelRole(int role)
1440 {
1441 return role == Qt::RangeModelDataRole
1442 || role == Qt::RangeModelAdapterRole;
1443 }
1444
1445 static constexpr bool isPrimaryRole(int role)
1446 {
1447 return role == Qt::DisplayRole || role == Qt::EditRole;
1448 }
1449
1450 QMap<int, QVariant> itemData(const QModelIndex &index) const
1451 {
1452 QMap<int, QVariant> result;
1453
1454 if (index.isValid()) {
1455 // optimisation for items backed by a QMap<int, QVariant> or equivalent
1456 if (!readAt(index, [&result](const auto &value) {
1457 if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) {
1458 result = value;
1459 return true;
1460 }
1461 return false;
1462 })) {
1463 const auto roles = this->itemModel().roleNames().keys();
1464 QVarLengthArray<QModelRoleData, 16> roleDataArray;
1465 roleDataArray.reserve(roles.size());
1466 for (auto role : roles) {
1467 if (isRangeModelRole(role))
1468 continue;
1469 roleDataArray.emplace_back(role);
1470 }
1471 QModelRoleDataSpan roleDataSpan(roleDataArray);
1472 multiData(index, roleDataSpan);
1473
1474 for (QModelRoleData &roleData : roleDataSpan) {
1475 if (roleData.data().isValid())
1476 result[roleData.role()] = std::move(roleData.data());
1477 }
1478 }
1479 }
1480 return result;
1481 }
1482
1484 {
1485 template <typename value_type>
1486 bool operator()(const value_type &value) const
1487 {
1488 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1489 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1490
1491 const auto readModelData = [&value](QModelRoleData &roleData){
1492 const int role = roleData.role();
1493 if (role == Qt::RangeModelDataRole) {
1494 // Qt QML support: "modelData" role returns the entire multi-role item.
1495 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1496 // only by value (so we take the reference).
1497 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1498 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1499 else
1500 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1501 } else if (role == Qt::RangeModelAdapterRole) {
1502 // for QRangeModelAdapter however, we want to respect smart pointer wrappers
1503 if constexpr (std::is_copy_assignable_v<value_type>)
1504 roleData.setData(QVariant::fromValue(value));
1505 else
1506 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1507 } else {
1508 return false;
1509 }
1510 return true;
1511 };
1512
1513 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasReadRole) {
1514 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1515 for (auto &roleData : roleDataSpan) {
1516 if (!readModelData(roleData)) {
1517 roleData.setData(ItemAccess::readRole(QRangeModelDetails::refTo(value),
1518 roleData.role()));
1519 }
1520 }
1521 } else if constexpr (multi_role()) {
1522 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1523 Q_UNUSED(this);
1524 if constexpr (!multi_role::int_key)
1525 return that->itemModel().roleNames();
1526 else
1527 return {};
1528 }();
1529 using key_type = typename value_type::key_type;
1530 for (auto &roleData : roleDataSpan) {
1531 const auto &it = [&roleNames, &value, role = roleData.role()]{
1532 Q_UNUSED(roleNames);
1533 if constexpr (multi_role::int_key)
1534 return value.find(key_type(role));
1535 else
1536 return value.find(roleNames.value(role));
1537 }();
1538 if (it != QRangeModelDetails::adl_end(value))
1539 roleData.setData(QRangeModelDetails::value(it));
1540 else
1541 roleData.clearData();
1542 }
1543 } else if constexpr (has_metaobject<value_type>) {
1544 if (row_traits::fixed_size() <= 1) {
1545 for (auto &roleData : roleDataSpan) {
1546 if (!readModelData(roleData)) {
1547 roleData.setData(that->readRole(index, roleData.role(),
1548 QRangeModelDetails::pointerTo(value)));
1549 }
1550 }
1551 } else if (index.column() <= row_traits::fixed_size()) {
1552 for (auto &roleData : roleDataSpan) {
1553 const int role = roleData.role();
1554 if (isPrimaryRole(role)) {
1555 roleData.setData(that->readProperty(index,
1556 QRangeModelDetails::pointerTo(value)));
1557 } else {
1558 roleData.clearData();
1559 }
1560 }
1561 }
1562 } else {
1563 for (auto &roleData : roleDataSpan) {
1564 const int role = roleData.role();
1565 if (isPrimaryRole(role) || isRangeModelRole(role))
1566 roleData.setData(read(value));
1567 else
1568 roleData.clearData();
1569 }
1570 }
1571 return true;
1572 }
1573
1576 const QRangeModelImpl * const that;
1577 };
1578
1579 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1580 {
1581 if (!readAt(index, ItemReader{index, roleDataSpan, this})) {
1582 for (auto &roleData : roleDataSpan)
1583 roleData.clearData();
1584 }
1585 }
1586
1587 bool setData(const QModelIndex &index, const QVariant &data, int role)
1588 {
1589 if (!index.isValid())
1590 return false;
1591
1592 if constexpr (isMutable()) {
1593 auto emitDataChanged = qScopeGuard([this, &index, role]{
1594 Q_EMIT this->dataChanged(index, index,
1595 role == Qt::EditRole || role == Qt::RangeModelDataRole
1596 || role == Qt::RangeModelAdapterRole
1597 ? QList<int>{} : QList<int>{role});
1598 });
1599 // we emit dataChanged at the end, block dispatches from auto-connected properties
1600 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1601
1602 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1603 using value_type = q20::remove_cvref_t<decltype(target)>;
1604 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1605 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1606
1607 auto setRangeModelDataRole = [&target, &data]{
1608 constexpr auto targetMetaType = QMetaType::fromType<value_type>();
1609 const auto dataMetaType = data.metaType();
1610 constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
1611 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
1612 // we don't support replacing objects that are stored as raw pointers,
1613 // as this makes object ownership very messy. But we can replace objects
1614 // stored in smart pointers, and we can initialize raw nullptr objects.
1615 if constexpr (isWrapped) {
1616 constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
1617 if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
1618 if (data.canConvert(targetMetaType)) {
1619 target = data.value<value_type>();
1620 return true;
1621 }
1622 } else if constexpr (is_raw_pointer) {
1623 if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
1624 target = data.value<value_type>();
1625 return true;
1626 }
1627 } else {
1628 Q_UNUSED(target);
1629 }
1630 }
1631 // Otherwise we have a move-only or polymorph type. fall through to
1632 // error handling.
1633 } else if constexpr (isWrapped) {
1634 if (QRangeModelDetails::isValid(target)) {
1635 auto &targetRef = QRangeModelDetails::refTo(target);
1636 // we need to get a wrapped value type out of the QVariant, which
1637 // might carry a pointer. We have to try all alternatives.
1638 if (const auto mt = QMetaType::fromType<wrapped_value_type>();
1639 data.canConvert(mt)) {
1640 targetRef = data.value<wrapped_value_type>();
1641 return true;
1642 } else if (const auto mtp = QMetaType::fromType<wrapped_value_type *>();
1643 data.canConvert(mtp)) {
1644 targetRef = *data.value<wrapped_value_type *>();
1645 return true;
1646 }
1647 }
1648 } else if (targetMetaType == dataMetaType) {
1649 QRangeModelDetails::refTo(target) = data.value<value_type>();
1650 return true;
1651 } else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1652 QRangeModelDetails::refTo(target) = *data.value<value_type *>();
1653 return true;
1654 }
1655#ifndef QT_NO_DEBUG
1656 qCritical("Not able to assign %s to %s",
1657 qPrintable(QDebug::toString(data)), targetMetaType.name());
1658#endif
1659 return false;
1660 };
1661
1662 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasWriteRole) {
1663 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1664 if (isRangeModelRole(role))
1665 return setRangeModelDataRole();
1666 return ItemAccess::writeRole(QRangeModelDetails::refTo(target), data, role);
1667 } else if constexpr (has_metaobject<value_type>) {
1668 if (row_traits::fixed_size() <= 1) { // multi-role value
1669 if (isRangeModelRole(role))
1670 return setRangeModelDataRole();
1671 return writeRole(role, QRangeModelDetails::pointerTo(target), data);
1672 } else if (column <= row_traits::fixed_size() // multi-column
1673 && (isPrimaryRole(role) || isRangeModelRole(role))) {
1674 return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
1675 }
1676 } else if constexpr (multi_role::value) {
1677 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1678 // If there is an entry for EditRole, overwrite that; otherwise,
1679 // set the entry for DisplayRole.
1680 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1681 Q_UNUSED(this);
1682 if constexpr (!multi_role::int_key)
1683 return this->itemModel().roleNames();
1684 else
1685 return {};
1686 }();
1687 if (role == Qt::EditRole) {
1688 if constexpr (multi_role::int_key) {
1689 if (target.find(roleToSet) == target.end())
1690 roleToSet = Qt::DisplayRole;
1691 } else {
1692 if (target.find(roleNames.value(roleToSet)) == target.end())
1693 roleToSet = Qt::DisplayRole;
1694 }
1695 }
1696 if constexpr (multi_role::int_key)
1697 return write(target[roleToSet], data);
1698 else
1699 return write(target[roleNames.value(roleToSet)], data);
1700 } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
1701 return write(target, data);
1702 }
1703 return false;
1704 };
1705
1706 if (!writeAt(index, writeData)) {
1707 emitDataChanged.dismiss();
1708 return false;
1709 } else if constexpr (itemsAreQObjects || rowsAreQObjects) {
1710 if (isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
1711 if (QObject *item = data.value<QObject *>())
1712 Self::connectProperties(index, item, m_data.context, m_data.properties);
1713 }
1714 }
1715 return true;
1716 }
1717 return false;
1718 }
1719
1720 template <typename LHS, typename RHS>
1721 void updateTarget(LHS &org, RHS &&copy) noexcept
1722 {
1723 if constexpr (std::is_pointer_v<RHS>)
1724 return;
1725 else if constexpr (std::is_assignable_v<LHS, RHS>)
1726 org = std::forward<RHS>(copy);
1727 else
1728 qSwap(org, copy);
1729 }
1730 template <typename LHS, typename RHS>
1731 void updateTarget(LHS *org, RHS &&copy) noexcept
1732 {
1733 updateTarget(*org, std::forward<RHS>(copy));
1734 }
1735
1736 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1737 {
1738 if (!index.isValid() || data.isEmpty())
1739 return false;
1740
1741 if constexpr (isMutable()) {
1742 auto emitDataChanged = qScopeGuard([this, &index, &data]{
1743 Q_EMIT this->dataChanged(index, index, data.keys());
1744 });
1745 // we emit dataChanged at the end, block dispatches from auto-connected properties
1746 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1747
1748 bool tried = false;
1749 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
1750 Q_UNUSED(this);
1751 using value_type = q20::remove_cvref_t<decltype(target)>;
1752 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1753 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1754
1755 // transactional: if possible, modify a copy and only
1756 // update target if all values from data could be stored.
1757 auto makeCopy = [](const value_type &original){
1758 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>)
1759 return QRangeModelDetails::pointerTo(original); // no transaction support
1760 else if constexpr (std::is_pointer_v<decltype(original)>)
1761 return *original;
1762 else if constexpr (std::is_copy_assignable_v<value_type>)
1763 return original;
1764 else
1765 return QRangeModelDetails::pointerTo(original);
1766 };
1767
1768 const auto roleNames = this->itemModel().roleNames();
1769
1770 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasWriteRole) {
1771 tried = true;
1772 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1773 const auto roles = roleNames.keys();
1774 auto targetCopy = makeCopy(target);
1775 for (int role : roles) {
1776 if (!ItemAccess::writeRole(QRangeModelDetails::refTo(targetCopy),
1777 data.value(role), role)) {
1778 return false;
1779 }
1780 }
1781 updateTarget(target, std::move(targetCopy));
1782 return true;
1783 } else if constexpr (multi_role()) {
1784 using key_type = typename value_type::key_type;
1785 tried = true;
1786 const auto roleName = [&roleNames](int role) {
1787 return roleNames.value(role);
1788 };
1789
1790 // transactional: only update target if all values from data
1791 // can be stored. Storing never fails with int-keys.
1792 if constexpr (!multi_role::int_key)
1793 {
1794 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
1795 [&roleName](int role) { return roleName(role).isEmpty(); }
1796 );
1797
1798 if (invalid != data.keyEnd()) {
1799#ifndef QT_NO_DEBUG
1800 qWarning("No role name set for %d", *invalid);
1801#endif
1802 return false;
1803 }
1804 }
1805
1806 for (auto &&[role, value] : data.asKeyValueRange()) {
1807 if constexpr (multi_role::int_key)
1808 target[static_cast<key_type>(role)] = value;
1809 else
1810 target[QString::fromUtf8(roleName(role))] = value;
1811 }
1812 return true;
1813 } else if constexpr (has_metaobject<value_type>) {
1814 if (row_traits::fixed_size() <= 1) {
1815 tried = true;
1816 auto targetCopy = makeCopy(target);
1817 for (auto &&[role, value] : data.asKeyValueRange()) {
1818 if (isRangeModelRole(role))
1819 continue;
1820 if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
1821 const QByteArray roleName = roleNames.value(role);
1822#ifndef QT_NO_DEBUG
1823 qWarning("Failed to write value '%s' to role '%s'",
1824 qPrintable(QDebug::toString(value)), roleName.data());
1825#endif
1826 return false;
1827 }
1828 }
1829 updateTarget(target, std::move(targetCopy));
1830 return true;
1831 }
1832 }
1833 return false;
1834 };
1835
1836 if (!writeAt(index, writeItemData)) {
1837 emitDataChanged.dismiss();
1838 if (!tried)
1839 return this->itemModel().QAbstractItemModel::setItemData(index, data);
1840 }
1841 return true;
1842 }
1843 return false;
1844 }
1845
1846 bool clearItemData(const QModelIndex &index)
1847 {
1848 if (!index.isValid())
1849 return false;
1850
1851 if constexpr (isMutable()) {
1852 auto emitDataChanged = qScopeGuard([this, &index]{
1853 Q_EMIT this->dataChanged(index, index, {});
1854 });
1855
1856 auto clearData = [column = index.column()](auto &&target) {
1857 if constexpr (row_traits::hasMetaObject) {
1858 if (row_traits::fixed_size() <= 1) {
1859 // multi-role object/gadget: reset all properties
1860 return resetProperty(-1, QRangeModelDetails::pointerTo(target));
1861 } else if (column <= row_traits::fixed_size()) {
1862 return resetProperty(column, QRangeModelDetails::pointerTo(target));
1863 }
1864 } else { // normal structs, values, associative containers
1865 target = {};
1866 return true;
1867 }
1868 return false;
1869 };
1870
1871 if (!writeAt(index, clearData)) {
1872 emitDataChanged.dismiss();
1873 return false;
1874 }
1875 return true;
1876 }
1877 return false;
1878 }
1879
1881 {
1882 // will be 'void' if columns don't all have the same type
1883 using item_type = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
1884 using item_traits = typename QRangeModelDetails::item_traits<item_type>;
1885 return item_traits::roleNames(this);
1886 }
1887
1888 bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
1889 {
1890 if (!QRangeModelDetails::isValid(row))
1891 return true; // nothing to do
1892 return row_traits::for_each_element(QRangeModelDetails::refTo(row),
1893 this->itemModel().index(rowIndex, 0, parent),
1894 [this](const QModelIndex &index, const QObject *item) {
1895 if constexpr (isMutable())
1896 return Self::connectProperties(index, item, m_data.context, m_data.properties);
1897 else
1898 return Self::connectPropertiesConst(index, item, m_data.context, m_data.properties);
1899 });
1900 }
1901
1902 void clearConnectionInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
1903 {
1904 if (!QRangeModelDetails::isValid(row))
1905 return;
1906 row_traits::for_each_element(QRangeModelDetails::refTo(row),
1907 this->itemModel().index(rowIndex, 0, parent),
1908 [this](const QModelIndex &, const QObject *item) {
1909 m_data.connections.removeIf([item](const auto &connection) {
1910 return connection.sender == item;
1911 });
1912 return true;
1913 });
1914 }
1915
1917 {
1918 if constexpr (itemsAreQObjects || rowsAreQObjects) {
1919 using item_type = std::remove_pointer_t<typename row_traits::item_type>;
1920 using Mapping = QRangeModelDetails::AutoConnectContext::AutoConnectMapping;
1921
1922 delete m_data.context;
1923 m_data.connections = {};
1924 switch (this->autoConnectPolicy()) {
1925 case AutoConnectPolicy::None:
1926 m_data.context = nullptr;
1927 break;
1928 case AutoConnectPolicy::Full:
1929 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
1930 if constexpr (itemsAreQObjects) {
1931 m_data.properties = QRangeModelImplBase::roleProperties(this->itemModel(),
1932 item_type::staticMetaObject);
1933 m_data.context->mapping = Mapping::Roles;
1934 } else {
1935 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
1936 m_data.context->mapping = Mapping::Columns;
1937 }
1938 if (!m_data.properties.isEmpty())
1939 that().autoConnectPropertiesImpl();
1940 break;
1941 case AutoConnectPolicy::OnRead:
1942 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
1943 if constexpr (itemsAreQObjects) {
1944 m_data.context->mapping = Mapping::Roles;
1945 } else {
1946 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
1947 m_data.context->mapping = Mapping::Columns;
1948 }
1949 break;
1950 }
1951 } else {
1952#ifndef QT_NO_DEBUG
1953 qWarning("All items in the range must be QObject subclasses");
1954#endif
1955 }
1956 }
1957
1958 struct unordered {
1959 friend constexpr bool operator<(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
1960 { return false; }
1961 friend constexpr bool operator>(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
1962 { return false; }
1963 };
1964
1965 struct Compare
1966 {
1967 template <typename C, typename LessThan>
1968 using sortMember_test = decltype(std::declval<C&>().sort(std::declval<LessThan &&>()));
1969 static constexpr bool hasSortMember = qxp::is_detected_v<sortMember_test, range_type, Compare>;
1970
1971 template <typename Stringish>
1972 using collatedCompare_test = decltype(
1973 std::declval<const QCollator&>().compare(std::declval<const Stringish&>(),
1974 std::declval<const Stringish&>())
1975 );
1976 template <typename Stringish>
1978 Stringish>;
1979
1980
1981 Compare(const QRangeModelImpl *impl, int column, Qt::SortOrder order)
1982 : that(impl), m_index(impl->createIndex(-1, column, nullptr))
1983 , collator(impl->sortCollator()), m_order(order), m_sortRole(impl->sortRole())
1984 {
1985 }
1986
1987 template <typename Item>
1988 auto operator()(const Item &lhs, const Item &rhs) const
1989 {
1990 auto ordering = compare(lhs, rhs);
1991 return m_order == Qt::AscendingOrder ? ordering < 0 : ordering > 0;
1992 }
1993
1994 template <typename Item>
1995 auto compare(const Item &lhs, const Item &rhs) const
1996 {
1997 using value_type = QRangeModelDetails::wrapped_t<Item>;
1998 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1999
2000 if constexpr (QRangeModelDetails::item_access<value_type>::hasReadRole
2001 || multi_role() || has_metaobject<value_type>) {
2002 QModelRoleData result(m_sortRole);
2003 // Minor abuse of QModelIndex: the reader needs an index to implement
2004 // lazy auto-connections, but we only have a column. So we construct
2005 // an invalid QModelIndex that carries only that column value. That's
2006 // enough for reading values, and the auto-connection logic skips for
2007 // invalid indexes.
2008 ItemReader reader{m_index, result, that};
2009 Q_ASSERT(!reader.index.isValid());
2010 reader(lhs);
2011 const QVariant lhsVariant = std::move(result.data());
2012 reader(rhs);
2013 const QVariant rhsVariant = std::move(result.data());
2014 return QRangeModelImplBase::compareData(lhsVariant, rhsVariant, collator);
2015 } else if constexpr (std::is_same_v<QVariant, value_type>) {
2016 return QRangeModelImplBase::compareData(lhs, rhs, collator);
2017 } else if constexpr (QtOrderingPrivate::CompareThreeWayTester::hasCompareThreeWay_v
2018 <value_type, value_type>) {
2019 // all types supported by QCollator are also three-way comparable
2020 if constexpr (hasCollatedCompare<value_type>) {
2021 if (collator) {
2022 using ordering = decltype(qCompareThreeWay(lhs, rhs));
2023 int res = collator->compare(lhs, rhs);
2024 if (res < 0)
2025 return ordering::less;
2026 if (res > 0)
2027 return ordering::greater;
2028 return ordering::equal;
2029 }
2030 }
2031 return qCompareThreeWay(lhs, rhs);
2032 } else {
2033 return unordered{};
2034 }
2035 }
2036
2037 bool checkComparable() const
2038 {
2039 return that->readAt(that->index(0, 0, {}), [this](const auto &item){
2040 // before we call std::stable_sort, check that we can compare the
2041 // types we'll ultimately get called with. This doesn't catch cases
2042 // where we end up comparing QVariant, and we cannot make this a
2043 // compile time check as long as readAt etc are not constexpr.
2044 using ordering = decltype(compare(item, item));
2045 if constexpr (std::is_same_v<ordering, unordered>) {
2046#ifndef QT_NO_DEBUG
2047 const QMetaType itemtype = QMetaType::fromType<QRangeModelDetails::wrapped_t<
2048 q20::remove_cvref_t<decltype(item)>>
2049 >();
2050 qCritical("QRangeModel: Cannot compare items of type %s in column %d!",
2051 itemtype.name(), m_index.column());
2052#endif
2053 return false;
2054 } else {
2055 return true;
2056 }
2057 });
2058 }
2059
2060 template <typename Item>
2061 static std::optional<bool> compareInvalid(const Item &lhs, const Item &rhs)
2062 {
2063 // invalid data > valid data
2064 if (!QRangeModelDetails::isValid(lhs))
2065 return false;
2066 if (!QRangeModelDetails::isValid(rhs))
2067 return true;
2068 return std::nullopt;
2069 }
2070
2071 const QRangeModelImpl * const that;
2073 const QCollator * const collator;
2075 const int m_sortRole;
2076 };
2077
2078 void sort(int column, Qt::SortOrder order)
2079 {
2080 if constexpr (isMutable() && std::is_swappable_v<row_type>) {
2081 if (rowCount({}) < 2 || column >= columnCount({}))
2082 return;
2083 Compare compare(this, column, order);
2084 if (!compare.checkComparable())
2085 return;
2086
2087 this->beginLayoutChange();
2088 QScopeGuard endLayoutChange([this]{ this->endLayoutChange(); });
2089 that().sortImpl([&compare](const auto &leftRow, const auto &rightRow) {
2090 if (auto anyInvalid = Compare::compareInvalid(leftRow, rightRow))
2091 return *anyInvalid;
2092 return row_traits::for_element_at(leftRow, compare.m_index.column(),
2093 [&rightRow, &compare](const auto &leftItem){
2094 return row_traits::for_element_at(rightRow, compare.m_index.column(),
2095 [&leftItem, &compare](const auto &rightItem){
2096 // Called by std::stable_sort. Since "column" is a runtime value, we
2097 // can't statically assert that lhs and rhs are of the same type.
2098 if constexpr (std::is_same_v<decltype(leftItem), decltype(rightItem)>) {
2099 if (auto anyInvalid = Compare::compareInvalid(leftItem, rightItem))
2100 return *anyInvalid;
2101 return compare(QRangeModelDetails::refTo(leftItem),
2102 QRangeModelDetails::refTo(rightItem));
2103 } else {
2104 Q_UNREACHABLE();
2105 }
2106 return false;
2107 });
2108 });
2109 });
2110 }
2111 }
2112
2113 template <typename LessThan>
2114 void sortSubRange(range_type &range, row_ptr expectedParent, const LessThan &lessThan)
2115 {
2116 auto begin = QRangeModelDetails::adl_begin(range);
2117 auto end = QRangeModelDetails::adl_end(range);
2118 if (begin == end)
2119 return;
2120
2121 QModelIndexList persistentIndexes = this->persistentIndexList();
2122 that().prunePersistentIndexList(persistentIndexes, expectedParent);
2123
2124 if (persistentIndexes.isEmpty()) {
2125 using It = typename range_features::iterator;
2126 constexpr bool is_random_access = std::is_base_of_v<std::random_access_iterator_tag,
2127 typename std::iterator_traits<It>::iterator_category>;
2128 // fast path if we have no persistent indexes: sort the range in place
2129 if constexpr (Compare::hasSortMember) {
2130 range.sort(lessThan);
2131 return;
2132 } else if constexpr (is_random_access) {
2133 std::stable_sort(begin, end, lessThan);
2134 return;
2135 }
2136 }
2137
2138 // slow path: create an indexed version of the range by adding a
2139 // column that records row movements.
2140 struct SortTracker
2141 {
2142 row_type row;
2143 int index;
2144 };
2145
2146 const int rangeSize = size(range);
2147 // Allocate all necessary memory here so that a potential exception
2148 // gets thrown before we have made any modifications.
2149 std::vector<SortTracker> tracked;
2150 tracked.reserve(rangeSize);
2151 std::vector<int> newRows;
2152 newRows.resize(rangeSize);
2153
2154 // move all rows into that index paired with its unsorted position
2155 int row = -1;
2156 for (auto &&it = std::move_iterator(begin); it != std::move_iterator(end); ++it)
2157 tracked.emplace_back(SortTracker{*it, ++row});
2158
2159 // sort the index based on a comparions of the data
2160 std::stable_sort(tracked.begin(), tracked.end(),
2161 [&lessThan](const SortTracker &lhs, const SortTracker &rhs){
2162 return lessThan(lhs.row, rhs.row);
2163 });
2164
2165 // write the values back to the range in (now ordered) sequence,
2166 // and create a mapping from old to new row
2167 auto sorted = std::move_iterator(tracked.begin());
2168 auto write = QRangeModelDetails::adl_begin(range);
2169 qsizetype changedIndexCount = 0;
2170 for (int newIndex = 0; newIndex < rangeSize; ++write, ++sorted, ++newIndex) {
2171 auto &&tracker = *sorted;
2172 changedIndexCount += (tracker.index != newIndex);
2173 *write = std::move(tracker.row);
2174 newRows[tracker.index] = newIndex;
2175 }
2176
2177 // free memory from intermediate vector
2178 tracked.clear();
2179 tracked.shrink_to_fit();
2180
2181 // update relevant persistent model indexes
2182 for (const auto &fromIndex : std::as_const(persistentIndexes)) {
2183 const int newRow = newRows.at(fromIndex.row());
2184 if (fromIndex.row() == newRow)
2185 continue;
2186 const QModelIndex toIndex = that().indexImpl(newRow,
2187 fromIndex.column(),
2188 fromIndex.parent());
2189 this->changePersistentIndex(fromIndex, toIndex);
2190 }
2191 }
2192
2193 QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits,
2194 Qt::MatchFlags flags) const
2195 {
2196 return that().matchImpl(start, role,
2197 QRangeModelImplBase::convertMatchValue(value, flags), hits, flags);
2198 }
2199
2200 bool matchRow(const_row_reference row, const QModelIndex &index, int role, const QVariant &value,
2201 Qt::MatchFlags flags) const
2202 {
2203 const uint matchType = (flags & Qt::MatchTypeMask).toInt();
2204
2205 return row_traits::for_element_at(row, index.column(), [&](const auto &element) {
2206 using value_type = q20::remove_cvref_t<decltype(element)>;
2207 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
2208 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
2209
2210 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasReadRole
2211 || multi_role() || has_metaobject<value_type>) {
2212 QModelRoleData roleData(role);
2213 ItemReader reader{index, roleData, this};
2214 reader(element);
2215 return QRangeModelImplBase::matchValue(roleData.data(), value, flags);
2216 } else if constexpr (std::is_same_v<wrapped_value_type, QVariant>) {
2217 return QRangeModelImplBase::matchValue(element, value, flags);
2218 } else {
2219 constexpr QMetaType mt = QMetaType::fromType<wrapped_value_type>();
2220 if (mt == value.metaType()) {
2221 if (matchType == Qt::MatchExactly)
2222 return mt.equals(QRangeModelDetails::pointerTo(element), value.constData());
2223 else if constexpr (std::is_same_v<wrapped_value_type, QString>)
2224 return QRangeModelImplBase::matchValue(element, value, flags);
2225 } else {
2226 return QRangeModelImplBase::matchValue(QVariant::fromValue(QRangeModelDetails::refTo(element)),
2227 value, flags);
2228 }
2229 }
2230 return false;
2231 });
2232 }
2233
2234 template <typename InsertFn>
2235 bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
2236 {
2237 if (count == 0)
2238 return false;
2239 range_type * const children = childRange(parent);
2240 if (!children)
2241 return false;
2242
2243 this->beginInsertColumns(parent, column, column + count - 1);
2244
2245 for (auto &child : *children) {
2246 auto it = QRangeModelDetails::pos(child, column);
2247 (void)insertFn(QRangeModelDetails::refTo(child), it, count);
2248 }
2249
2250 this->endInsertColumns();
2251
2252 // endInsertColumns emits columnsInserted, at which point clients might
2253 // have populated the new columns with objects (if the columns aren't objects
2254 // themselves).
2255 if constexpr (itemsAreQObjects) {
2256 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2257 for (int r = 0; r < that().rowCount(parent); ++r) {
2258 for (int c = column; c < column + count; ++c) {
2259 const QModelIndex index = that().index(r, c, parent);
2260 writeAt(index, [this, &index](QObject *item){
2261 return Self::connectProperties(index, item,
2262 m_data.context, m_data.properties);
2263 });
2264 }
2265 }
2266 }
2267 }
2268
2269 return true;
2270 }
2271
2272 bool insertColumns(int column, int count, const QModelIndex &parent)
2273 {
2274 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
2275 return doInsertColumns(column, count, parent, [](auto &row, auto it, int n){
2276 row.insert(it, n, {});
2277 return true;
2278 });
2279 } else {
2280 return false;
2281 }
2282 }
2283
2284 bool removeColumns(int column, int count, const QModelIndex &parent)
2285 {
2286 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
2287 if (column < 0 || column + count > columnCount(parent))
2288 return false;
2289
2290 range_type * const children = childRange(parent);
2291 if (!children)
2292 return false;
2293
2294 if constexpr (itemsAreQObjects) {
2295 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2296 for (int r = 0; r < that().rowCount(parent); ++r) {
2297 for (int c = column; c < column + count; ++c) {
2298 const QModelIndex index = that().index(r, c, parent);
2299 writeAt(index, [this](QObject *item){
2300 m_data.connections.removeIf([item](const auto &connection) {
2301 return connection.sender == item;
2302 });
2303 return true;
2304 });
2305 }
2306 }
2307 }
2308 }
2309
2310 this->beginRemoveColumns(parent, column, column + count - 1);
2311 for (auto &child : *children) {
2312 const auto start = QRangeModelDetails::pos(child, column);
2313 QRangeModelDetails::refTo(child).erase(start, std::next(start, count));
2314 }
2315 this->endRemoveColumns();
2316 return true;
2317 }
2318 return false;
2319 }
2320
2321 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
2322 const QModelIndex &destParent, int destColumn)
2323 {
2324 // we only support moving columns within the same parent
2325 if (sourceParent != destParent)
2326 return false;
2327 if constexpr (isMutable() && (row_features::has_rotate || row_features::has_splice)) {
2328 if (!Structure::canMoveColumns(sourceParent, destParent))
2329 return false;
2330
2331 if constexpr (dynamicColumns()) {
2332 // we only support ranges as columns, as other types might
2333 // not have the same data type across all columns
2334 range_type * const children = childRange(sourceParent);
2335 if (!children)
2336 return false;
2337
2338 if (!this->beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
2339 destParent, destColumn)) {
2340 return false;
2341 }
2342
2343 for (auto &child : *children)
2344 QRangeModelDetails::rotate(child, sourceColumn, count, destColumn);
2345
2346 this->endMoveColumns();
2347 return true;
2348 }
2349 }
2350 return false;
2351 }
2352
2353 template <typename InsertFn>
2354 bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
2355 {
2356 range_type *children = childRange(parent);
2357 if (!children)
2358 return false;
2359
2360 this->beginInsertRows(parent, row, row + count - 1);
2361
2362 row_ptr parentRow = parent.isValid()
2363 ? QRangeModelDetails::pointerTo(this->rowData(parent))
2364 : nullptr;
2365 (void)std::forward<InsertFn>(insertFn)(*children, parentRow, row, count);
2366
2367 // fix the parent in all children of the modified row, as the
2368 // references back to the parent might have become invalid.
2369 that().resetParentInChildren(children);
2370
2371 this->endInsertRows();
2372
2373 // endInsertRows emits rowsInserted, at which point clients might
2374 // have populated the new row with objects (if the rows aren't objects
2375 // themselves).
2376 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2377 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2378 const auto begin = QRangeModelDetails::pos(children, row);
2379 const auto end = std::next(begin, count);
2380 int rowIndex = row;
2381 for (auto it = begin; it != end; ++it, ++rowIndex)
2382 autoConnectPropertiesInRow(*it, rowIndex, parent);
2383 }
2384 }
2385
2386 return true;
2387 }
2388
2389 bool insertRows(int row, int count, const QModelIndex &parent)
2390 {
2391 if constexpr (canInsertRows()) {
2392 return doInsertRows(row, count, parent,
2393 [this](range_type &children, row_ptr parentRow, int r, int n){
2394 EmptyRowGenerator generator{0, &that(), parentRow};
2395
2396 const auto pos = QRangeModelDetails::pos(children, r);
2397 if constexpr (range_features::has_insert_range) {
2398 children.insert(pos, std::move(generator), EmptyRowGenerator{n});
2399 } else if constexpr (rows_are_owning_or_raw_pointers) {
2400 auto start = children.insert(pos, n, nullptr); // MSVC doesn't like row_type{}
2401 std::copy(std::move(generator), EmptyRowGenerator{n}, start);
2402 } else {
2403 children.insert(pos, n, std::move(*generator));
2404 }
2405 return true;
2406 });
2407 } else {
2408 return false;
2409 }
2410 }
2411
2412 bool removeRows(int row, int count, const QModelIndex &parent = {})
2413 {
2414 if constexpr (canRemoveRows()) {
2415 const int prevRowCount = rowCount(parent);
2416 if (row < 0 || row + count > prevRowCount)
2417 return false;
2418
2419 range_type *children = childRange(parent);
2420 if (!children)
2421 return false;
2422
2423 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2424 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2425 const auto begin = QRangeModelDetails::pos(children, row);
2426 const auto end = std::next(begin, count);
2427 int rowIndex = row;
2428 for (auto it = begin; it != end; ++it, ++rowIndex)
2429 clearConnectionInRow(*it, rowIndex, parent);
2430 }
2431 }
2432
2433 this->beginRemoveRows(parent, row, row + count - 1);
2434 [[maybe_unused]] bool callEndRemoveColumns = false;
2435 if constexpr (dynamicColumns()) {
2436 // if we remove the last row in a dynamic model, then we no longer
2437 // know how many columns we should have, so they will be reported as 0.
2438 if (prevRowCount == count) {
2439 if (const int columns = columnCount(parent)) {
2440 callEndRemoveColumns = true;
2441 this->beginRemoveColumns(parent, 0, columns - 1);
2442 }
2443 }
2444 }
2445 { // erase invalidates iterators
2446 const auto begin = QRangeModelDetails::pos(children, row);
2447 const auto end = std::next(begin, count);
2448 that().deleteRemovedRows(begin, end);
2449 children->erase(begin, end);
2450 }
2451 // fix the parent in all children of the modified row, as the
2452 // references back to the parent might have become invalid.
2453 that().resetParentInChildren(children);
2454
2455 if constexpr (dynamicColumns()) {
2456 if (callEndRemoveColumns) {
2457 Q_ASSERT(columnCount(parent) == 0);
2458 this->endRemoveColumns();
2459 }
2460 }
2461 this->endRemoveRows();
2462 return true;
2463 } else {
2464 return false;
2465 }
2466 }
2467
2468 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
2469 const QModelIndex &destParent, int destRow)
2470 {
2471 if constexpr (isMutable() && (range_features::has_rotate || range_features::has_splice)) {
2472 if (!Structure::canMoveRows(sourceParent, destParent))
2473 return false;
2474
2475 if (sourceParent != destParent) {
2476 return that().moveRowsAcross(sourceParent, sourceRow, count,
2477 destParent, destRow);
2478 }
2479
2480 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
2481 || sourceRow < 0 || sourceRow + count - 1 >= this->rowCount(sourceParent)
2482 || destRow < 0 || destRow > this->rowCount(destParent)) {
2483 return false;
2484 }
2485
2486 range_type *source = childRange(sourceParent);
2487 // moving within the same range
2488 if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
2489 return false;
2490
2491 QRangeModelDetails::rotate(source, sourceRow, count, destRow);
2492
2493 that().resetParentInChildren(source);
2494
2495 this->endMoveRows();
2496 return true;
2497 } else {
2498 return false;
2499 }
2500 }
2501
2502 const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2503 protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2504
2505 QModelIndex parent(const QModelIndex &child) const { return that().parentImpl(child); }
2506
2507 int rowCount(const QModelIndex &parent) const { return that().rowCountImpl(parent); }
2508
2509 static constexpr int fixedColumnCount()
2510 {
2511 if constexpr (one_dimensional_range)
2512 return row_traits::fixed_size();
2513 else
2514 return static_column_count;
2515 }
2516 int columnCount(const QModelIndex &parent) const { return that().columnCountImpl(parent); }
2517
2518 void destroy() { delete std::addressof(that()); }
2519
2520 template <typename BaseMethod, typename BaseMethod::template Overridden<Self> overridden>
2521 using Override = typename Ancestor::template Override<BaseMethod, overridden>;
2522
2531
2546
2550
2554protected:
2556 {
2558 }
2559
2561 {
2562 // We delete row objects if we are not operating on a reference or pointer
2563 // to a range, as in that case, the owner of the referenced/pointed to
2564 // range also owns the row entries.
2565 // ### Problem: if we get a copy of a range (no matter if shared or not),
2566 // then adding rows will create row objects in the model's copy, and the
2567 // client can never delete those. But copied rows will be the same pointer,
2568 // which we must not delete (as we didn't create them).
2569
2570 static constexpr bool modelCopied = !QRangeModelDetails::is_wrapped<Range>() &&
2571 (std::is_reference_v<Range> || std::is_const_v<std::remove_reference_t<Range>>);
2572
2573 static constexpr bool modelShared = QRangeModelDetails::is_any_shared_ptr<Range>();
2574
2575 static constexpr bool default_row_deleter = protocol_traits::is_default &&
2576 protocol_traits::has_deleteRow;
2577
2578 static constexpr bool ambiguousRowOwnership = (modelCopied || modelShared) &&
2579 rows_are_raw_pointers && default_row_deleter;
2580
2581 static_assert(!ambiguousRowOwnership,
2582 "Using of copied and shared tree and table models with rows as raw pointers, "
2583 "and the default protocol is not allowed due to ambiguity of rows ownership. "
2584 "Move the model in, use another row type, or implement a custom tree protocol.");
2585
2586 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
2587 && !QRangeModelDetails::is_any_of<Range, std::reference_wrapper>()) {
2588 const auto begin = QRangeModelDetails::adl_begin(*m_data.model());
2589 const auto end = QRangeModelDetails::adl_end(*m_data.model());
2590 that().deleteRemovedRows(begin, end);
2591 }
2592 }
2593
2594 static constexpr bool canInsertRows()
2595 {
2596 if constexpr (dynamicColumns() && !row_features::has_resize) {
2597 // If we operate on dynamic columns and cannot resize a newly
2598 // constructed row, then we cannot insert.
2599 return false;
2600 } else if constexpr (!protocol_traits::has_newRow) {
2601 // We also cannot insert if we cannot create a new row element
2602 return false;
2603 } else if constexpr (!range_features::has_insert_range
2604 && !std::is_copy_constructible_v<row_type>) {
2605 // And if the row is a move-only type, then the range needs to be
2606 // backed by a container that can move-insert default-constructed
2607 // row elements.
2608 return false;
2609 } else {
2610 return Structure::canInsertRowsImpl();
2611 }
2612 }
2613
2614 static constexpr bool canRemoveRows()
2615 {
2616 return Structure::canRemoveRowsImpl();
2617 }
2618
2619 template <typename F>
2620 bool writeAt(const QModelIndex &index, F&& writer)
2621 {
2622 row_reference row = rowData(index);
2623 if (!QRangeModelDetails::isValid(row))
2624 return false;
2625 return row_traits::for_element_at(row, index.column(), [&writer](auto &&target) {
2626 using target_type = decltype(target);
2627 // we can only assign to an lvalue reference
2628 if constexpr (std::is_lvalue_reference_v<target_type>
2629 && !std::is_const_v<std::remove_reference_t<target_type>>) {
2630 return writer(std::forward<target_type>(target));
2631 } else {
2632 return false;
2633 }
2634 });
2635 }
2636
2637 template <typename F>
2638 bool readAt(const QModelIndex &index, F&& reader) const {
2639 const_row_reference row = rowData(index);
2640 if (!QRangeModelDetails::isValid(row))
2641 return false;
2642 return row_traits::for_element_at(row, index.column(), std::forward<F>(reader));
2643 }
2644
2645 template <typename Value>
2646 static QVariant read(const Value &value)
2647 {
2648 if constexpr (std::is_constructible_v<QVariant, Value>)
2649 return QVariant(value);
2650 else
2651 return QVariant::fromValue(value);
2652 }
2653 template <typename Value>
2654 static QVariant read(Value *value)
2655 {
2656 if (value) {
2657 if constexpr (std::is_constructible_v<QVariant, Value *>)
2658 return QVariant(value);
2659 else
2660 return read(*value);
2661 }
2662 return {};
2663 }
2664
2665 template <typename Target>
2666 static bool write(Target &target, const QVariant &value)
2667 {
2668 using Type = std::remove_reference_t<Target>;
2669 if constexpr (std::is_constructible_v<Target, QVariant>) {
2670 target = value;
2671 return true;
2672 } else if (value.canConvert<Type>()) {
2673 target = value.value<Type>();
2674 return true;
2675 }
2676 return false;
2677 }
2678 template <typename Target>
2679 static bool write(Target *target, const QVariant &value)
2680 {
2681 if (target)
2682 return write(*target, value);
2683 return false;
2684 }
2685
2686 template <typename ItemType>
2688 {
2689 struct {
2690 operator QMetaProperty() const {
2691 const QByteArray roleName = that.itemModel().roleNames().value(role);
2692 const QMetaObject &mo = ItemType::staticMetaObject;
2693 if (const int index = mo.indexOfProperty(roleName.data());
2694 index >= 0) {
2695 return mo.property(index);
2696 }
2697 return {};
2698 }
2699 const QRangeModelImpl &that;
2700 const int role;
2701 } findProperty{*this, role};
2702
2703 if constexpr (ModelData::cachesProperties)
2704 return *m_data.properties.tryEmplace(role, findProperty).iterator;
2705 else
2706 return findProperty;
2707 }
2708
2709 void connectPropertyOnRead(const QModelIndex &index, int role,
2710 const QObject *gadget, const QMetaProperty &prop) const
2711 {
2712 if (!index.isValid())
2713 return;
2714 const typename ModelData::Connection connection = {gadget, role};
2715 if (prop.hasNotifySignal() && this->autoConnectPolicy() == AutoConnectPolicy::OnRead
2716 && !m_data.connections.contains(connection)) {
2717 if constexpr (isMutable())
2718 Self::connectProperty(index, gadget, m_data.context, role, prop);
2719 else
2720 Self::connectPropertyConst(index, gadget, m_data.context, role, prop);
2721 m_data.connections.insert(connection);
2722 }
2723 }
2724
2725 template <typename ItemType>
2726 QVariant readRole(const QModelIndex &index, int role, ItemType *gadget) const
2727 {
2728 using item_type = std::remove_pointer_t<ItemType>;
2729 QVariant result;
2730 QMetaProperty prop = roleProperty<item_type>(role);
2731 if (!prop.isValid() && role == Qt::EditRole) {
2732 role = Qt::DisplayRole;
2733 prop = roleProperty<item_type>(Qt::DisplayRole);
2734 }
2735
2736 if (prop.isValid()) {
2737 if constexpr (itemsAreQObjects)
2738 connectPropertyOnRead(index, role, gadget, prop);
2739 result = readProperty(prop, gadget);
2740 }
2741 return result;
2742 }
2743
2744 template <typename ItemType>
2745 QVariant readRole(const QModelIndex &index, int role, const ItemType &gadget) const
2746 {
2747 return readRole(index, role, &gadget);
2748 }
2749
2750 template <typename ItemType>
2751 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
2752 {
2753 if constexpr (std::is_base_of_v<QObject, ItemType>)
2754 return prop.read(gadget);
2755 else
2756 return prop.readOnGadget(gadget);
2757 }
2758
2759 template <typename ItemType>
2760 QVariant readProperty(const QModelIndex &index, ItemType *gadget) const
2761 {
2762 using item_type = std::remove_pointer_t<ItemType>;
2763 const QMetaObject &mo = item_type::staticMetaObject;
2764 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
2765
2766 if constexpr (rowsAreQObjects)
2767 connectPropertyOnRead(index, Qt::DisplayRole, gadget, prop);
2768
2769 return readProperty(prop, gadget);
2770 }
2771
2772 template <typename ItemType>
2773 QVariant readProperty(const QModelIndex &index, const ItemType &gadget) const
2774 {
2775 return readProperty(index, &gadget);
2776 }
2777
2778 template <typename ItemType>
2779 bool writeRole(int role, ItemType *gadget, const QVariant &data)
2780 {
2781 using item_type = std::remove_pointer_t<ItemType>;
2782 auto prop = roleProperty<item_type>(role);
2783 if (!prop.isValid() && role == Qt::EditRole)
2784 prop = roleProperty<item_type>(Qt::DisplayRole);
2785
2786 return prop.isValid() ? writeProperty(prop, gadget, data) : false;
2787 }
2788
2789 template <typename ItemType>
2790 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
2791 {
2792 return writeRole(role, &gadget, data);
2793 }
2794
2795 template <typename ItemType>
2796 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
2797 {
2798 if constexpr (std::is_base_of_v<QObject, ItemType>)
2799 return prop.write(gadget, data);
2800 else
2801 return prop.writeOnGadget(gadget, data);
2802 }
2803 template <typename ItemType>
2804 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
2805 {
2806 using item_type = std::remove_pointer_t<ItemType>;
2807 const QMetaObject &mo = item_type::staticMetaObject;
2808 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
2809 }
2810
2811 template <typename ItemType>
2812 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
2813 {
2814 return writeProperty(property, &gadget, data);
2815 }
2816
2817 template <typename ItemType>
2818 static bool resetProperty(int property, ItemType *object)
2819 {
2820 using item_type = std::remove_pointer_t<ItemType>;
2821 const QMetaObject &mo = item_type::staticMetaObject;
2822 bool success = true;
2823 if (property == -1) {
2824 // reset all properties
2825 if constexpr (std::is_base_of_v<QObject, item_type>) {
2826 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
2827 success = writeProperty(mo.property(p), object, {}) && success;
2828 } else { // reset a gadget by assigning a default-constructed
2829 *object = {};
2830 }
2831 } else {
2832 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
2833 }
2834 return success;
2835 }
2836
2837 template <typename ItemType>
2838 static bool resetProperty(int property, ItemType &&object)
2839 {
2840 return resetProperty(property, &object);
2841 }
2842
2843 // helpers
2844 const_row_reference rowData(const QModelIndex &index) const
2845 {
2846 Q_ASSERT(index.isValid());
2847 return that().rowDataImpl(index);
2848 }
2849
2850 row_reference rowData(const QModelIndex &index)
2851 {
2852 Q_ASSERT(index.isValid());
2853 return that().rowDataImpl(index);
2854 }
2855
2856 const range_type *childRange(const QModelIndex &index) const
2857 {
2858 if (!index.isValid())
2859 return m_data.model();
2860 if (index.column()) // only items at column 0 can have children
2861 return nullptr;
2862 return that().childRangeImpl(index);
2863 }
2864
2865 range_type *childRange(const QModelIndex &index)
2866 {
2867 if (!index.isValid())
2868 return m_data.model();
2869 if (index.column()) // only items at column 0 can have children
2870 return nullptr;
2871 return that().childRangeImpl(index);
2872 }
2873
2874 template <typename, typename, typename> friend class QRangeModelAdapter;
2875
2877};
2878
2879// Implementations that depends on the model structure (flat vs tree) that will
2880// be specialized based on a protocol type. The main template implements tree
2881// support through a protocol type.
2882template <typename Range, typename Protocol>
2884 : public QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
2885{
2886 using Base = QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2887 friend class QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
2888
2889 using range_type = typename Base::range_type;
2890 using range_features = typename Base::range_features;
2891 using row_type = typename Base::row_type;
2892 using row_ptr = typename Base::row_ptr;
2893 using const_row_ptr = typename Base::const_row_ptr;
2894
2895 using tree_traits = typename Base::protocol_traits;
2896 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
2897
2898 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
2899 QRangeModelDetails::is_smart_ptr<row_type>() ||
2900 QRangeModelDetails::is_any_of<row_type, std::reference_wrapper>();
2901 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
2902
2903public:
2904 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
2905 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
2906 {};
2907
2908 void setParentRow(range_type &children, row_ptr parent)
2909 {
2910 for (auto &&child : children)
2911 this->protocol().setParentRow(QRangeModelDetails::refTo(child), parent);
2912 resetParentInChildren(&children);
2913 }
2914
2915 void deleteRemovedRows(range_type &range)
2916 {
2917 deleteRemovedRows(QRangeModelDetails::adl_begin(range), QRangeModelDetails::adl_end(range));
2918 }
2919
2920 bool autoConnectProperties(const QModelIndex &parent) const
2921 {
2922 auto *children = this->childRange(parent);
2923 if (!children)
2924 return true;
2925 return autoConnectPropertiesRange(QRangeModelDetails::refTo(children), parent);
2926 }
2927
2928protected:
2929 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
2930 {
2931 if (!parent.isValid())
2932 return this->createIndex(row, column);
2933 // only items at column 0 can have children
2934 if (parent.column())
2935 return QModelIndex();
2936
2937 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
2938 const auto &parentSiblings = childrenOf(grandParent);
2939 const auto it = QRangeModelDetails::pos(parentSiblings, parent.row());
2940 return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
2941 }
2942
2943 QModelIndex parentImpl(const QModelIndex &child) const
2944 {
2945 if (!child.isValid())
2946 return {};
2947
2948 // no pointer to parent row - no parent
2949 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
2950 if (!parentRow)
2951 return {};
2952
2953 // get the siblings of the parent via the grand parent
2954 auto &&grandParent = this->protocol().parentRow(QRangeModelDetails::refTo(parentRow));
2955 const range_type &parentSiblings = childrenOf(QRangeModelDetails::pointerTo(grandParent));
2956 // find the index of parentRow
2957 const auto begin = QRangeModelDetails::adl_begin(parentSiblings);
2958 const auto end = QRangeModelDetails::adl_end(parentSiblings);
2959 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
2960 return QRangeModelDetails::pointerTo(std::forward<decltype(s)>(s)) == parentRow;
2961 });
2962 if (it != end)
2963 return this->createIndex(std::distance(begin, it), 0,
2964 QRangeModelDetails::pointerTo(grandParent));
2965 return {};
2966 }
2967
2968 int rowCountImpl(const QModelIndex &parent) const
2969 {
2970 return Base::size(this->childRange(parent));
2971 }
2972
2973 int columnCountImpl(const QModelIndex &) const
2974 {
2975 // All levels of a tree have to have the same, fixed, column count.
2976 // If static_column_count is -1 for a tree, static assert fires
2977 return Base::fixedColumnCount();
2978 }
2979
2980 static constexpr Qt::ItemFlags defaultFlags()
2981 {
2982 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2983 }
2984
2985 static constexpr bool canInsertRowsImpl()
2986 {
2987 // We must not insert rows if we cannot adjust the parents of the
2988 // children of the following rows. We don't have to do that if the
2989 // range operates on pointers.
2990 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
2991 && Base::dynamicRows() && range_features::has_insert;
2992 }
2993
2994 static constexpr bool canRemoveRowsImpl()
2995 {
2996 // We must not remove rows if we cannot adjust the parents of the
2997 // children of the following rows. We don't have to do that if the
2998 // range operates on pointers.
2999 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
3000 && Base::dynamicRows() && range_features::has_erase;
3001 }
3002
3003 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
3004 {
3005 return true;
3006 }
3007
3008 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
3009 {
3010 return true;
3011 }
3012
3013 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
3014 const QModelIndex &destParent, int destRow)
3015 {
3016 // If rows are pointers, then reference to the parent row don't
3017 // change, so we can move them around freely. Otherwise we need to
3018 // be able to explicitly update the parent pointer.
3019 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
3020 return false;
3021 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
3022 return false;
3023 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
3024 destParent, destRow)) {
3025 return false;
3026 }
3027
3028 range_type *source = this->childRange(sourceParent);
3029 range_type *destination = this->childRange(destParent);
3030
3031 // If we can insert data from another range into, then
3032 // use that to move the old data over.
3033 const auto destStart = QRangeModelDetails::pos(destination, destRow);
3034 if constexpr (range_features::has_insert_range) {
3035 const auto sourceStart = QRangeModelDetails::pos(*source, sourceRow);
3036 const auto sourceEnd = std::next(sourceStart, count);
3037
3038 destination->insert(destStart, std::move_iterator(sourceStart),
3039 std::move_iterator(sourceEnd));
3040 } else if constexpr (std::is_copy_constructible_v<row_type>) {
3041 // otherwise we have to make space first, and copy later.
3042 destination->insert(destStart, count, row_type{});
3043 }
3044
3045 row_ptr parentRow = destParent.isValid()
3046 ? QRangeModelDetails::pointerTo(this->rowData(destParent))
3047 : nullptr;
3048
3049 // if the source's parent was already inside the new parent row,
3050 // then the source row might have become invalid, so reset it.
3051 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
3052 if (sourceParent.row() < destRow) {
3053 source = this->childRange(sourceParent);
3054 } else {
3055 // the source parent moved down within destination
3056 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
3057 sourceParent.internalPointer()));
3058 }
3059 }
3060
3061 // move the data over and update the parent pointer
3062 {
3063 const auto writeStart = QRangeModelDetails::pos(destination, destRow);
3064 const auto writeEnd = std::next(writeStart, count);
3065 const auto sourceStart = QRangeModelDetails::pos(source, sourceRow);
3066 const auto sourceEnd = std::next(sourceStart, count);
3067
3068 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
3069 // move data over if not already done, otherwise
3070 // only fix the parent pointer
3071 if constexpr (!range_features::has_insert_range)
3072 *write = std::move(*read);
3073 this->protocol().setParentRow(QRangeModelDetails::refTo(*write), parentRow);
3074 }
3075 // remove the old rows from the source parent
3076 source->erase(sourceStart, sourceEnd);
3077 }
3078
3079 // Fix the parent pointers in children of both source and destination
3080 // ranges, as the references to the entries might have become invalid.
3081 // We don't have to do that if the rows are pointers, as in that case
3082 // the references to the entries are stable.
3083 resetParentInChildren(destination);
3085
3086 this->endMoveRows();
3087 return true;
3088 }
3089
3090 auto makeEmptyRow(row_ptr parentRow)
3091 {
3092 // tree traversal protocol: if we are here, then it must be possible
3093 // to change the parent of a row.
3094 static_assert(tree_traits::has_setParentRow);
3095 row_type empty_row = this->protocol().newRow();
3096 if (QRangeModelDetails::isValid(empty_row) && parentRow)
3097 this->protocol().setParentRow(QRangeModelDetails::refTo(empty_row), parentRow);
3098 return empty_row;
3099 }
3100
3101 template <typename It, typename Sentinel>
3102 void deleteRemovedRows(It &&begin, Sentinel &&end)
3103 {
3104 if constexpr (tree_traits::has_deleteRow) {
3105 for (auto it = begin; it != end; ++it) {
3106 if constexpr (Base::isMutable()) {
3107 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
3108 if (QRangeModelDetails::isValid(children)) {
3109 deleteRemovedRows(QRangeModelDetails::adl_begin(children),
3110 QRangeModelDetails::adl_end(children));
3111 QRangeModelDetails::refTo(children) = range_type{ };
3112 }
3113 }
3114
3115 this->protocol().deleteRow(std::move(*it));
3116 }
3117 }
3118 }
3119
3120 void resetParentInChildren(range_type *children)
3121 {
3122 const auto persistentIndexList = this->persistentIndexList();
3123 const auto [firstColumn, lastColumn] = [&persistentIndexList]{
3124 int first = std::numeric_limits<int>::max();
3125 int last = -1;
3126 for (const auto &pmi : persistentIndexList) {
3127 first = (std::min)(pmi.column(), first);
3128 last = (std::max)(pmi.column(), last);
3129 }
3130 return std::pair(first, last);
3131 }();
3132
3133 resetParentInChildrenRecursive(children, firstColumn, lastColumn);
3134 }
3135
3136 void resetParentInChildrenRecursive(range_type *children, int pmiFromColumn, int pmiToColumn)
3137 {
3138 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
3139 const bool changePersistentIndexes = pmiToColumn >= pmiFromColumn;
3140 const auto begin = QRangeModelDetails::adl_begin(*children);
3141 const auto end = QRangeModelDetails::adl_end(*children);
3142 for (auto it = begin; it != end; ++it) {
3143 decltype(auto) maybeChildren = this->protocol().childRows(*it);
3144 if (QRangeModelDetails::isValid(maybeChildren)) {
3145 auto &childrenRef = QRangeModelDetails::refTo(maybeChildren);
3146 auto *parentRow = QRangeModelDetails::pointerTo(*it);
3147
3148 int row = 0;
3149 for (auto &child : childrenRef) {
3150 const_row_ptr oldParent = this->protocol().parentRow(child);
3151 if (oldParent != parentRow) {
3152 if (changePersistentIndexes) {
3153 for (int column = pmiFromColumn; column <= pmiToColumn; ++column) {
3154 this->changePersistentIndex(this->createIndex(row, column, oldParent),
3155 this->createIndex(row, column, parentRow));
3156 }
3157 }
3158 this->protocol().setParentRow(child, parentRow);
3159 }
3160 ++row;
3161 }
3162 resetParentInChildrenRecursive(&childrenRef, pmiFromColumn, pmiToColumn);
3163 }
3164 }
3165 }
3166 }
3167
3168 bool autoConnectPropertiesRange(const range_type &range, const QModelIndex &parent) const
3169 {
3170 int rowIndex = 0;
3171 for (const auto &row : range) {
3172 if (!this->autoConnectPropertiesInRow(row, rowIndex, parent))
3173 return false;
3174 Q_ASSERT(QRangeModelDetails::isValid(row));
3175 const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3176 if (QRangeModelDetails::isValid(children)) {
3177 if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
3178 this->itemModel().index(rowIndex, 0, parent))) {
3179 return false;
3180 }
3181 }
3182 ++rowIndex;
3183 }
3184 return true;
3185 }
3186
3188 {
3189 return autoConnectPropertiesRange(*this->m_data.model(), {});
3190 }
3191
3192 decltype(auto) rowDataImpl(const QModelIndex &index) const
3193 {
3194 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
3195 const range_type &siblings = childrenOf(parentRow);
3196 Q_ASSERT(index.row() < int(Base::size(siblings)));
3197 return *QRangeModelDetails::pos(siblings, index.row());
3198 }
3199
3200 decltype(auto) rowDataImpl(const QModelIndex &index)
3201 {
3202 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
3203 range_type &siblings = childrenOf(parentRow);
3204 Q_ASSERT(index.row() < int(Base::size(siblings)));
3205 return *QRangeModelDetails::pos(siblings, index.row());
3206 }
3207
3208 const range_type *childRangeImpl(const QModelIndex &index) const
3209 {
3210 const auto &row = this->rowData(index);
3211 if (!QRangeModelDetails::isValid(row))
3212 return static_cast<const range_type *>(nullptr);
3213
3214 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3215 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
3216 }
3217
3218 range_type *childRangeImpl(const QModelIndex &index)
3219 {
3220 auto &row = this->rowData(index);
3221 if (!QRangeModelDetails::isValid(row))
3222 return static_cast<range_type *>(nullptr);
3223
3224 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3225 using Children = std::remove_reference_t<decltype(children)>;
3226
3227 if constexpr (QRangeModelDetails::is_any_of<Children, std::optional>())
3228 if constexpr (std::is_default_constructible<typename Children::value_type>()) {
3229 if (!children)
3230 children.emplace(range_type{});
3231 }
3232
3233 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
3234 }
3235
3236 const range_type &childrenOf(const_row_ptr row) const
3237 {
3238 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
3239 : *this->m_data.model();
3240 }
3241
3242 template <typename LessThan>
3243 void sortImplRecursive(range_type &range, row_ptr parentRow, const LessThan &lessThan)
3244 {
3245 for (auto &row : range) {
3246 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3247 if (QRangeModelDetails::isValid(children)) {
3248 sortImplRecursive(QRangeModelDetails::refTo(children),
3249 QRangeModelDetails::pointerTo(row), lessThan);
3250 }
3251 }
3252 this->sortSubRange(range, parentRow, lessThan);
3253 }
3254
3255 template <typename LessThan>
3256 void sortImpl(const LessThan &lessThan)
3257 {
3258 sortImplRecursive(*this->m_data.model(), nullptr, lessThan);
3259 resetParentInChildren(this->m_data.model());
3260 }
3261
3262 void prunePersistentIndexList(QModelIndexList &list, row_ptr expectedParent)
3263 {
3264 erase_if(list, [expectedParent](const QModelIndex &index){
3265 return static_cast<row_ptr>(index.internalPointer()) != expectedParent;
3266 });
3267 }
3268
3269 void matchImplRecursive(const range_type &range, const_row_ptr parentPtr, int from, int to,
3270 int role, const QVariant &value, int hits, Qt::MatchFlags flags, int column,
3271 QModelIndexList &result) const
3272 {
3273 auto it = QRangeModelDetails::pos(range, from);
3274 auto end = QRangeModelDetails::adl_end(range);
3275
3276 const bool recurse = flags.testAnyFlag(Qt::MatchRecursive);
3277 const bool allHits = (hits == -1);
3278
3279 for (int r = from; it != end && r < to && (allHits || result.size() < hits); ++it, ++r) {
3280 const QModelIndex index = this->createIndex(r, column, parentPtr);
3281 if (this->matchRow(*it, index, role, value, flags))
3282 result.append(index);
3283
3284 if (recurse) {
3285 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
3286
3287 if (QRangeModelDetails::isValid(children)) {
3288 matchImplRecursive(QRangeModelDetails::refTo(children),
3289 QRangeModelDetails::pointerTo(*it), 0,
3290 int(QRangeModelDetails::size(QRangeModelDetails::refTo(children))),
3291 role, value, hits, flags, column, result);
3292 }
3293 }
3294 }
3295 }
3296
3297 QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value, int hits,
3298 Qt::MatchFlags flags) const
3299 {
3300 QModelIndexList result;
3301 const bool wrap = flags.testAnyFlag(Qt::MatchWrap);
3302 const int column = start.column();
3303 const int from = start.row();
3304 const int to = this->rowCount(start.parent());
3305
3306 for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) {
3307 const int fromRow = (i == 0) ? from : 0;
3308 const int toRow = (i == 0) ? to : from;
3309 matchImplRecursive(*this->m_data.model(), nullptr, fromRow, toRow,
3310 role, value, hits, flags, column, result);
3311 }
3312 return result;
3313 }
3314
3315private:
3316 range_type &childrenOf(row_ptr row)
3317 {
3318 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
3319 : *this->m_data.model();
3320 }
3321};
3322
3323// specialization for flat models without protocol
3324template <typename Range>
3327{
3330
3331 static constexpr bool is_mutable_impl = true;
3332
3333public:
3334 using range_type = typename Base::range_type;
3336 using row_type = typename Base::row_type;
3338 using row_traits = typename Base::row_traits;
3339 using row_features = typename Base::row_features;
3340
3341 explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
3342 : Base(std::forward<Range>(model), {}, itemModel)
3343 {}
3344
3345protected:
3346 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
3347 {
3348 if constexpr (Base::dynamicColumns()) {
3349 if (column < int(Base::size(*QRangeModelDetails::pos(*this->m_data.model(), row))))
3350 return this->createIndex(row, column);
3351#ifndef QT_NO_DEBUG
3352 // if we got here, then column < columnCount(), but this row is too short
3353 qCritical("QRangeModel: Column-range at row %d is not large enough!", row);
3354#endif
3355 return {};
3356 } else {
3357 return this->createIndex(row, column);
3358 }
3359 }
3360
3361 QModelIndex parentImpl(const QModelIndex &) const
3362 {
3363 return {};
3364 }
3365
3366 int rowCountImpl(const QModelIndex &parent) const
3367 {
3368 if (parent.isValid())
3369 return 0;
3370 return int(Base::size(*this->m_data.model()));
3371 }
3372
3373 int columnCountImpl(const QModelIndex &parent) const
3374 {
3375 if (parent.isValid())
3376 return 0;
3377
3378 // in a table, all rows have the same number of columns (as the first row)
3379 if constexpr (Base::dynamicColumns()) {
3380 return int(Base::size(*this->m_data.model()) == 0
3381 ? 0
3382 : Base::size(*QRangeModelDetails::adl_begin(*this->m_data.model())));
3383 } else {
3384 return Base::fixedColumnCount();
3385 }
3386 }
3387
3388 static constexpr Qt::ItemFlags defaultFlags()
3389 {
3390 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
3391 }
3392
3393 static constexpr bool canInsertRowsImpl()
3394 {
3395 return Base::dynamicRows() && range_features::has_insert;
3396 }
3397
3398 static constexpr bool canRemoveRowsImpl()
3399 {
3400 return Base::dynamicRows() && range_features::has_erase;
3401 }
3402
3403 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
3404 {
3405 return !source.isValid() && !destination.isValid();
3406 }
3407
3408 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
3409 {
3410 return !source.isValid() && !destination.isValid();
3411 }
3412
3413 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
3414 const QModelIndex &, int) noexcept
3415 {
3416 // table/flat model: can't move rows between different parents
3417 return false;
3418 }
3419
3420 auto makeEmptyRow(typename Base::row_ptr)
3421 {
3422 row_type empty_row = this->protocol().newRow();
3423
3424 // dynamically sized rows all have to have the same column count
3425 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
3426 if (QRangeModelDetails::isValid(empty_row))
3427 QRangeModelDetails::refTo(empty_row).resize(this->columnCount({}));
3428 }
3429
3430 return empty_row;
3431 }
3432
3433 template <typename It, typename Sentinel>
3434 void deleteRemovedRows(It &&begin, Sentinel &&end)
3435 {
3436 if constexpr (Base::protocol_traits::has_deleteRow) {
3437 for (auto it = begin; it != end; ++it)
3438 this->protocol().deleteRow(std::move(*it));
3439 }
3440 }
3441
3442 decltype(auto) rowDataImpl(const QModelIndex &index) const
3443 {
3444 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
3445 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
3446 }
3447
3448 decltype(auto) rowDataImpl(const QModelIndex &index)
3449 {
3450 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
3451 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
3452 }
3453
3454 const range_type *childRangeImpl(const QModelIndex &) const
3455 {
3456 return nullptr;
3457 }
3458
3459 range_type *childRangeImpl(const QModelIndex &)
3460 {
3461 return nullptr;
3462 }
3463
3464 const range_type &childrenOf(const_row_ptr row) const
3465 {
3466 Q_ASSERT(!row);
3467 return *this->m_data.model();
3468 }
3469
3470 void resetParentInChildren(range_type *)
3471 {
3472 }
3473
3475 {
3476 bool result = true;
3477 int rowIndex = 0;
3478 for (const auto &row : *this->m_data.model()) {
3479 result &= this->autoConnectPropertiesInRow(row, rowIndex, {});
3480 ++rowIndex;
3481 }
3482 return result;
3483 }
3484
3485 template <typename LessThan>
3486 void sortImpl(const LessThan &lessThan)
3487 {
3488 this->sortSubRange(*this->m_data.model(), nullptr, lessThan);
3489 }
3490
3491 void prunePersistentIndexList(QModelIndexList &, typename Base::row_ptr) {}
3492
3493 QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value,
3494 int hits, Qt::MatchFlags flags) const
3495 {
3496 QModelIndexList result;
3497 const bool wrap = flags.testAnyFlag(Qt::MatchWrap);
3498 const bool allHits = (hits == -1);
3499 const int column = start.column();
3500 int from = start.row();
3501 int to = this->rowCount({});
3502 decltype(auto) siblings = *this->m_data.model();
3503
3504 for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) {
3505 auto it = QRangeModelDetails::pos(siblings, from);
3506 auto end = QRangeModelDetails::adl_end(siblings);
3507 for (int r = from; it != end && r < to && (allHits || result.size() < hits);
3508 ++it, ++r) {
3509 if (!QRangeModelDetails::isValid(*it))
3510 continue;
3511 const QModelIndex index = this->createIndex(r, column, nullptr);
3512 if (this->matchRow(*it, index, role, value, flags))
3513 result.append(index);
3514 }
3515 from = 0;
3516 to = start.row();
3517 }
3518
3519 return result;
3520 }
3521};
3522
3523QT_END_NAMESPACE
3524
3525#endif // Q_QDOC
3526
3527#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)
QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
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 *)
void prunePersistentIndexList(QModelIndexList &, typename Base::row_ptr)
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()
void sortImpl(const LessThan &lessThan)
auto makeEmptyRow(typename Base::row_ptr)
static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
void matchImplRecursive(const range_type &range, const_row_ptr parentPtr, int from, int to, int role, const QVariant &value, int hits, Qt::MatchFlags flags, int column, QModelIndexList &result) const
QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
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)
void prunePersistentIndexList(QModelIndexList &list, row_ptr expectedParent)
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
void sortImplRecursive(range_type &range, row_ptr parentRow, const LessThan &lessThan)
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()
void sortImpl(const LessThan &lessThan)
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)
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, typename C::InterfaceVersion, typename C::Sort, typename C::Match > MethodTemplates
QVariant data(const QModelIndex &index, int role) const
QRangeModelImplBase(QRangeModel *itemModel)
QModelIndexList persistentIndexList() const
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)
void sort(int column, Qt::SortOrder order)
bool removeRows(int row, int count, const QModelIndex &parent)
static Q_CORE_EXPORT bool matchValue(const QString &itemData, const QVariant &value, Qt::MatchFlags flags)
QModelIndex parent(const QModelIndex &child) const
void changePersistentIndex(const QModelIndex &from, const QModelIndex &to)
QAbstractItemModel & itemModel()
QModelIndex createIndex(int row, int column, const void *ptr=nullptr) const
QHash< int, QByteArray > roleNames() const
void interfaceVersion(int &version) 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)
static Qt::partial_ordering compareData(const QVariant &lhs, const QVariant &rhs, const QCollator *collator)
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
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role)
int rowCount(const QModelIndex &parent) const
Q_CORE_EXPORT int sortRole() 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
bool insertColumns(int column, int count, const QModelIndex &parent)
QVariant data(const QModelIndex &index, int role) const
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) 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()
void interfaceVersion(int &versionNumber) const
static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
QVariant readProperty(const QModelIndex &index, ItemType *gadget) const
void sort(int column, Qt::SortOrder order)
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)
void sortSubRange(range_type &range, row_ptr expectedParent, const LessThan &lessThan)
const Structure & that() const
static constexpr bool isPrimaryRole(int role)
void updateTarget(LHS *org, RHS &&copy) noexcept
static constexpr bool itemsAreQObjects
static constexpr bool dynamicColumns()
range_type * childRange(const QModelIndex &index)
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
QVariant readRole(const QModelIndex &index, int role, ItemType *gadget) const
static bool resetProperty(int property, ItemType *object)
bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
bool readAt(const QModelIndex &index, F &&reader) 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)
bool matchRow(const_row_reference row, const QModelIndex &index, int role, const QVariant &value, Qt::MatchFlags flags) const
static auto adl_end(C &&c) -> decltype(end(QRangeModelDetails::refTo(std::forward< C >(c))))
decltype(QRangeModelRowOptions< row_type >::flags(std::declval< const row_type & >())) hasRowFlags_test
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 constexpr bool hasRowFlags
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
decltype(Access::flags(std::declval< const Test & >())) hasFlags_test
static constexpr bool hasReadRole
static constexpr bool hasWriteRole
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 bool for_element_at(C &&container, std::size_t idx, Fn &&fn)
static constexpr bool hasMetaObject
decltype(std::declval< C & >().sort(std::declval< LessThan && >())) sortMember_test
static constexpr bool hasCollatedCompare
const QRangeModelImpl *const that
const QCollator *const collator
auto compare(const Item &lhs, const Item &rhs) const
static constexpr bool hasSortMember
auto operator()(const Item &lhs, const Item &rhs) const
Compare(const QRangeModelImpl *impl, int column, Qt::SortOrder order)
const Qt::SortOrder m_order
static std::optional< bool > compareInvalid(const Item &lhs, const Item &rhs)
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
bool operator()(const value_type &value) const
const QRangeModelImpl *const that
friend constexpr bool operator<(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
friend constexpr bool operator>(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept