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
95 template <typename T>
97
98 template <typename T>
99 static auto pointerTo(T&& t) {
100 using Type = q20::remove_cvref_t<T>;
101 if constexpr (is_any_of<Type, std::optional>())
102 return t ? std::addressof(*std::forward<T>(t)) : nullptr;
103 else if constexpr (std::is_pointer<Type>())
104 return t;
105 else if constexpr (is_smart_ptr<Type>())
106 return t.get();
107 else if constexpr (is_any_of<Type, std::reference_wrapper>())
108 return std::addressof(t.get());
109 else
110 return std::addressof(std::forward<T>(t));
111 }
112
113 template <typename T>
115 {
117 };
118 template <>
119 struct wrapped_helper<void>
120 {
121 using type = void;
122 };
123 template <typename T>
125
126 template <typename T>
129 >>;
130
131 template <typename T, typename = void>
133 template <typename T, std::size_t N>
134 struct tuple_like<std::array<T, N>> : std::false_type {};
135 template <typename T>
138 template <typename T>
139 [[maybe_unused]] static constexpr bool tuple_like_v = tuple_like<T>::value;
140
141 template <typename T, typename = void>
143 template <typename T, std::size_t N>
144 struct array_like<std::array<T, N>> : std::true_type {};
145 template <typename T, std::size_t N>
146 struct array_like<T[N]> : std::true_type {};
147 template <typename T>
148 [[maybe_unused]] static constexpr bool array_like_v = array_like<T>::value;
149
150 template <typename T, typename = void>
152 template <typename T>
155 template <typename T>
156 [[maybe_unused]] static constexpr bool has_metaobject_v = has_metaobject<T>::value;
157
158 template <typename T>
159 static constexpr bool isValid(const T &t) noexcept
160 {
161 if constexpr (std::is_array_v<T>)
162 return true;
163 else if constexpr (is_validatable<T>())
164 return bool(t);
165 else
166 return true;
167 }
168
169 template <typename T>
170 static decltype(auto) refTo(T&& t) {
171 Q_ASSERT(QRangeModelDetails::isValid(t));
172 // it's allowed to move only if the object holds unique ownership of the wrapped data
173 using Type = q20::remove_cvref_t<T>;
174 if constexpr (is_any_of<T, std::optional>())
175 return *std::forward<T>(t); // let std::optional resolve dereferencing
176 if constexpr (!is_wrapped<Type>() || is_any_unique_ptr<Type>())
177 return q23::forward_like<T>(*QRangeModelDetails::pointerTo(t));
178 else
179 return *QRangeModelDetails::pointerTo(t);
180 }
181
182 template <typename It>
183 auto key(It&& it) -> decltype(it.key()) { return std::forward<It>(it).key(); }
184 template <typename It>
185 auto key(It&& it) -> decltype((it->first)) { return std::forward<It>(it)->first; }
186
187 template <typename It>
188 auto value(It&& it) -> decltype(it.value()) { return std::forward<It>(it).value(); }
189 template <typename It>
190 auto value(It&& it) -> decltype((it->second)) { return std::forward<It>(it)->second; }
191
192 // use our own, ADL friendly versions of begin/end so that we can overload
193 // for pointers.
194 using std::begin;
195 using std::end;
196 template <typename C>
197 static auto adl_begin(C &&c) -> decltype(begin(QRangeModelDetails::refTo(std::forward<C>(c))))
198 { return begin(QRangeModelDetails::refTo(std::forward<C>(c))); }
199 template <typename C>
200 static auto adl_end(C &&c) -> decltype(end(QRangeModelDetails::refTo(std::forward<C>(c))))
201 { return end(QRangeModelDetails::refTo(std::forward<C>(c))); }
202 template <typename C>
203 static auto pos(C &&c, int i)
204 { return std::next(QRangeModelDetails::adl_begin(std::forward<C>(c)), i); }
205
206 // Test if a type is a range, and whether we can modify it using the
207 // standard C++ container member functions insert, erase, and resize.
208 // For the sake of QAIM, we cannot modify a range if it holds const data
209 // even if the range itself is not const; we'd need to initialize new rows
210 // and columns, and move old row and column data.
211 template <typename C, typename = void>
213
214 template <typename C>
215 struct test_insert<C, std::void_t<decltype(std::declval<C>().insert(
216 std::declval<typename C::const_iterator>(),
217 std::declval<typename C::size_type>(),
218 std::declval<typename C::value_type>()
219 ))>>
220 : std::true_type
221 {};
222
223 // Can we insert from another (identical) range? Required to support
224 // move-only types
225 template <typename C, typename = void>
227
228 template <typename C>
229 struct test_insert_range<C, std::void_t<decltype(std::declval<C&>().insert(
230 std::declval<typename C::const_iterator&>(),
231 std::declval<std::move_iterator<typename C::iterator>&>(),
232 std::declval<std::move_iterator<typename C::iterator>&>()
233 ))>>
234 : std::true_type
235 {};
236
237 template <typename C, typename = void>
239
240 template <typename C>
241 struct test_erase<C, std::void_t<decltype(std::declval<C>().erase(
242 std::declval<typename C::const_iterator>(),
243 std::declval<typename C::const_iterator>()
244 ))>>
245 : std::true_type
246 {};
247
248 template <typename C, typename = void>
250
251 template <typename C>
252 struct test_resize<C, std::void_t<decltype(std::declval<C>().resize(
253 std::declval<typename C::size_type>(),
254 std::declval<typename C::value_type>()
255 ))>>
256 : std::true_type
257 {};
258
259 // we use std::rotate in moveRows/Columns, which requires the values (which
260 // might be const if we only get a const iterator) to be swappable, and the
261 // iterator type to be at least a forward iterator
262 template <typename It>
263 using test_rotate = std::conjunction<
264 std::is_swappable<decltype(*std::declval<It>())>,
265 std::is_base_of<std::forward_iterator_tag,
266 typename std::iterator_traits<It>::iterator_category>
267 >;
268
269 template <typename C, typename = void>
271
272 template <typename C>
273 struct test_splice<C, std::void_t<decltype(std::declval<C>().splice(
274 std::declval<typename C::const_iterator>(),
275 std::declval<C&>(),
276 std::declval<typename C::const_iterator>(),
277 std::declval<typename C::const_iterator>()
278 ))>>
279 : std::true_type
280 {};
281
282 template <typename C>
283 static void rotate(C& c, int src, int count, int dst) {
284 auto& container = QRangeModelDetails::refTo(c);
285 using Container = std::remove_reference_t<decltype(container)>;
286
287 const auto srcBegin = QRangeModelDetails::pos(container, src);
288 const auto srcEnd = std::next(srcBegin, count);
289 const auto dstBegin = QRangeModelDetails::pos(container, dst);
290
291 if constexpr (test_splice<Container>::value) {
292 if (dst > src && dst < src + count) // dst must be out of the source range
293 container.splice(srcBegin, container, dstBegin, srcEnd);
294 else if (dst != src) // otherwise, std::list gets corrupted
295 container.splice(dstBegin, container, srcBegin, srcEnd);
296 } else {
297 if (src < dst) // moving right
298 std::rotate(srcBegin, srcEnd, dstBegin);
299 else // moving left
300 std::rotate(dstBegin, srcBegin, srcEnd);
301 }
302 }
303
304 // Test if a type is an associative container that we can use for multi-role
305 // data, i.e. has a key_type and a mapped_type typedef, and maps from int,
306 // Qt::ItemDataRole, or QString to QVariant. This excludes std::set (and
307 // unordered_set), which are not useful for us anyway even though they are
308 // considered associative containers.
309 template <typename C, typename = void> struct is_multi_role : std::false_type
310 {
311 static constexpr bool int_key = false;
312 };
313 template <typename C> // Qt::ItemDataRole -> QVariant, or QString -> QVariant, int -> QVariant
314 struct is_multi_role<C, std::void_t<typename C::key_type, typename C::mapped_type>>
315 : std::conjunction<std::disjunction<std::is_same<typename C::key_type, int>,
316 std::is_same<typename C::key_type, Qt::ItemDataRole>,
317 std::is_same<typename C::key_type, QString>>,
318 std::is_same<typename C::mapped_type, QVariant>>
319 {
320 static constexpr bool int_key = !std::is_same_v<typename C::key_type, QString>;
321 };
322 template <typename C>
323 [[maybe_unused]]
324 static constexpr bool is_multi_role_v = is_multi_role<C>::value;
325
326 using std::size;
327 template <typename C, typename = void>
329 template <typename C>
330 struct test_size<C, std::void_t<decltype(size(std::declval<C&>()))>> : std::true_type {};
331
332 template <typename C, typename = void>
334 template <typename C>
335 struct test_cbegin<C, std::void_t<decltype(QRangeModelDetails::adl_begin(std::declval<const C&>()))>>
336 : std::true_type
337 {};
338
339 template <typename C, typename = void>
341 static constexpr bool is_mutable = !std::is_const_v<C>;
342 static constexpr bool has_insert = false;
343 static constexpr bool has_insert_range = false;
344 static constexpr bool has_erase = false;
345 static constexpr bool has_resize = false;
346 static constexpr bool has_rotate = false;
347 static constexpr bool has_splice = false;
348 static constexpr bool has_cbegin = false;
349 };
350 template <typename C>
352 decltype(QRangeModelDetails::adl_end(std::declval<C&>())),
354 >> : std::true_type
355 {
358 static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
359 static constexpr bool has_insert = test_insert<C>();
360 static constexpr bool has_insert_range = test_insert_range<C>();
361 static constexpr bool has_erase = test_erase<C>();
362 static constexpr bool has_resize = test_resize<C>();
363 static constexpr bool has_rotate = test_rotate<iterator>();
364 static constexpr bool has_splice = test_splice<C>();
365 static constexpr bool has_cbegin = test_cbegin<C>::value;
366 };
367
368 // Specializations for types that look like ranges, but should be
369 // treated as values.
370 enum class Mutable { Yes, No };
371 template <Mutable IsMutable>
373 static constexpr bool is_mutable = IsMutable == Mutable::Yes;
374 static constexpr bool has_insert = false;
375 static constexpr bool has_erase = false;
376 static constexpr bool has_resize = false;
377 static constexpr bool has_rotate = false;
378 static constexpr bool has_splice = false;
379 static constexpr bool has_cbegin = true;
380 };
382 template <> struct range_traits<QString> : iterable_value<Mutable::Yes> {};
383 template <class CharT, class Traits, class Allocator>
386
387 // const T * and views are read-only
388 template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
390
391 template <typename C>
393 template <typename C>
394 [[maybe_unused]] static constexpr bool is_range_v = is_range<C>();
395
396 // Detect an ItemAccess specialization with static read/writeRole members
397 template <typename T> struct QRangeModelItemAccess;
398
399 template <typename T>
401 {
404
405 template <typename Access, typename Test>
406 using hasReadRole_test = decltype(Access::readRole(std::declval<const Test &>(),
407 Qt::DisplayRole));
408 static constexpr bool hasReadRole = qxp::is_detected_v<hasReadRole_test, ItemAccess, ItemType>;
409
410 template <typename Access, typename Test>
413 static constexpr bool hasWriteRole = qxp::is_detected_v<hasWriteRole_test, ItemAccess, ItemType>;
414
415 template <typename Access, typename Test>
416 using hasFlags_test = decltype(Access::flags(std::declval<const Test&>()));
417
418 static constexpr bool hasFlags = qxp::is_detected_v<hasFlags_test, ItemAccess, ItemType>;
419
420 template <typename Access, typename Test>
421 using hasMimeTypes_test = decltype(Access::mimeTypes());
422 static constexpr bool hasMimeTypes = qxp::is_detected_v<hasMimeTypes_test, ItemAccess, ItemType>;
423
424 template <typename Access, typename Test>
425 using hasMimeData_test = decltype(Access::mimeData(std::declval<QSpan<const Test>>()));
426 static constexpr bool hasMimeData = qxp::is_detected_v<hasMimeData_test, ItemAccess, ItemType>;
427
428 template <typename Access>
430 std::declval<const QMimeData *>()
431 ));
432 static constexpr bool hasCanDropMimeData = qxp::is_detected_v<hasCanDropMimeData_test, ItemAccess>;
433 template <typename Access, typename Test>
435 std::declval<const QMimeData *>(),
437 );
438 static constexpr bool hasDropMimeData = qxp::is_detected_v<hasDropMimeData_test,
439 ItemAccess, ItemType>;
440
441 // full versions with all parameters
442 template <typename Access>
444 std::declval<const QMimeData *>(), Qt::CopyAction, 0, 0, std::declval<const QModelIndex &>()
445 ));
447 ItemAccess>;
448 template <typename Access, typename Test>
450 std::declval<const QMimeData *>(),
451 Qt::CopyAction, 0, 0, std::declval<const QModelIndex &>(),
453 ));
454 static constexpr bool hasDropMimeDataFull = qxp::is_detected_v<hasDropMimeDataFull_test,
455 ItemAccess, ItemType>;
456 };
457
458 // Detect which options are set to override default heuristics. Since
459 // QRangeModel is not yet defined we need to delay the evaluation.
460 template <typename T> struct QRangeModelRowOptions;
461
462 template <typename T, typename = void>
464 {
466 };
467
468 template <typename T>
470 : std::true_type
471 {
473 using RowCategory = decltype(rowCategory);
475 };
476
477 template <typename RowOptions>
479 template <typename row_type>
481
482 template <typename row_type>
483 using hasRowFlags_test = decltype(QRangeModelRowOptions<row_type>::flags(std::declval<const row_type &>()));
484 template <typename row_type>
485 static constexpr bool hasRowFlags = qxp::is_detected_v<hasRowFlags_test, row_type>;
486
487 // drag'n'drop handling
488 template <typename row_type>
489 using hasMimeTypes_test = decltype(QRangeModelRowOptions<row_type>::mimeTypes());
490 template <typename row_type>
491 static constexpr bool hasMimeTypes = qxp::is_detected_v<hasMimeTypes_test, row_type>;
492 template <typename row_type>
494 std::declval<const QModelIndexList &>())
495 );
496 template <typename row_type>
497 static constexpr bool hasMimeDataIndexList = qxp::is_detected_v<hasMimeDataIndexList_test, row_type>;
498 template <typename row_type>
500 // we don't call it with a QSpan, but with a range type. QSpan is a close enough match.
501 std::declval<QSpan<const row_type>>())
502 );
503 template <typename row_type>
504 static constexpr bool hasMimeDataRowSpan = qxp::is_detected_v<hasMimeDataRowSpan_test, row_type>;
505
506 // we allow simplified versions of (can)DropMimeData
507 template <typename row_type>
509 std::declval<const QMimeData *>()
510 ));
511 template <typename row_type>
512 static constexpr bool hasCanDropMimeData = qxp::is_detected_v<hasCanDropMimeData_test, row_type>;
513 template <typename row_type>
515 std::declval<const QMimeData *>(),
517 ));
518 template <typename row_type>
519 static constexpr bool hasDropMimeData = qxp::is_detected_v<hasDropMimeData_test, row_type>;
520
521 // the full versions get all the parameters
522 template <typename row_type>
524 std::declval<const QMimeData *>(), Qt::CopyAction, 0, 0, std::declval<const QModelIndex &>()
525 ));
526 template <typename row_type>
528 row_type>;
529 template <typename row_type>
531 std::declval<const QMimeData *>(),
532 Qt::CopyAction, 0, 0, std::declval<const QModelIndex &>(),
534 ));
535 template <typename row_type>
537 row_type>;
538
539 // Find out how many fixed elements can be retrieved from a row element.
540 // main template for simple values and ranges. Specializing for ranges
541 // is ambiguous with arrays, as they are also ranges
542 template <typename T, typename = void>
543 struct row_traits {
544 static constexpr bool is_range = is_range_v<q20::remove_cvref_t<T>>
546 // A static size of -1 indicates dynamically sized range
547 // A static size of 0 indicates that the specified type doesn't
548 // represent static or dynamic range.
549 static constexpr int static_size = is_range ? -1 : 0;
551 static constexpr int fixed_size() { return 1; }
552 static constexpr bool hasMetaObject = false;
553
555 {
556 return {};
557 }
558
559 template <typename C, typename Fn>
560 static bool for_element_at(C &&container, std::size_t idx, Fn &&fn)
561 {
562 if constexpr (is_range)
563 return std::forward<Fn>(fn)(*QRangeModelDetails::pos(std::forward<C>(container), idx));
564 else
565 return std::forward<Fn>(fn)(std::forward<C>(container));
566 }
567
568 template <typename Fn>
569 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
570 {
571 if constexpr (static_size == 0) {
572 return std::forward<Fn>(fn)(firstIndex, QRangeModelDetails::pointerTo(row));
573 } else {
574 int columnIndex = -1;
575 return std::all_of(QRangeModelDetails::adl_begin(row),
576 QRangeModelDetails::adl_end(row), [&](const auto &item) {
577 return std::forward<Fn>(fn)(firstIndex.siblingAtColumn(++columnIndex),
578 QRangeModelDetails::pointerTo(item));
579 });
580 }
581 }
582 };
583
584 // Specialization for tuple-like semantics (prioritized over metaobject)
585 template <typename T>
587 {
588 static constexpr std::size_t size64 = std::tuple_size_v<T>;
589 static_assert(q20::in_range<int>(size64));
590 static constexpr int static_size = int(size64);
591
592 // are the types in a tuple all the same
593 template <std::size_t ...I>
594 static constexpr bool allSameTypes(std::index_sequence<I...>)
595 {
596 return (std::is_same_v<std::tuple_element_t<0, T>,
597 std::tuple_element_t<I, T>> && ...);
598 }
599
601 std::tuple_element_t<0, T>, void>;
602 static constexpr int fixed_size() { return 0; }
603 static constexpr bool hasMetaObject = false;
604
605 template <typename C, typename F>
607 {
609 constexpr size_t size = std::tuple_size_v<type>;
610 Q_ASSERT(idx < size);
611 return QtPrivate::applyIndexSwitch<size>(idx, [&](auto idxConstant) {
613 });
614 }
615
617 {
618 constexpr auto size = std::tuple_size_v<T>;
620
626 >();
627 if (metaType.isValid())
629 });
630 return result;
631 }
632
633 template <typename Fn, std::size_t ...Is>
634 static bool forEachTupleElement(const T &row, Fn &&fn, std::index_sequence<Is...>)
635 {
636 using std::get;
637 return (std::forward<Fn>(fn)(QRangeModelDetails::pointerTo(get<Is>(row))) && ...);
638 }
639
640 template <typename Fn>
641 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
642 {
643 int column = -1;
644 return forEachTupleElement(row, [&column, &fn, &firstIndex](const QObject *item){
647 }
648 };
649
650 // Specialization for C arrays and std::array
651 template <typename T, std::size_t N>
652 struct row_traits<std::array<T, N>>
653 {
654 static_assert(q20::in_range<int>(N));
655 static constexpr int static_size = int(N);
656 using item_type = T;
657 static constexpr int fixed_size() { return 0; }
658 static constexpr bool hasMetaObject = false;
659
660 template <typename C, typename F>
666
668 {
669 return section;
670 }
671
672 template <typename Fn>
673 static bool for_each_element(const std::array<T, N> &row, const QModelIndex &firstIndex, Fn &&fn)
674 {
675 int columnIndex = -1;
677 QRangeModelDetails::adl_end(row), [&](const auto &item) {
680 });
681 }
682 };
683
684 template <typename T, std::size_t N>
685 struct row_traits<T[N]> : row_traits<std::array<T, N>> {};
686
687 // prioritize tuple-like over metaobject
688 template <typename T>
690 {
691 static constexpr int static_size = 0;
693 static int fixed_size() {
694 if constexpr (row_category<T>::isMultiRole) {
695 return 1;
696 } else {
697 // Interpret a gadget in a list as a multi-column row item. To make
698 // a list of multi-role items, wrap it into SingleColumn.
699 static const int columnCount = []{
701 return mo.propertyCount() - mo.propertyOffset();
702 }();
703 return columnCount;
704 }
705 }
706
707 static constexpr bool hasMetaObject = true;
708
709 template <typename C, typename F>
711 {
712 return std::forward<F>(function)(std::forward<C>(container));
713 }
714
716 {
718 if (fixed_size() == 1) {
721 } else if (section <= fixed_size()) {
725 }
726 return result;
727 }
728
729 template <typename Fn>
730 static bool for_each_element(const T &row, const QModelIndex &firstIndex, Fn &&fn)
731 {
733 }
734 };
735
736 template <typename T, typename = void>
738 {
739 template <typename That>
740 static QHash<int, QByteArray> roleNames(That *)
741 {
742 return That::roleNamesForSimpleType();
743 }
744 };
745
746 template <>
747 struct item_traits<void>
748 {
749 template <typename That>
751 {
753 }
754 };
755
756 template <typename T>
761
762 template <typename T>
764 {
765 template <typename That>
770 };
771
772 template <typename T>
773 [[maybe_unused]] static constexpr int static_size_v =
775
776 template <typename Range>
778 {
780
781 template <typename R = row_type>
782 auto newRow() -> decltype(R{}) { return R{}; }
783 };
784
785 template <typename Range>
787 {
789
790 template <typename R = row_type,
795 >,
796 bool> = true>
797 auto newRow() -> decltype(R(new QRangeModelDetails::wrapped_t<R>)) {
798 if constexpr (is_any_of<R, std::shared_ptr>())
800 else
801 return R(new QRangeModelDetails::wrapped_t<R>);
802 }
803
804 template <typename R = row_type,
806 auto newRow() -> decltype(R{}) { return R{}; }
807
808 template <typename R = row_type,
810 auto deleteRow(R&& row) -> decltype(delete row) { delete row; }
811 };
812
813 template <typename Range,
817
818 // Default tree traversal protocol implementation for row types that have
819 // the respective member functions. The trailing return type implicitly
820 // removes those functions that are not available.
821 template <typename Range>
823 {
824 template <typename R /*wrapped_row_type*/>
825 auto parentRow(const R& row) const -> decltype(row.parentRow())
826 {
827 return row.parentRow();
828 }
829
830 template <typename R /* = wrapped_row_type*/>
831 auto setParentRow(R &row, R* parent) -> decltype(row.setParentRow(parent))
832 {
833 row.setParentRow(parent);
834 }
835
836 template <typename R /* = wrapped_row_type*/>
837 auto childRows(const R &row) const -> decltype(row.childRows())
838 {
839 return row.childRows();
840 }
841
842 template <typename R /* = wrapped_row_type*/>
843 auto childRows(R &row) -> decltype(row.childRows())
844 {
845 return row.childRows();
846 }
847 };
848
849 template <typename P, typename R>
850 using protocol_parentRow_test = decltype(std::declval<P&>()
852 template <typename P, typename R>
854
855 template <typename P, typename R>
856 using protocol_childRows_test = decltype(std::declval<P&>()
858 template <typename P, typename R>
860
861 template <typename P, typename R>
865 template <typename P, typename R>
867
868 template <typename P, typename R>
871 template <typename P, typename R>
873
874 template <typename P, typename = void>
876 template <typename P>
877 struct protocol_newRow<P, std::void_t<decltype(std::declval<P&>().newRow())>>
878 : std::true_type {};
879
880 template <typename P, typename R, typename = void>
882 template <typename P, typename R>
884 std::void_t<decltype(std::declval<P&>().deleteRow(std::declval<R&&>()))>>
885 : std::true_type {};
886
887 template <typename Range,
888 typename Protocol = DefaultTreeProtocol<Range>,
889 typename R = typename range_traits<Range>::value_type,
890 typename = void>
892
893 template <typename Range, typename Protocol, typename R>
898
899 template <typename Range>
903 >, bool>;
904
905 template <typename Range, typename Protocol = DefaultTreeProtocol<Range>>
910 >, bool>;
911
912 template <typename Range, typename Protocol>
930
931 // Helpers for drag'n'drop:
932 // MimeDataEntry gives customisations access to a pair of either a row or
933 // an item (in form of the underlying type, i.e. unwrapped, as that's what
934 // customizations specialize RowOptions and ItemAccess for), and the
935 // corresponding index, with decomposition support for easy iteration.
936 template <typename Entry>
938 {
941 template<typename T, typename = void>
943
944#ifndef Q_CC_MSVC // MSVC selects this even for non-constexpr types
945 template<typename T>
947#endif
948
949 bool isValid() const { return QRangeModelDetails::isValid(m_entry); }
950 const wrapped_entry &entry() const
951 {
952 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<entry_type>()) {
953 // While we mark null-items or indexes in null-rows as not draggable,
954 // client code might override that, or explicitly call QRM::mimeData()
955 // with indexes that point at null-rows or -items.
956 if (Q_UNLIKELY(!QRangeModelDetails::isValid(m_entry))) {
957#ifndef QT_NO_DEBUG
958 qDebug("QRangeModel::mimeData: null-entry, test with isValid before accessing");
959#endif
960 constexpr bool is_constexpr_default_constructible_v =
961 is_constexpr_default_constructible<wrapped_entry>::value;
962 if constexpr (is_constexpr_default_constructible_v) {
963 Q_CONSTINIT static const wrapped_entry emptyDefault;
964 return QRangeModelDetails::refTo(emptyDefault);
965 } else {
966 // known to cause runtime initialization
967 static const wrapped_entry emptyDefault;
968 return QRangeModelDetails::refTo(emptyDefault);
969 }
970 }
971 }
972 return std::as_const(QRangeModelDetails::refTo(m_entry));
973 }
974
975 const QModelIndex &index() const { return m_index; }
976
977 template <std::size_t N>
978 friend decltype(auto) get(const MimeDataEntry &entry)
979 {
980 if constexpr (N == 0)
981 return entry.entry();
982 else if constexpr (N == 1)
983 return entry.index();
984 }
985 const Entry &m_entry;
987 };
988} // namespace QRangeModelDetails
989
990QT_END_NAMESPACE
991
992// decomposition protocol
993namespace std {
994template <typename T>
997template <typename T>
1000template <typename T>
1003} // namespace QRangeModelDetails
1004
1005QT_BEGIN_NAMESPACE
1006
1007namespace QRangeModelDetails {
1008 // A helper type for drop-support. Client code populates a sequence of
1009 // dropped things via an insertion iterator, and those get wrapped in a
1010 // DroppedEntry, which allows user code to also specify the position of the
1011 // thing in the target model.
1012 template <typename Entry>
1014 {
1015 struct Cell {
1018
1019 // implicit conversion is intentional
1020 Q_IMPLICIT Cell() noexcept : m_row(-1), m_column(-1) {}
1021 Q_IMPLICIT Cell(int row, int column = 0) noexcept : m_row(row), m_column(column) {}
1022
1023 friend bool operator==(const Cell &lhs, const Cell &rhs) noexcept
1024 {
1025 return lhs.m_row == rhs.m_row && lhs.m_column == rhs.m_column;
1026 }
1027 };
1028
1029 // implicit conversion from and to entry is intentional
1036
1037 // we only move the actual data out
1038 operator Entry&&() && { return std::move(m_entry); }
1039
1040 Entry m_entry;
1042 };
1043
1060
1061 template <bool cacheProperties, bool itemsAreQObjects>
1063 static constexpr bool cachesProperties = false;
1064
1066 };
1067
1069 {
1070 static constexpr bool cachesProperties = true;
1072
1074 {
1075 properties.clear();
1076 }
1077 protected:
1078 ~PropertyCache() = default;
1079 };
1080
1081 template <>
1082 struct PropertyData<true, false> : PropertyCache
1083 {};
1084
1086 {
1087 struct Connection {
1089 int role;
1090
1091 friend bool operator==(const Connection &lhs, const Connection &rhs) noexcept
1092 {
1093 return lhs.sender == rhs.sender && lhs.role == rhs.role;
1094 }
1095 friend size_t qHash(const Connection &c, size_t seed) noexcept
1096 {
1097 return qHashMulti(seed, c.sender, c.role);
1098 }
1099 };
1100
1103
1104 protected:
1105 ~ConnectionStorage() = default;
1106 };
1107
1108 template <>
1110 {};
1111
1112 template <>
1113 struct PropertyData<false, true> : PropertyData<false, false>, ConnectionStorage
1114 {
1116 };
1117
1118 // The storage of the model data. We might store it as a pointer, or as a
1119 // (copied- or moved-into) value (or smart pointer). But we always return a
1120 // raw pointer.
1121 template <typename ModelStorage, typename = void>
1129
1130 template <typename ModelStorage>
1139
1140 template <typename ModelStorage, typename PropertyStorage>
1142 PropertyStorage
1143 {
1147
1148 auto model() { return QRangeModelDetails::pointerTo(this->m_model); }
1149 auto model() const { return QRangeModelDetails::pointerTo(this->m_model); }
1150
1151 template <typename Model = ModelStorage>
1152 ModelData(Model &&model)
1154 {}
1155 };
1156} // namespace QRangeModelDetails
1157
1158class QRangeModel;
1159// forward declare so that we can declare friends
1160template <typename, typename, typename> class QRangeModelAdapter;
1161
1163{
1164 using Self = QRangeModelImplBase;
1166
1167public:
1168 // keep in sync with QRangeModel::AutoConnectPolicy
1174
1175 // keep in sync with QRangeModel::DropOperation
1184
1185 // overridable prototypes (quasi-pure-virtual methods)
1187 bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
1188 bool setData(const QModelIndex &index, const QVariant &data, int role);
1189 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data);
1190 bool clearItemData(const QModelIndex &index);
1191 bool insertColumns(int column, int count, const QModelIndex &parent);
1192 bool removeColumns(int column, int count, const QModelIndex &parent);
1193 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn);
1194 bool insertRows(int row, int count, const QModelIndex &parent);
1195 bool removeRows(int row, int count, const QModelIndex &parent);
1196 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow);
1197
1198 QModelIndex index(int row, int column, const QModelIndex &parent) const;
1199 QModelIndex sibling(int row, int column, const QModelIndex &index) const;
1200 int rowCount(const QModelIndex &parent) const;
1201 int columnCount(const QModelIndex &parent) const;
1202 Qt::ItemFlags flags(const QModelIndex &index) const;
1203 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
1204 QVariant data(const QModelIndex &index, int role) const;
1205 QMap<int, QVariant> itemData(const QModelIndex &index) const;
1206 inline QHash<int, QByteArray> roleNames() const;
1207 QModelIndex parent(const QModelIndex &child) const;
1208
1209 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
1211
1212 void interfaceVersion(int &version) const;
1213 void sort(int column, Qt::SortOrder order);
1214 QModelIndexList match(const QModelIndex &start, int role, const QVariant &value,
1215 int hits, Qt::MatchFlags flags) const;
1216
1217 Qt::DropActions adjustSupportedDragActions(Qt::DropActions dragActions);
1218 Qt::DropActions adjustSupportedDropActions(Qt::DropActions dropActions);
1220 bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
1221 const QModelIndex &parent) const;
1222 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
1223 const QModelIndex &parent);
1224 QMimeData *mimeData(const QModelIndexList &indexes) const;
1225
1226 // bindings for overriding
1227
1239
1246 using Data = Method<&Self::data>;
1250
1251 // 6.11
1254
1255 // 6.12
1257 using Sort = Method<&Self::sort>;
1261
1266
1267 template <typename C>
1268 using MethodTemplates = std::tuple<
1269 typename C::Destroy,
1270 typename C::InvalidateCaches,
1271 typename C::SetHeaderData,
1272 typename C::SetData,
1273 typename C::SetItemData,
1274 typename C::ClearItemData,
1275 typename C::InsertColumns,
1276 typename C::RemoveColumns,
1277 typename C::MoveColumns,
1278 typename C::InsertRows,
1279 typename C::RemoveRows,
1280 typename C::MoveRows,
1281 typename C::Index,
1282 typename C::Parent,
1283 typename C::Sibling,
1284 typename C::RowCount,
1285 typename C::ColumnCount,
1286 typename C::Flags,
1287 typename C::HeaderData,
1288 typename C::Data,
1289 typename C::ItemData,
1290 typename C::RoleNames,
1291 typename C::MultiData,
1292 typename C::SetAutoConnectPolicy,
1293 typename C::InterfaceVersion,
1294 typename C::Sort,
1295 typename C::Match,
1296 typename C::AdjustSupportedDragActions,
1297 typename C::AdjustSupportedDropActions,
1298 typename C::MimeTypes,
1299 typename C::CanDropMimeData,
1300 typename C::DropMimeData,
1301 typename C::MimeData
1302 >;
1303
1304 static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
1305 static Q_CORE_EXPORT const QRangeModelImplBase *getImplementation(const QRangeModel *model);
1306
1307private:
1308 friend class QRangeModelPrivate;
1310
1311 QRangeModel *m_rangeModel;
1312
1313protected:
1314 explicit QRangeModelImplBase(QRangeModel *itemModel)
1315 : m_rangeModel(itemModel)
1316 {}
1317
1318 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
1319 inline QModelIndexList persistentIndexList() const;
1320 inline void changePersistentIndex(const QModelIndex &from, const QModelIndex &to);
1321 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
1322 const QList<int> &roles);
1323 inline void beginResetModel();
1324 inline void endResetModel();
1325 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
1326 inline void endInsertColumns();
1327 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
1328 inline void endRemoveColumns();
1329 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1330 const QModelIndex &destParent, int destRow);
1331 inline void endMoveColumns();
1332 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
1333 inline void endInsertRows();
1334 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
1335 inline void endRemoveRows();
1336 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1337 const QModelIndex &destParent, int destRow);
1338 inline void endMoveRows();
1339 inline void beginLayoutChange();
1340 inline void endLayoutChange();
1341 inline AutoConnectPolicy autoConnectPolicy() const;
1342 inline static Qt::partial_ordering compareData(const QVariant &lhs, const QVariant &rhs,
1343 const QCollator *collator);
1344
1345public:
1346 inline QAbstractItemModel &itemModel();
1347 inline const QAbstractItemModel &itemModel() const;
1348
1349 // implemented in qrangemodel.cpp
1351 const QMetaObject &metaObject);
1353
1354protected:
1355 Q_CORE_EXPORT QScopedValueRollback<bool> blockDataChangedDispatch();
1356
1358 const QMetaObject &metaObject);
1360 Q_CORE_EXPORT static bool connectProperty(const QModelIndex &index, const QObject *item,
1361 QRangeModelDetails::AutoConnectContext *context,
1362 int role, const QMetaProperty &property);
1363 Q_CORE_EXPORT static bool connectPropertyConst(const QModelIndex &index, const QObject *item,
1364 QRangeModelDetails::AutoConnectContext *context,
1365 int role, const QMetaProperty &property);
1366 Q_CORE_EXPORT static bool connectProperties(const QModelIndex &index, const QObject *item,
1367 QRangeModelDetails::AutoConnectContext *context,
1368 const QHash<int, QMetaProperty> &properties);
1369 Q_CORE_EXPORT static bool connectPropertiesConst(const QModelIndex &index, const QObject *item,
1370 QRangeModelDetails::AutoConnectContext *context,
1371 const QHash<int, QMetaProperty> &properties);
1372 Q_CORE_EXPORT int sortRole() const;
1373 Q_CORE_EXPORT const QCollator *sortCollator() const;
1375
1376 Q_CORE_EXPORT static QVariant convertMatchValue(const QVariant &value, Qt::MatchFlags flags);
1377 Q_CORE_EXPORT static bool matchValue(const QString &itemData, const QVariant &value,
1378 Qt::MatchFlags flags);
1379 Q_CORE_EXPORT static bool matchValue(const QString &itemData, const QVariant &value,
1380 Qt::MatchFlags flags, const QCollator &collator);
1381 static bool matchValue(const QVariant &itemData, const QVariant &value, Qt::MatchFlags flags,
1382 const QCollator &collator)
1383 {
1384 if ((flags & Qt::MatchTypeMask) == Qt::MatchExactly)
1385 return itemData == value;
1386 return matchValue(itemData.toString(), value, flags, collator);
1387 }
1388
1389 Q_CORE_EXPORT bool dropDataOnItem(const QMimeData *data, const QModelIndex &index);
1390};
1391
1392template <typename Structure, typename Range,
1393 typename Protocol = QRangeModelDetails::table_protocol_t<Range>>
1398{
1399public:
1409
1411 typename row_traits::item_type>>;
1413 && row_traits::hasMetaObject; // not treated as tuple
1414
1418 >,
1421 >
1422 >;
1424
1425 using const_row_reference = decltype(*std::declval<typename ModelData::const_iterator&>());
1426
1427 static_assert(!QRangeModelDetails::is_any_of<range_type, std::optional>() &&
1429 "Currently, std::optional is not supported for ranges and rows, as "
1430 "it has range semantics in c++26. Once the required behavior is clarified, "
1431 "std::optional for ranges and rows will be supported.");
1432
1433protected:
1434
1435 using Self = QRangeModelImpl<Structure, Range, Protocol>;
1437
1438 Structure& that() { return static_cast<Structure &>(*this); }
1439 const Structure& that() const { return static_cast<const Structure &>(*this); }
1440
1441 template <typename C>
1442 static constexpr int size(const C &c)
1443 {
1444 if (!QRangeModelDetails::isValid(c))
1445 return 0;
1446
1447 if constexpr (QRangeModelDetails::test_size<C>()) {
1448 using std::size;
1449 return int(size(c));
1450 } else {
1451#if defined(__cpp_lib_ranges)
1452 using std::ranges::distance;
1453#else
1454 using std::distance;
1455#endif
1456 using container_type = std::conditional_t<QRangeModelDetails::range_traits<C>::has_cbegin,
1457 const QRangeModelDetails::wrapped_t<C>,
1458 QRangeModelDetails::wrapped_t<C>>;
1459 container_type& container = const_cast<container_type &>(QRangeModelDetails::refTo(c));
1460 return int(distance(QRangeModelDetails::adl_begin(container),
1461 QRangeModelDetails::adl_end(container)));
1462 }
1463 }
1464
1467 static constexpr bool rows_are_owning_or_raw_pointers =
1470 static constexpr bool one_dimensional_range = static_column_count == 0;
1471
1473 {
1474 if constexpr (itemsAreQObjects || rowsAreQObjects)
1475 return this->blockDataChangedDispatch();
1476 else
1477 return false;
1478 }
1479
1480 // A row might be a value (or range of values), or a pointer.
1481 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
1484
1485 template <typename T>
1488
1489 // A iterator type to use as the input iterator with the
1490 // range_type::insert(pos, start, end) overload if available (it is in
1491 // std::vector, but not in QList). Generates a prvalue when dereferenced,
1492 // which then gets moved into the newly constructed row, which allows us to
1493 // implement insertRows() for move-only row types.
1495 {
1499 using iterator_category = std::input_iterator_tag;
1500 using difference_type = int;
1501
1502 value_type operator*() { return impl->makeEmptyRow(parentRow); }
1503 EmptyRowGenerator &operator++() { ++n; return *this; }
1504 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1505 { return lhs.n == rhs.n; }
1506 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1507 { return !(lhs == rhs); }
1508
1510 Structure *impl = nullptr;
1511 const row_ptr parentRow = nullptr;
1512 };
1513
1514 // If we have a move-only row_type and can add/remove rows, then the range
1515 // must have an insert-from-range overload.
1518 "The range holding a move-only row-type must support insert(pos, start, end)");
1519
1522
1523public:
1524 static constexpr bool isMutable()
1525 {
1526 return range_features::is_mutable && row_features::is_mutable
1527 && std::is_reference_v<row_reference>
1528 && Structure::is_mutable_impl;
1529 }
1530 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
1531 static constexpr bool dynamicColumns() { return static_column_count < 0; }
1532
1533 explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
1537 {
1538 }
1539
1540
1541 // static interface, called by QRangeModelImplBase
1542
1543 void interfaceVersion(int &versionNumber) const
1544 {
1545 versionNumber = QT_VERSION;
1546 }
1547
1548 void invalidateCaches() { m_data.invalidateCaches(); }
1549
1550 // Not implemented
1551 bool setHeaderData(int , Qt::Orientation , const QVariant &, int ) { return false; }
1552
1553 // actual implementations
1554 QModelIndex index(int row, int column, const QModelIndex &parent) const
1555 {
1556 if (row < 0 || column < 0 || column >= columnCount(parent)
1557 || row >= rowCount(parent)) {
1558 return {};
1559 }
1560
1561 return that().indexImpl(row, column, parent);
1562 }
1563
1564 QModelIndex sibling(int row, int column, const QModelIndex &index) const
1565 {
1566 if (row == index.row() && column == index.column())
1567 return index;
1568
1569 // we use indexes at column -1 in drag'n'drop handling to mark full rows
1570 if (column >= this->columnCount({}))
1571 return {};
1572
1573 if (row == index.row())
1574 return this->createIndex(row, column, index.constInternalPointer());
1575
1576 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1577 const auto siblingCount = size(that().childrenOf(parentRow));
1578 if (row < 0 || row >= int(siblingCount))
1579 return {};
1580 return this->createIndex(row, column, parentRow);
1581 }
1582
1583 Qt::ItemFlags flags(const QModelIndex &index) const
1584 {
1585 if (!index.isValid()) {
1586 if constexpr (isMutable())
1587 return Qt::ItemIsDropEnabled;
1588 else
1589 return Qt::NoItemFlags;
1590 }
1591
1592 // try customization
1593 std::optional<Qt::ItemFlags> customFlags;
1594 if constexpr (QRangeModelDetails::hasRowFlags<wrapped_row_type>) {
1595 const_row_reference row = rowData(index);
1596 if (QRangeModelDetails::isValid(row)) {
1597 customFlags = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::flags(
1598 QRangeModelDetails::refTo(row)
1599 );
1600 }
1601 }
1602
1603 readAt(index, [&customFlags](auto &&ref){
1604 Q_UNUSED(ref);
1605 using wrapped_value_type = q20::remove_cvref_t<QRangeModelDetails::wrapped_t<decltype(ref)>>;
1606 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasFlags) {
1607 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1608 if (QRangeModelDetails::isValid(ref)) {
1609 customFlags = ItemAccess::flags(QRangeModelDetails::refTo(ref));
1610 return true;
1611 }
1612 }
1613 return false;
1614 });
1615
1616 Qt::ItemFlags f = customFlags ? *customFlags : Structure::defaultFlags();
1617 // adjust custom flags based on what is not possible
1618 if constexpr (!isMutable())
1619 f &= ~(Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
1620 if (index.column())
1621 f |= Qt::ItemNeverHasChildren;
1622 if (customFlags)
1623 return f;
1624
1625 // compute flags ourselves
1626 if (!this->itemModel().mimeTypes().isEmpty()) {
1627 f |= Qt::ItemIsDragEnabled;
1628 if constexpr (isMutable())
1629 f |= Qt::ItemIsDropEnabled;
1630 }
1631
1632 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<row_type>()) {
1633 // pointer rows might be null
1634 const_row_reference row = rowData(index);
1635 if (!QRangeModelDetails::isValid(row))
1636 f &= ~Qt::ItemIsDragEnabled;
1637 }
1638
1639 if constexpr (isMutable()) {
1640 // Note: Read-only items are still droppable - we can't know here
1641 // whether the model will insert data as new rows or children, or if
1642 // it will overwrite the data of the dropped-on item. So we allow
1643 // dropping on items that are not editable.
1644 if constexpr (row_traits::hasMetaObject) {
1645 if (index.column() < row_traits::fixed_size()) {
1646 const QMetaObject mo = wrapped_row_type::staticMetaObject;
1647 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
1648 if (prop.isWritable())
1649 f |= Qt::ItemIsEditable;
1650 }
1651 } else if constexpr (static_column_count <= 0) {
1652 using item_type = typename row_traits::item_type;
1653 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<item_type>()) {
1654 // pointer items might be null
1655 if (!readAt(index, [](auto &&i){ return QRangeModelDetails::isValid(i); }))
1656 f &= ~Qt::ItemIsDragEnabled;
1657 }
1658 f |= Qt::ItemIsEditable;
1659 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
1660 // we want to know if the elements in the tuple are const; they'd always be, if
1661 // we didn't remove the const of the range first.
1662 const_row_reference row = rowData(index);
1663 row_reference mutableRow = const_cast<row_reference>(row);
1664 if (QRangeModelDetails::isValid(mutableRow)) {
1665 row_traits::for_element_at(mutableRow, index.column(), [&f](auto &&ref){
1666 using target_type = decltype(ref);
1667 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<target_type>()) {
1668 // pointer items might be null
1669 if (!QRangeModelDetails::isValid(ref))
1670 f &= ~Qt::ItemIsDragEnabled;
1671 }
1672 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
1673 f &= ~Qt::ItemIsEditable;
1674 else if constexpr (std::is_lvalue_reference_v<target_type>)
1675 f |= Qt::ItemIsEditable;
1676 });
1677 } else {
1678 // If there's no usable value stored in the row, then we can't
1679 // do anything with this item, except perhaps drop data into it
1680 f &= ~Qt::ItemIsEditable;
1681 }
1682 }
1683 }
1684 return f;
1685 }
1686
1687 QVariant headerData(int section, Qt::Orientation orientation, int role) const
1688 {
1689 QVariant result;
1690 if constexpr (QRangeModelDetails::hasHeaderData<wrapped_row_type>) {
1691 if (orientation == Qt::Horizontal) {
1692 result = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::headerData(
1693 section, role
1694 );
1695 if (result.isValid())
1696 return result;
1697 }
1698 }
1699
1700 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
1701 || section < 0 || section >= columnCount({})) {
1702 return this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1703 }
1704
1705 result = row_traits::column_name(section);
1706 if (!result.isValid())
1707 result = this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1708 return result;
1709 }
1710
1711 QVariant data(const QModelIndex &index, int role) const
1712 {
1713 if (!index.isValid())
1714 return {};
1715
1716 QModelRoleData result(role);
1717 multiData(index, result);
1718 return std::move(result.data());
1719 }
1720
1721 static constexpr bool isRangeModelRole(int role)
1722 {
1723 return role == Qt::RangeModelDataRole
1724 || role == Qt::RangeModelAdapterRole;
1725 }
1726
1727 static constexpr bool isPrimaryRole(int role)
1728 {
1729 return role == Qt::DisplayRole || role == Qt::EditRole;
1730 }
1731
1732 QMap<int, QVariant> itemData(const QModelIndex &index) const
1733 {
1734 QMap<int, QVariant> result;
1735
1736 if (index.isValid()) {
1737 // optimisation for items backed by a QMap<int, QVariant> or equivalent
1738 if (!readAt(index, [&result](const auto &value) {
1739 if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) {
1740 result = value;
1741 return true;
1742 }
1743 return false;
1744 })) {
1745 const auto roles = this->itemModel().roleNames().keys();
1746 QVarLengthArray<QModelRoleData, 16> roleDataArray;
1747 roleDataArray.reserve(roles.size());
1748 for (auto role : roles) {
1749 if (isRangeModelRole(role))
1750 continue;
1751 roleDataArray.emplace_back(role);
1752 }
1753 QModelRoleDataSpan roleDataSpan(roleDataArray);
1754 multiData(index, roleDataSpan);
1755
1756 for (QModelRoleData &roleData : roleDataSpan) {
1757 if (roleData.data().isValid())
1758 result[roleData.role()] = std::move(roleData.data());
1759 }
1760 }
1761 }
1762 return result;
1763 }
1764
1766 {
1767 template <typename value_type>
1768 bool operator()(const value_type &value) const
1769 {
1770 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1771 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1772
1773 const auto readModelData = [&value](QModelRoleData &roleData){
1774 if (!QRangeModelDetails::isValid(value)) {
1775 roleData.clearData();
1776 return true;
1777 }
1778
1779 const int role = roleData.role();
1780 if (role == Qt::RangeModelDataRole) {
1781 // Qt QML support: "modelData" role returns the entire multi-role item.
1782 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1783 // only by value (so we take the reference).
1784 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1785 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1786 else
1787 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1788 } else if (role == Qt::RangeModelAdapterRole) {
1789 // for QRangeModelAdapter however, we want to respect smart pointer wrappers
1790 if constexpr (std::is_copy_assignable_v<value_type>)
1791 roleData.setData(QVariant::fromValue(value));
1792 else
1793 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1794 } else {
1795 return false;
1796 }
1797 return true;
1798 };
1799
1800 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasReadRole) {
1801 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1802 for (auto &roleData : roleDataSpan) {
1803 if (!readModelData(roleData)) {
1804 roleData.setData(ItemAccess::readRole(QRangeModelDetails::refTo(value),
1805 roleData.role()));
1806 }
1807 }
1808 } else if constexpr (multi_role()) {
1809 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1810 Q_UNUSED(this);
1811 if constexpr (!multi_role::int_key)
1812 return that->itemModel().roleNames();
1813 else
1814 return {};
1815 }();
1816 using key_type = typename value_type::key_type;
1817 for (auto &roleData : roleDataSpan) {
1818 const auto &it = [&roleNames, &value, role = roleData.role()]{
1819 Q_UNUSED(roleNames);
1820 if constexpr (multi_role::int_key)
1821 return value.find(key_type(role));
1822 else
1823 return value.find(roleNames.value(role));
1824 }();
1825 if (it != QRangeModelDetails::adl_end(value))
1826 roleData.setData(QRangeModelDetails::value(it));
1827 else
1828 roleData.clearData();
1829 }
1830 } else if constexpr (has_metaobject<value_type>) {
1831 if (row_traits::fixed_size() <= 1) {
1832 for (auto &roleData : roleDataSpan) {
1833 if (!readModelData(roleData)) {
1834 roleData.setData(that->readRole(index, roleData.role(),
1835 QRangeModelDetails::pointerTo(value)));
1836 }
1837 }
1838 } else if (index.column() <= row_traits::fixed_size()) {
1839 for (auto &roleData : roleDataSpan) {
1840 const int role = roleData.role();
1841 if (isPrimaryRole(role)) {
1842 roleData.setData(that->readProperty(index,
1843 QRangeModelDetails::pointerTo(value)));
1844 } else {
1845 roleData.clearData();
1846 }
1847 }
1848 }
1849 } else {
1850 for (auto &roleData : roleDataSpan) {
1851 const int role = roleData.role();
1852 if (isPrimaryRole(role) || isRangeModelRole(role))
1853 roleData.setData(read(value));
1854 else
1855 roleData.clearData();
1856 }
1857 }
1858 return true;
1859 }
1860
1863 const QRangeModelImpl * const that;
1864 };
1865
1866 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1867 {
1868 if (!readAt(index, ItemReader{index, roleDataSpan, this})) {
1869 for (auto &roleData : roleDataSpan)
1870 roleData.clearData();
1871 }
1872 }
1873
1874 bool setData(const QModelIndex &index, const QVariant &data, int role)
1875 {
1876 if (!index.isValid())
1877 return false;
1878
1879 if constexpr (isMutable()) {
1880 auto emitDataChanged = qScopeGuard([this, &index, role]{
1881 Q_EMIT this->dataChanged(index, index,
1882 role == Qt::EditRole || role == Qt::RangeModelDataRole
1883 || role == Qt::RangeModelAdapterRole
1884 ? QList<int>{} : QList<int>{role});
1885 });
1886 // we emit dataChanged at the end, block dispatches from auto-connected properties
1887 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1888
1889 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1890 using value_type = q20::remove_cvref_t<decltype(target)>;
1891 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1892 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1893
1894 if constexpr (std::conjunction_v<QRangeModelDetails::is_any_owning_ptr<value_type>,
1895 std::is_default_constructible<wrapped_value_type>>) {
1896 if (!QRangeModelDetails::isValid(target))
1897 target.reset(new wrapped_value_type);
1898 }
1899
1900 auto setRangeModelDataRole = [&target, &data]{
1901 constexpr auto targetMetaType = QMetaType::fromType<value_type>();
1902 const auto dataMetaType = data.metaType();
1903 constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
1904 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
1905 // we don't support replacing objects that are stored as raw pointers,
1906 // as this makes object ownership very messy. But we can replace objects
1907 // stored in smart pointers, and we can initialize raw nullptr objects.
1908 if constexpr (isWrapped) {
1909 constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
1910 if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
1911 if (data.canConvert(targetMetaType)) {
1912 target = data.value<value_type>();
1913 return true;
1914 }
1915 } else if constexpr (is_raw_pointer) {
1916 if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
1917 target = data.value<value_type>();
1918 return true;
1919 }
1920 } else {
1921 Q_UNUSED(target);
1922 }
1923 }
1924 // Otherwise we have a move-only or polymorph type. fall through to
1925 // error handling.
1926 } else if constexpr (isWrapped) {
1927 if (QRangeModelDetails::isValid(target)) {
1928 auto &targetRef = QRangeModelDetails::refTo(target);
1929 // we need to get a wrapped value type out of the QVariant, which
1930 // might carry a pointer. We have to try all alternatives.
1931 if (const auto mt = QMetaType::fromType<wrapped_value_type>();
1932 data.canConvert(mt)) {
1933 targetRef = data.value<wrapped_value_type>();
1934 return true;
1935 } else if (const auto mtp = QMetaType::fromType<wrapped_value_type *>();
1936 data.canConvert(mtp)) {
1937 targetRef = *data.value<wrapped_value_type *>();
1938 return true;
1939 }
1940 }
1941 } else if (targetMetaType == dataMetaType) {
1942 QRangeModelDetails::refTo(target) = data.value<value_type>();
1943 return true;
1944 } else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1945 QRangeModelDetails::refTo(target) = *data.value<value_type *>();
1946 return true;
1947 }
1948#ifndef QT_NO_DEBUG
1949 qCritical("Not able to assign %s to %s",
1950 qPrintable(QDebug::toString(data)), targetMetaType.name());
1951#endif
1952 return false;
1953 };
1954
1955 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasWriteRole) {
1956 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1957 if (isRangeModelRole(role))
1958 return setRangeModelDataRole();
1959 return ItemAccess::writeRole(QRangeModelDetails::refTo(target), data, role);
1960 } else if constexpr (has_metaobject<value_type>) {
1961 if (row_traits::fixed_size() <= 1) { // multi-role value
1962 if (isRangeModelRole(role))
1963 return setRangeModelDataRole();
1964 return writeRole(role, QRangeModelDetails::pointerTo(target), data);
1965 } else if (column <= row_traits::fixed_size() // multi-column
1966 && (isPrimaryRole(role) || isRangeModelRole(role))) {
1967 return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
1968 }
1969 } else if constexpr (multi_role::value) {
1970 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1971 // If there is an entry for EditRole, overwrite that; otherwise,
1972 // set the entry for DisplayRole.
1973 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1974 Q_UNUSED(this);
1975 if constexpr (!multi_role::int_key)
1976 return this->itemModel().roleNames();
1977 else
1978 return {};
1979 }();
1980 if (role == Qt::EditRole) {
1981 if constexpr (multi_role::int_key) {
1982 if (target.find(roleToSet) == target.end())
1983 roleToSet = Qt::DisplayRole;
1984 } else {
1985 if (target.find(roleNames.value(roleToSet)) == target.end())
1986 roleToSet = Qt::DisplayRole;
1987 }
1988 }
1989 if constexpr (multi_role::int_key)
1990 return write(target[roleToSet], data);
1991 else
1992 return write(target[roleNames.value(roleToSet)], data);
1993 } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
1994 return write(target, data);
1995 }
1996 return false;
1997 };
1998
1999 if (!writeAt(index, writeData)) {
2000 emitDataChanged.dismiss();
2001 return false;
2002 } else if constexpr (itemsAreQObjects || rowsAreQObjects) {
2003 if (isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2004 if (QObject *item = data.value<QObject *>())
2005 Self::connectProperties(index, item, m_data.context, m_data.properties);
2006 }
2007 }
2008 return true;
2009 }
2010 return false;
2011 }
2012
2013 template <typename LHS, typename RHS>
2014 void updateTarget(LHS &org, RHS &&copy) noexcept
2015 {
2016 if constexpr (std::is_pointer_v<RHS>)
2017 return;
2018 else if constexpr (std::is_assignable_v<LHS, RHS>)
2019 org = std::forward<RHS>(copy);
2020 else
2021 qSwap(org, copy);
2022 }
2023 template <typename LHS, typename RHS>
2024 void updateTarget(LHS *org, RHS &&copy) noexcept
2025 {
2026 updateTarget(*org, std::forward<RHS>(copy));
2027 }
2028
2029 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
2030 {
2031 if (!index.isValid() || data.isEmpty())
2032 return false;
2033
2034 if constexpr (isMutable()) {
2035 auto emitDataChanged = qScopeGuard([this, &index, &data]{
2036 Q_EMIT this->dataChanged(index, index, data.keys());
2037 });
2038 // we emit dataChanged at the end, block dispatches from auto-connected properties
2039 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
2040
2041 bool tried = false;
2042 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
2043 Q_UNUSED(this);
2044 using value_type = q20::remove_cvref_t<decltype(target)>;
2045 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
2046 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
2047
2048 // transactional: if possible, modify a copy and only
2049 // update target if all values from data could be stored.
2050 auto makeCopy = [](const value_type &original){
2051 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>)
2052 return QRangeModelDetails::pointerTo(original); // no transaction support
2053 else if constexpr (std::is_pointer_v<decltype(original)>)
2054 return *original;
2055 else if constexpr (std::is_copy_assignable_v<value_type>)
2056 return original;
2057 else
2058 return QRangeModelDetails::pointerTo(original);
2059 };
2060
2061 const auto roleNames = this->itemModel().roleNames();
2062
2063 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasWriteRole) {
2064 tried = true;
2065 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
2066 const auto roles = roleNames.keys();
2067 auto targetCopy = makeCopy(target);
2068 for (int role : roles) {
2069 if (!ItemAccess::writeRole(QRangeModelDetails::refTo(targetCopy),
2070 data.value(role), role)) {
2071 return false;
2072 }
2073 }
2074 updateTarget(target, std::move(targetCopy));
2075 return true;
2076 } else if constexpr (multi_role()) {
2077 using key_type = typename value_type::key_type;
2078 tried = true;
2079 const auto roleName = [&roleNames](int role) {
2080 return roleNames.value(role);
2081 };
2082
2083 // transactional: only update target if all values from data
2084 // can be stored. Storing never fails with int-keys.
2085 if constexpr (!multi_role::int_key)
2086 {
2087 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
2088 [&roleName](int role) { return roleName(role).isEmpty(); }
2089 );
2090
2091 if (invalid != data.keyEnd()) {
2092#ifndef QT_NO_DEBUG
2093 qWarning("No role name set for %d", *invalid);
2094#endif
2095 return false;
2096 }
2097 }
2098
2099 for (auto &&[role, value] : data.asKeyValueRange()) {
2100 if constexpr (multi_role::int_key)
2101 target[static_cast<key_type>(role)] = value;
2102 else
2103 target[QString::fromUtf8(roleName(role))] = value;
2104 }
2105 return true;
2106 } else if constexpr (has_metaobject<value_type>) {
2107 if (row_traits::fixed_size() <= 1) {
2108 tried = true;
2109 auto targetCopy = makeCopy(target);
2110 for (auto &&[role, value] : data.asKeyValueRange()) {
2111 if (isRangeModelRole(role))
2112 continue;
2113 if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
2114 const QByteArray roleName = roleNames.value(role);
2115#ifndef QT_NO_DEBUG
2116 qWarning("Failed to write value '%s' to role '%s'",
2117 qPrintable(QDebug::toString(value)), roleName.data());
2118#endif
2119 return false;
2120 }
2121 }
2122 updateTarget(target, std::move(targetCopy));
2123 return true;
2124 }
2125 }
2126 return false;
2127 };
2128
2129 if (!writeAt(index, writeItemData)) {
2130 emitDataChanged.dismiss();
2131 if (!tried)
2132 return this->itemModel().QAbstractItemModel::setItemData(index, data);
2133 }
2134 return true;
2135 }
2136 return false;
2137 }
2138
2139 bool clearItemData(const QModelIndex &index)
2140 {
2141 if (!index.isValid())
2142 return false;
2143
2144 if constexpr (isMutable()) {
2145 auto emitDataChanged = qScopeGuard([this, &index]{
2146 Q_EMIT this->dataChanged(index, index, {});
2147 });
2148
2149 auto clearData = [column = index.column()](auto &&target) {
2150 if constexpr (row_traits::hasMetaObject) {
2151 if (row_traits::fixed_size() <= 1) {
2152 // multi-role object/gadget: reset all properties
2153 return resetProperty(-1, QRangeModelDetails::pointerTo(target));
2154 } else if (column <= row_traits::fixed_size()) {
2155 return resetProperty(column, QRangeModelDetails::pointerTo(target));
2156 }
2157 } else { // normal structs, values, associative containers
2158 target = {};
2159 return true;
2160 }
2161 return false;
2162 };
2163
2164 if (!writeAt(index, clearData)) {
2165 emitDataChanged.dismiss();
2166 return false;
2167 }
2168 return true;
2169 }
2170 return false;
2171 }
2172
2174 {
2175 // will be 'void' if columns don't all have the same type
2176 using item_type = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
2177 using item_traits = typename QRangeModelDetails::item_traits<item_type>;
2178 return item_traits::roleNames(this);
2179 }
2180
2181 bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
2182 {
2183 if (!QRangeModelDetails::isValid(row))
2184 return true; // nothing to do
2185 return row_traits::for_each_element(QRangeModelDetails::refTo(row),
2186 this->itemModel().index(rowIndex, 0, parent),
2187 [this](const QModelIndex &index, const QObject *item) {
2188 if constexpr (isMutable())
2189 return Self::connectProperties(index, item, m_data.context, m_data.properties);
2190 else
2191 return Self::connectPropertiesConst(index, item, m_data.context, m_data.properties);
2192 });
2193 }
2194
2195 void clearConnectionInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
2196 {
2197 if (!QRangeModelDetails::isValid(row))
2198 return;
2199 row_traits::for_each_element(QRangeModelDetails::refTo(row),
2200 this->itemModel().index(rowIndex, 0, parent),
2201 [this](const QModelIndex &, const QObject *item) {
2202 m_data.connections.removeIf([item](const auto &connection) {
2203 return connection.sender == item;
2204 });
2205 return true;
2206 });
2207 }
2208
2210 {
2211 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2212 using item_type = std::remove_pointer_t<typename row_traits::item_type>;
2213 using Mapping = QRangeModelDetails::AutoConnectContext::AutoConnectMapping;
2214
2215 delete m_data.context;
2216 m_data.connections = {};
2217 switch (this->autoConnectPolicy()) {
2218 case AutoConnectPolicy::None:
2219 m_data.context = nullptr;
2220 break;
2221 case AutoConnectPolicy::Full:
2222 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
2223 if constexpr (itemsAreQObjects) {
2224 m_data.properties = QRangeModelImplBase::roleProperties(this->itemModel(),
2225 item_type::staticMetaObject);
2226 m_data.context->mapping = Mapping::Roles;
2227 } else {
2228 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
2229 m_data.context->mapping = Mapping::Columns;
2230 }
2231 if (!m_data.properties.isEmpty())
2232 that().autoConnectPropertiesImpl();
2233 break;
2234 case AutoConnectPolicy::OnRead:
2235 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
2236 if constexpr (itemsAreQObjects) {
2237 m_data.context->mapping = Mapping::Roles;
2238 } else {
2239 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
2240 m_data.context->mapping = Mapping::Columns;
2241 }
2242 break;
2243 }
2244 } else {
2245#ifndef QT_NO_DEBUG
2246 qWarning("All items in the range must be QObject subclasses");
2247#endif
2248 }
2249 }
2250
2251 struct unordered {
2252 friend constexpr bool operator<(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
2253 { return false; }
2254 friend constexpr bool operator>(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
2255 { return false; }
2256 };
2257
2258 struct Compare
2259 {
2260 template <typename C, typename LessThan>
2261 using sortMember_test = decltype(std::declval<C&>().sort(std::declval<LessThan &&>()));
2262 static constexpr bool hasSortMember = qxp::is_detected_v<sortMember_test, range_type, Compare>;
2263
2264 template <typename Stringish>
2265 using collatedCompare_test = decltype(
2266 std::declval<const QCollator&>().compare(std::declval<const Stringish&>(),
2267 std::declval<const Stringish&>())
2268 );
2269 template <typename Stringish>
2271 Stringish>;
2272
2273
2274 Compare(const QRangeModelImpl *impl, int column, Qt::SortOrder order)
2275 : that(impl), m_index(impl->createIndex(-1, column, nullptr))
2276 , collator(impl->sortCollator()), m_order(order), m_sortRole(impl->sortRole())
2277 {
2278 }
2279
2280 template <typename Item>
2281 auto operator()(const Item &lhs, const Item &rhs) const
2282 {
2283 auto ordering = compare(lhs, rhs);
2284 return m_order == Qt::AscendingOrder ? ordering < 0 : ordering > 0;
2285 }
2286
2287 template <typename Item>
2288 auto compare(const Item &lhs, const Item &rhs) const
2289 {
2290 using value_type = QRangeModelDetails::wrapped_t<Item>;
2291 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
2292
2293 if constexpr (QRangeModelDetails::item_access<value_type>::hasReadRole
2294 || multi_role() || has_metaobject<value_type>) {
2295 QModelRoleData result(m_sortRole);
2296 // Minor abuse of QModelIndex: the reader needs an index to implement
2297 // lazy auto-connections, but we only have a column. So we construct
2298 // an invalid QModelIndex that carries only that column value. That's
2299 // enough for reading values, and the auto-connection logic skips for
2300 // invalid indexes.
2301 ItemReader reader{m_index, result, that};
2302 Q_ASSERT(!reader.index.isValid());
2303 reader(lhs);
2304 const QVariant lhsVariant = std::move(result.data());
2305 reader(rhs);
2306 const QVariant rhsVariant = std::move(result.data());
2307 return QRangeModelImplBase::compareData(lhsVariant, rhsVariant, collator);
2308 } else if constexpr (std::is_same_v<QVariant, value_type>) {
2309 return QRangeModelImplBase::compareData(lhs, rhs, collator);
2310 } else if constexpr (QtOrderingPrivate::CompareThreeWayTester::hasCompareThreeWay_v
2311 <value_type, value_type>) {
2312 // all types supported by QCollator are also three-way comparable
2313 if constexpr (hasCollatedCompare<value_type>) {
2314 if (collator) {
2315 using ordering = decltype(qCompareThreeWay(lhs, rhs));
2316 int res = collator->compare(lhs, rhs);
2317 if (res < 0)
2318 return ordering::less;
2319 if (res > 0)
2320 return ordering::greater;
2321 return ordering::equal;
2322 }
2323 }
2324 return qCompareThreeWay(lhs, rhs);
2325 } else {
2326 return unordered{};
2327 }
2328 }
2329
2330 bool checkComparable() const
2331 {
2332 return that->readAt(that->index(0, 0, {}), [this](const auto &item){
2333 // before we call std::stable_sort, check that we can compare the
2334 // types we'll ultimately get called with. This doesn't catch cases
2335 // where we end up comparing QVariant, and we cannot make this a
2336 // compile time check as long as readAt etc are not constexpr.
2337 using ordering = decltype(compare(item, item));
2338 if constexpr (std::is_same_v<ordering, unordered>) {
2339#ifndef QT_NO_DEBUG
2340 const QMetaType itemtype = QMetaType::fromType<QRangeModelDetails::wrapped_t<
2341 q20::remove_cvref_t<decltype(item)>>
2342 >();
2343 qCritical("QRangeModel: Cannot compare items of type %s in column %d!",
2344 itemtype.name(), m_index.column());
2345#else
2346 Q_UNUSED(this);
2347#endif
2348 return false;
2349 } else {
2350 return true;
2351 }
2352 });
2353 }
2354
2355 template <typename Item>
2356 static std::optional<bool> compareInvalid(const Item &lhs, const Item &rhs)
2357 {
2358 // invalid data > valid data
2359 if (!QRangeModelDetails::isValid(lhs))
2360 return false;
2361 if (!QRangeModelDetails::isValid(rhs))
2362 return true;
2363 return std::nullopt;
2364 }
2365
2366 const QRangeModelImpl * const that;
2368 const QCollator * const collator;
2370 const int m_sortRole;
2371 };
2372
2373 void sort(int column, Qt::SortOrder order)
2374 {
2375 if constexpr (isMutable() && std::is_swappable_v<row_type>) {
2376 if (rowCount({}) < 2 || column >= columnCount({}))
2377 return;
2378 Compare compare(this, column, order);
2379 if (!compare.checkComparable())
2380 return;
2381
2382 this->beginLayoutChange();
2383 QScopeGuard endLayoutChange([this]{ this->endLayoutChange(); });
2384 that().sortImpl([&compare](const auto &leftRow, const auto &rightRow) {
2385 if (auto anyInvalid = Compare::compareInvalid(leftRow, rightRow))
2386 return *anyInvalid;
2387 return row_traits::for_element_at(leftRow, compare.m_index.column(),
2388 [&rightRow, &compare](const auto &leftItem){
2389 return row_traits::for_element_at(rightRow, compare.m_index.column(),
2390 [&leftItem, &compare](const auto &rightItem){
2391 // Called by std::stable_sort. Since "column" is a runtime value, we
2392 // can't statically assert that lhs and rhs are of the same type.
2393 if constexpr (std::is_same_v<decltype(leftItem), decltype(rightItem)>) {
2394 if (auto anyInvalid = Compare::compareInvalid(leftItem, rightItem))
2395 return *anyInvalid;
2396 return compare(QRangeModelDetails::refTo(leftItem),
2397 QRangeModelDetails::refTo(rightItem));
2398 } else {
2399 Q_UNREACHABLE();
2400 }
2401 return false;
2402 });
2403 });
2404 });
2405 }
2406 }
2407
2408 template <typename LessThan>
2409 void sortSubRange(range_type &range, row_ptr expectedParent, const LessThan &lessThan)
2410 {
2411 auto begin = QRangeModelDetails::adl_begin(range);
2412 auto end = QRangeModelDetails::adl_end(range);
2413 if (begin == end)
2414 return;
2415
2416 QModelIndexList persistentIndexes = this->persistentIndexList();
2417 that().prunePersistentIndexList(persistentIndexes, expectedParent);
2418
2419 if (persistentIndexes.isEmpty()) {
2420 using It = typename range_features::iterator;
2421 constexpr bool is_random_access = std::is_base_of_v<std::random_access_iterator_tag,
2422 typename std::iterator_traits<It>::iterator_category>;
2423 // fast path if we have no persistent indexes: sort the range in place
2424 if constexpr (Compare::hasSortMember) {
2425 range.sort(lessThan);
2426 return;
2427 } else if constexpr (is_random_access) {
2428 std::stable_sort(begin, end, lessThan);
2429 return;
2430 }
2431 }
2432
2433 // slow path: create an indexed version of the range by adding a
2434 // column that records row movements.
2435 struct SortTracker
2436 {
2437 row_type row;
2438 int index;
2439 };
2440
2441 const int rangeSize = size(range);
2442 // Allocate all necessary memory here so that a potential exception
2443 // gets thrown before we have made any modifications.
2444 std::vector<SortTracker> tracked;
2445 tracked.reserve(rangeSize);
2446 std::vector<int> newRows;
2447 newRows.resize(rangeSize);
2448
2449 // move all rows into that index paired with its unsorted position
2450 int row = -1;
2451 for (auto &&it = std::move_iterator(begin); it != std::move_iterator(end); ++it)
2452 tracked.emplace_back(SortTracker{*it, ++row});
2453
2454 // sort the index based on a comparions of the data
2455 std::stable_sort(tracked.begin(), tracked.end(),
2456 [&lessThan](const SortTracker &lhs, const SortTracker &rhs){
2457 return lessThan(lhs.row, rhs.row);
2458 });
2459
2460 // write the values back to the range in (now ordered) sequence,
2461 // and create a mapping from old to new row
2462 auto sorted = std::move_iterator(tracked.begin());
2463 auto write = QRangeModelDetails::adl_begin(range);
2464 qsizetype changedIndexCount = 0;
2465 for (int newIndex = 0; newIndex < rangeSize; ++write, ++sorted, ++newIndex) {
2466 auto &&tracker = *sorted;
2467 changedIndexCount += (tracker.index != newIndex);
2468 *write = std::move(tracker.row);
2469 newRows[tracker.index] = newIndex;
2470 }
2471
2472 // free memory from intermediate vector
2473 tracked.clear();
2474 tracked.shrink_to_fit();
2475
2476 // update relevant persistent model indexes
2477 for (const auto &fromIndex : std::as_const(persistentIndexes)) {
2478 const int newRow = newRows.at(fromIndex.row());
2479 if (fromIndex.row() == newRow)
2480 continue;
2481 const QModelIndex toIndex = that().indexImpl(newRow,
2482 fromIndex.column(),
2483 fromIndex.parent());
2484 this->changePersistentIndex(fromIndex, toIndex);
2485 }
2486 }
2487
2488 QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits,
2489 Qt::MatchFlags flags) const
2490 {
2491 QCollator collator = this->matchCollator();
2492 collator.setCaseSensitivity(flags.testFlag(Qt::MatchCaseSensitive) ? Qt::CaseSensitive
2493 : Qt::CaseInsensitive);
2494 return that().matchImpl(start, role,
2495 QRangeModelImplBase::convertMatchValue(value, flags), hits,
2496 flags, collator);
2497 }
2498
2499 bool matchRow(const_row_reference row, const QModelIndex &index, int role, const QVariant &value,
2500 Qt::MatchFlags flags, const QCollator &collator) const
2501 {
2502 const uint matchType = (flags & Qt::MatchTypeMask).toInt();
2503
2504 return row_traits::for_element_at(row, index.column(), [&](const auto &element) {
2505 using value_type = q20::remove_cvref_t<decltype(element)>;
2506 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
2507 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
2508
2509 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasReadRole
2510 || multi_role() || has_metaobject<value_type>) {
2511 QModelRoleData roleData(role);
2512 ItemReader reader{index, roleData, this};
2513 reader(element);
2514 return QRangeModelImplBase::matchValue(roleData.data(), value, flags, collator);
2515 } else if constexpr (std::is_same_v<wrapped_value_type, QVariant>) {
2516 return QRangeModelImplBase::matchValue(element, value, flags, collator);
2517 } else {
2518 constexpr QMetaType mt = QMetaType::fromType<wrapped_value_type>();
2519 if (mt == value.metaType()) {
2520 if (matchType == Qt::MatchExactly)
2521 return mt.equals(QRangeModelDetails::pointerTo(element), value.constData());
2522 else if constexpr (std::is_same_v<wrapped_value_type, QString>)
2523 return QRangeModelImplBase::matchValue(element, value, flags, collator);
2524 } else {
2525 return QRangeModelImplBase::matchValue(QVariant::fromValue(QRangeModelDetails::refTo(element)),
2526 value, flags, collator);
2527 }
2528 }
2529 return false;
2530 });
2531 }
2532
2533 template <typename InsertFn>
2534 bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
2535 {
2536 if (count == 0)
2537 return false;
2538 range_type * const children = childRange(parent);
2539 if (!children)
2540 return false;
2541
2542 this->beginInsertColumns(parent, column, column + count - 1);
2543
2544 for (auto &child : *children) {
2545 auto it = QRangeModelDetails::pos(child, column);
2546 (void)insertFn(QRangeModelDetails::refTo(child), it, count);
2547 }
2548
2549 this->endInsertColumns();
2550
2551 // endInsertColumns emits columnsInserted, at which point clients might
2552 // have populated the new columns with objects (if the columns aren't objects
2553 // themselves).
2554 if constexpr (itemsAreQObjects) {
2555 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2556 for (int r = 0; r < that().rowCount(parent); ++r) {
2557 for (int c = column; c < column + count; ++c) {
2558 const QModelIndex index = that().index(r, c, parent);
2559 writeAt(index, [this, &index](QObject *item){
2560 return Self::connectProperties(index, item,
2561 m_data.context, m_data.properties);
2562 });
2563 }
2564 }
2565 }
2566 }
2567
2568 return true;
2569 }
2570
2571 bool insertColumns(int column, int count, const QModelIndex &parent)
2572 {
2573 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
2574 return doInsertColumns(column, count, parent, [](auto &row, auto it, int n){
2575 row.insert(it, n, {});
2576 return true;
2577 });
2578 } else {
2579 return false;
2580 }
2581 }
2582
2583 bool removeColumns(int column, int count, const QModelIndex &parent)
2584 {
2585 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
2586 if (column < 0 || column + count > columnCount(parent))
2587 return false;
2588
2589 range_type * const children = childRange(parent);
2590 if (!children)
2591 return false;
2592
2593 if constexpr (itemsAreQObjects) {
2594 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2595 for (int r = 0; r < that().rowCount(parent); ++r) {
2596 for (int c = column; c < column + count; ++c) {
2597 const QModelIndex index = that().index(r, c, parent);
2598 writeAt(index, [this](QObject *item){
2599 m_data.connections.removeIf([item](const auto &connection) {
2600 return connection.sender == item;
2601 });
2602 return true;
2603 });
2604 }
2605 }
2606 }
2607 }
2608
2609 this->beginRemoveColumns(parent, column, column + count - 1);
2610 for (auto &child : *children) {
2611 const auto start = QRangeModelDetails::pos(child, column);
2612 QRangeModelDetails::refTo(child).erase(start, std::next(start, count));
2613 }
2614 this->endRemoveColumns();
2615 return true;
2616 }
2617 return false;
2618 }
2619
2620 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
2621 const QModelIndex &destParent, int destColumn)
2622 {
2623 // we only support moving columns within the same parent
2624 if (sourceParent != destParent)
2625 return false;
2626 if constexpr (isMutable() && (row_features::has_rotate || row_features::has_splice)) {
2627 if (!Structure::canMoveColumns(sourceParent, destParent))
2628 return false;
2629
2630 if constexpr (dynamicColumns()) {
2631 // we only support ranges as columns, as other types might
2632 // not have the same data type across all columns
2633 range_type * const children = childRange(sourceParent);
2634 if (!children)
2635 return false;
2636
2637 if (!this->beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
2638 destParent, destColumn)) {
2639 return false;
2640 }
2641
2642 for (auto &child : *children)
2643 QRangeModelDetails::rotate(child, sourceColumn, count, destColumn);
2644
2645 this->endMoveColumns();
2646 return true;
2647 }
2648 }
2649 return false;
2650 }
2651
2652 template <typename InsertFn>
2653 bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
2654 {
2655 range_type *children = childRange(parent);
2656 if (!children)
2657 return false;
2658
2659 this->beginInsertRows(parent, row, row + count - 1);
2660
2661 row_ptr parentRow = parent.isValid()
2662 ? QRangeModelDetails::pointerTo(this->rowData(parent))
2663 : nullptr;
2664 (void)std::forward<InsertFn>(insertFn)(*children, parentRow, row, count);
2665
2666 // fix the parent in all children of the modified row, as the
2667 // references back to the parent might have become invalid.
2668 that().resetParentInChildren(children);
2669
2670 this->endInsertRows();
2671
2672 // endInsertRows emits rowsInserted, at which point clients might
2673 // have populated the new row with objects (if the rows aren't objects
2674 // themselves).
2675 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2676 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2677 const auto begin = QRangeModelDetails::pos(children, row);
2678 const auto end = std::next(begin, count);
2679 int rowIndex = row;
2680 for (auto it = begin; it != end; ++it, ++rowIndex)
2681 autoConnectPropertiesInRow(*it, rowIndex, parent);
2682 }
2683 }
2684
2685 return true;
2686 }
2687
2688 bool insertRows(int row, int count, const QModelIndex &parent)
2689 {
2690 if constexpr (canInsertRows()) {
2691 return doInsertRows(row, count, parent,
2692 [this](range_type &children, row_ptr parentRow, int r, int n){
2693 EmptyRowGenerator generator{0, &that(), parentRow};
2694
2695 const auto pos = QRangeModelDetails::pos(children, r);
2696 if constexpr (range_features::has_insert_range) {
2697 children.insert(pos, std::move(generator), EmptyRowGenerator{n});
2698 } else if constexpr (rows_are_owning_or_raw_pointers) {
2699 auto start = children.insert(pos, n, nullptr); // MSVC doesn't like row_type{}
2700 std::copy(std::move(generator), EmptyRowGenerator{n}, start);
2701 } else {
2702 children.insert(pos, n, std::move(*generator));
2703 }
2704 return true;
2705 });
2706 } else {
2707 return false;
2708 }
2709 }
2710
2711 bool removeRows(int row, int count, const QModelIndex &parent = {})
2712 {
2713 if constexpr (canRemoveRows()) {
2714 const int prevRowCount = rowCount(parent);
2715 if (row < 0 || row + count > prevRowCount)
2716 return false;
2717
2718 range_type *children = childRange(parent);
2719 if (!children)
2720 return false;
2721
2722 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2723 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2724 const auto begin = QRangeModelDetails::pos(children, row);
2725 const auto end = std::next(begin, count);
2726 int rowIndex = row;
2727 for (auto it = begin; it != end; ++it, ++rowIndex)
2728 clearConnectionInRow(*it, rowIndex, parent);
2729 }
2730 }
2731
2732 this->beginRemoveRows(parent, row, row + count - 1);
2733 [[maybe_unused]] bool callEndRemoveColumns = false;
2734 if constexpr (dynamicColumns()) {
2735 // if we remove the last row in a dynamic model, then we no longer
2736 // know how many columns we should have, so they will be reported as 0.
2737 if (prevRowCount == count) {
2738 if (const int columns = columnCount(parent)) {
2739 callEndRemoveColumns = true;
2740 this->beginRemoveColumns(parent, 0, columns - 1);
2741 }
2742 }
2743 }
2744 { // erase invalidates iterators
2745 const auto begin = QRangeModelDetails::pos(children, row);
2746 const auto end = std::next(begin, count);
2747 that().deleteRemovedRows(begin, end);
2748 children->erase(begin, end);
2749 }
2750 // fix the parent in all children of the modified row, as the
2751 // references back to the parent might have become invalid.
2752 that().resetParentInChildren(children);
2753
2754 if constexpr (dynamicColumns()) {
2755 if (callEndRemoveColumns) {
2756 Q_ASSERT(columnCount(parent) == 0);
2757 this->endRemoveColumns();
2758 }
2759 }
2760 this->endRemoveRows();
2761 return true;
2762 } else {
2763 return false;
2764 }
2765 }
2766
2767 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
2768 const QModelIndex &destParent, int destRow)
2769 {
2770 if constexpr (isMutable() && (range_features::has_rotate || range_features::has_splice)) {
2771 if (!Structure::canMoveRows(sourceParent, destParent))
2772 return false;
2773
2774 if (sourceParent != destParent) {
2775 return that().moveRowsAcross(sourceParent, sourceRow, count,
2776 destParent, destRow);
2777 }
2778
2779 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
2780 || sourceRow < 0 || sourceRow + count - 1 >= this->rowCount(sourceParent)
2781 || destRow < 0 || destRow > this->rowCount(destParent)) {
2782 return false;
2783 }
2784
2785 range_type *source = childRange(sourceParent);
2786 // moving within the same range
2787 if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
2788 return false;
2789
2790 QRangeModelDetails::rotate(source, sourceRow, count, destRow);
2791
2792 that().resetParentInChildren(source);
2793
2794 this->endMoveRows();
2795 return true;
2796 } else {
2797 return false;
2798 }
2799 }
2800
2801 const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2802 protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2803
2804 QModelIndex parent(const QModelIndex &child) const { return that().parentImpl(child); }
2805
2806 int rowCount(const QModelIndex &parent) const { return that().rowCountImpl(parent); }
2807
2808 static constexpr int fixedColumnCount()
2809 {
2810 if constexpr (one_dimensional_range)
2811 return row_traits::fixed_size();
2812 else
2813 return static_column_count;
2814 }
2815 int columnCount(const QModelIndex &parent) const { return that().columnCountImpl(parent); }
2816
2817 void destroy() { delete std::addressof(that()); }
2818
2819 Qt::DropActions adjustSupportedDragActions(Qt::DropActions dragActions) {
2820 if constexpr (!isMutable())
2821 dragActions &= ~Qt::MoveAction;
2822 return dragActions;
2823 }
2824 Qt::DropActions adjustSupportedDropActions(Qt::DropActions dropActions)
2825 {
2826 if constexpr (!isMutable())
2827 dropActions = Qt::IgnoreAction;
2828
2829 return dropActions;
2830 }
2831
2833 {
2834 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
2835 if constexpr (QRangeModelDetails::item_access<ItemType>::hasMimeTypes)
2836 return QRangeModelDetails::QRangeModelItemAccess<ItemType>::mimeTypes();
2837 else if constexpr (QRangeModelDetails::hasMimeTypes<wrapped_row_type>)
2838 return QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::mimeTypes();
2839 else
2840 return this->itemModel().QAbstractItemModel::mimeTypes();
2841 }
2842
2843 bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
2844 const QModelIndex &target) const
2845 {
2846 if constexpr (isMutable()) {
2847 bool canDrop;
2848 using RowOptions = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>;
2849 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
2850 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<ItemType>;
2851 if constexpr (QRangeModelDetails::item_access<ItemType>::hasCanDropMimeDataFull) {
2852 canDrop = ItemAccess::canDropMimeData(data, action, row, column, target);
2853 } else if constexpr (QRangeModelDetails::hasCanDropMimeDataFull<wrapped_row_type>) {
2854 canDrop = RowOptions::canDropMimeData(data, action, row, column, target);
2855 } else {
2856 canDrop = this->itemModel().QAbstractItemModel::canDropMimeData(data, action, row,
2857 column, target);
2858 if constexpr (QRangeModelDetails::item_access<ItemType>::hasCanDropMimeData)
2859 canDrop &= ItemAccess::canDropMimeData(data);
2860 else if constexpr (QRangeModelDetails::hasCanDropMimeData<wrapped_row_type>)
2861 canDrop &= RowOptions::canDropMimeData(data);
2862 }
2863 return canDrop;
2864 } else {
2865 return false;
2866 }
2867 }
2868
2869 // orientation == vertical: we drop rows; otherwise we drop individual items
2870 template <Qt::Orientation orient, typename Entry>
2871 bool doDropMimeData(std::vector<QRangeModelDetails::DroppedEntry<Entry>> &droppedEntries,
2872 DropOperation dropOperation, int row, int column, const QModelIndex &target)
2873 {
2874 using DroppedEntry = QRangeModelDetails::DroppedEntry<Entry>;
2875 using Cell = typename DroppedEntry::Cell;
2876 if (dropOperation == DropOperation::DontDrop)
2877 return false;
2878
2879 const bool dropOnTarget = row == -1 && column == -1 && target.isValid();
2880 const QModelIndex parent = dropOperation == DropOperation::InsertAsChildren
2881 ? target.siblingAtColumn(0) : target.parent();
2882
2883 Cell lastCell;
2884 if constexpr (orient == Qt::Horizontal)
2885 lastCell = {0, -1};
2886 else
2887 lastCell = {-1, 0};
2888 int bottomRow = -1;
2889 int rightColumn = -1;
2890 // set the target cell for all dropped entries and find the bottom-right
2891 // cell relative to the drop position
2892 int maxColumn = that().columnCount(parent) - 1;
2893 for (auto &droppedEntry : droppedEntries) {
2894 if (droppedEntry.m_cell == Cell{-1, -1}) {
2895 if constexpr (orient == Qt::Horizontal) {
2896 // auto-inserted items fill all columns before moving to
2897 // the next row
2898 droppedEntry.m_cell = {lastCell.m_row, lastCell.m_column + 1};
2899 if (droppedEntry.m_cell.m_column > maxColumn) {
2900 droppedEntry.m_cell.m_column = 0;
2901 ++droppedEntry.m_cell.m_row;
2902 }
2903 } else {
2904 droppedEntry.m_cell = {lastCell.m_row + 1, lastCell.m_column};
2905 }
2906 }
2907 lastCell = droppedEntry.m_cell;
2908 bottomRow = std::max(lastCell.m_row, bottomRow);
2909 rightColumn = std::max(lastCell.m_column, rightColumn);
2910 }
2911
2912 if (dropOperation == DropOperation::InsertAsChildren) {
2913 row = rowCount(parent);
2914 column = 0;
2915 } else if (dropOnTarget) {
2916 row = target.row();
2917 column = target.column();
2918 } else {
2919 if (row < 0)
2920 row = rowCount(parent);
2921 // dropping into empty space to the right of a table doesn't widen
2922 if (column < 0)
2923 column = 0;
2924 }
2925 const bool overwrite = dropOperation == DropOperation::OverwriteAndIgnore
2926 || dropOperation == DropOperation::OverwriteAndExtend;
2927
2928 // Compute if we need more rows, and try to add them. Abort if that fails.
2929 const int overwriteRows = overwrite
2930 ? std::min(bottomRow + 1, rowCount(parent) - row)
2931 : 0;
2932 const int newRows = dropOperation == DropOperation::OverwriteAndExtend
2933 ? bottomRow - overwriteRows + 1
2934 : (dropOperation == DropOperation::InsertAsChildren
2935 || dropOperation == DropOperation::InsertAsSiblings)
2936 ? bottomRow + 1
2937 : 0;
2938 if (newRows > 0 && !insertRows(row, newRows, parent))
2939 return false;
2940
2941 // Ditto for columns, but InsertAsSiblings/Children only applies to rows
2942 const int overwriteColumns = overwrite
2943 ? std::min(rightColumn + 1, columnCount(parent) - column)
2944 : 0;
2945 const int newColumns = dropOperation == DropOperation::OverwriteAndExtend
2946 ? rightColumn - overwriteColumns + 1 : 0;
2947 if (newColumns > 0 && !insertColumns(column, newColumns, parent))
2948 return false;
2949
2950 // access the target range
2951 range_type *parentRange = that().childRange(parent);
2952 if (!parentRange)
2953 return false;
2954 range_type &targetRange = *parentRange;
2955
2956 int maxRow = that().rowCount(parent) - 1;
2957 maxColumn = that().columnCount(parent) - 1;
2958 auto begin = std::move_iterator(droppedEntries.begin());
2959 auto end = std::move_iterator(droppedEntries.end());
2960 for (; begin != end; ++begin) {
2961 DroppedEntry droppedEntry = *begin;
2962 const Cell cell = {droppedEntry.m_cell.m_row + row, droppedEntry.m_cell.m_column + column};
2963 if (cell.m_row > maxRow || cell.m_column > maxColumn) // Ignore
2964 continue;
2965 auto writeRow = QRangeModelDetails::pos(targetRange, cell.m_row);
2966 if constexpr (orient == Qt::Vertical) { // complete rows
2967 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<row_type>()) {
2968 if (!*writeRow)
2969 *writeRow = this->protocol().newRow();
2970 **writeRow = std::move(droppedEntry);
2971 } else {
2972 *writeRow = std::move(droppedEntry);
2973 }
2974 } else {
2975 row_traits::for_element_at(*writeRow, cell.m_column, [&](auto &item){
2976 using item_type = q20::remove_cvref_t<decltype(item)>;
2977 using wrapped_item_type = QRangeModelDetails::wrapped_t<item_type>;
2978 if constexpr (QRangeModelDetails::is_any_owning_ptr<item_type>()) {
2979 if (!QRangeModelDetails::isValid(item))
2980 item.reset(new wrapped_item_type{std::move(droppedEntry)});
2981 else
2982 *item = std::move(droppedEntry);
2983 } else if (!QRangeModelDetails::isValid(item)) {
2984 return false;
2985 } else {
2986 item = std::move(droppedEntry);
2987 }
2988 return true;
2989 });
2990 }
2991 }
2992
2993 that().resetParentInChildren(&targetRange);
2994
2995 const QModelIndex topLeft = index(row, column, parent);
2996 const QModelIndex bottomRight = orient == Qt::Horizontal
2997 ? sibling(row + bottomRow, column + rightColumn, topLeft)
2998 : sibling(row + bottomRow, maxColumn, topLeft);
2999 this->dataChanged(topLeft, bottomRight, {});
3000
3001 return true;
3002 }
3003
3004 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
3005 const QModelIndex &target)
3006 {
3007 if constexpr (isMutable()) {
3008 if (!canDropMimeData(data, action, row, column, target))
3009 return false;
3010
3011 const bool dropOnTarget = row == -1 && column == -1 && target.isValid();
3012
3013 auto automaticDropOption = [=](auto dropResult){
3014 DropOperation dropOperation;
3015 if constexpr (std::is_same_v<bool, decltype(dropResult)>) {
3016 dropOperation = dropResult ? DropOperation::Automatic
3017 : DropOperation::DontDrop;
3018 } else { // it's a QRangeModel::DropOperation
3019 dropOperation = static_cast<DropOperation>(dropResult);
3020 }
3021
3022 if (dropOperation == DropOperation::Automatic) {
3023 if constexpr (!canInsertRows())
3024 dropOperation = DropOperation::OverwriteAndIgnore;
3025 else if (!dropOnTarget)
3026 dropOperation = DropOperation::InsertAsSiblings;
3027 else if (target.siblingAtColumn(0).flags().testFlag(Qt::ItemNeverHasChildren))
3028 dropOperation = DropOperation::OverwriteAndExtend;
3029 else
3030 dropOperation = DropOperation::InsertAsChildren;
3031 }
3032 return dropOperation;
3033 };
3034
3035 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
3036 if constexpr (QRangeModelDetails::item_access<ItemType>::hasDropMimeDataFull
3037 || QRangeModelDetails::item_access<ItemType>::hasDropMimeData) {
3038 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<ItemType>;
3039 using DroppedItem = QRangeModelDetails::DroppedEntry<ItemType>;
3040 std::vector<DroppedItem> droppedItems;
3041 DropOperation dropOperation = automaticDropOption([&]{
3042 auto inserter = std::back_inserter(droppedItems);
3043 if constexpr (QRangeModelDetails::item_access<ItemType>::hasDropMimeDataFull)
3044 return ItemAccess::dropMimeData(data, action, row, column, target, inserter);
3045 else
3046 return ItemAccess::dropMimeData(data, inserter);
3047 }());
3048 if (doDropMimeData<Qt::Horizontal>(droppedItems, dropOperation, row, column, target))
3049 return true;
3050 // fall through to try the default mime type
3051 } else if constexpr (QRangeModelDetails::hasDropMimeDataFull<wrapped_row_type>
3052 || QRangeModelDetails::hasDropMimeData<wrapped_row_type>) {
3053 using RowOptions = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>;
3054 using DroppedRow = QRangeModelDetails::DroppedEntry<wrapped_row_type>;
3055 std::vector<DroppedRow> droppedRows;
3056 DropOperation dropOperation = automaticDropOption([&]{
3057 auto inserter = std::back_inserter(droppedRows);
3058 if constexpr (QRangeModelDetails::hasDropMimeDataFull<wrapped_row_type>)
3059 return RowOptions::dropMimeData(data, action, row, column, target, inserter);
3060 else
3061 return RowOptions::dropMimeData(data, inserter);
3062 }());
3063 if (doDropMimeData<Qt::Vertical>(droppedRows, dropOperation, row, column, target))
3064 return true;
3065 }
3066 // default mime type handling: dropping on item -> try to set the data
3067 if (dropOnTarget && that().dropOnItem(data, target))
3068 return true;
3069 }
3070 return false;
3071 }
3072
3073 // A bidirectional-iterator that, given a list of QModelIndex, dereferences
3074 // to a list of rows or items, plus QModelIndex, without copying any data.
3075 // For segments in indexes covering full rows, we skip over the individual
3076 // indexes and give the dereferenced index a column value of -1.
3078 {
3081 using iterator_category = std::bidirectional_iterator_tag;
3083 using reference [[maybe_unused]] = value_type;
3085 using pointer [[maybe_unused]] = void;
3086
3088 MimeDataRowIterator(base_iterator it, base_iterator begin, base_iterator end,
3089 const QRangeModelImpl *model)
3090 : m_it(it), m_begin(begin), m_end(end), m_model(model)
3091 , m_columnCount(model->columnCount({}))
3092 {
3093 updateCurrentIndexFullRow();
3094 }
3095
3097 {
3098 const QModelIndex &index = *m_it;
3099 return {m_model->rowData(index),
3100 m_currentIndexIsFullRow
3101 ? m_model->createIndex(m_it->row(), -1, m_it->internalPointer()) : index};
3102 }
3103
3105 if (m_currentIndexIsFullRow)
3106 m_it += m_columnCount;
3107 else
3108 ++m_it;
3109 updateCurrentIndexFullRow();
3110 return *this;
3111 }
3112 MimeDataRowIterator operator++(int) { auto tmp = *this; ++(*this); return tmp; }
3113
3115 --m_it;
3116 m_currentIndexIsFullRow = false;
3117 const int lastColumn = m_columnCount - 1;
3118 if (m_it - m_begin >= lastColumn && m_it->column() == lastColumn) {
3119 const QModelIndex &firstInRow = m_it[-lastColumn];
3120 if (m_it->row() == firstInRow.row()
3121 && firstInRow.internalPointer() == m_it->internalPointer()) {
3122 m_currentIndexIsFullRow = true;
3123 m_it -= lastColumn;
3124 }
3125 }
3126 return *this;
3127 }
3128 MimeDataRowIterator operator--(int) { auto tmp = *this; --(*this); return tmp; }
3129
3130 MimeDataRowIterator operator-(difference_type n) const
3131 {
3132 auto tmp = *this; tmp.m_it -= n; return tmp;
3133 }
3134
3135 bool operator==(const MimeDataRowIterator &other) const { return m_it == other.m_it; }
3136 bool operator!=(const MimeDataRowIterator &other) const { return m_it != other.m_it; }
3137
3138 private:
3139 void updateCurrentIndexFullRow()
3140 {
3141 m_currentIndexIsFullRow = false;
3142 if (m_it == m_end || m_it->column() || m_end - m_it < m_columnCount)
3143 return;
3144 const QModelIndex &lastInRow = m_it[m_columnCount - 1];
3145 m_currentIndexIsFullRow = lastInRow.row() == m_it->row()
3146 && lastInRow.internalPointer() == m_it->internalPointer();
3147 }
3148
3149 base_iterator m_it;
3150 base_iterator m_begin;
3151 base_iterator m_end;
3152 const QRangeModelImpl *m_model;
3153 int m_columnCount = 0;
3154 bool m_currentIndexIsFullRow = false;
3155 };
3156
3158 {
3160 // row_traits::item_type is wrapped, and void if not the same for all columns
3162 void *, typename row_traits::item_type>;
3164 using iterator_category = std::bidirectional_iterator_tag;
3166 using reference [[maybe_unused]] = value_type;
3168 using pointer [[maybe_unused]] = void;
3169
3171 {
3172 const QModelIndex &index = *m_it;
3173 // pointer to the item as stored, including wrapping
3174 const item_type *pitem = nullptr;
3175 const auto &row = m_model->rowData(index);
3176 if (QRangeModelDetails::isValid(row)) {
3177 row_traits::for_element_at(QRangeModelDetails::refTo(row), index.column(),
3178 [&pitem](const auto &item){
3179 pitem = &item;
3180 return true;
3181 });
3182 }
3183 if constexpr (std::disjunction_v<QRangeModelDetails::is_owning_or_raw_pointer<row_type>,
3184 QRangeModelDetails::is_owning_or_raw_pointer<item_type>>) {
3185 if (Q_UNLIKELY(!QRangeModelDetails::isValid(pitem))) {
3186 // no use in warning about invalid item here, as the user
3187 // can't check for validity without dereferencing the iterator.
3188 constexpr bool is_constexpr_default_constructible_v =
3189 value_type::template is_constexpr_default_constructible<item_type>::value;
3190 if constexpr (is_constexpr_default_constructible_v) {
3191 Q_CONSTINIT static const item_type emptyDefault;
3192 return {emptyDefault, index};
3193 } else {
3194 // known to cause runtime initialization
3195 static const item_type emptyDefault;
3196 return {emptyDefault, index};
3197 }
3198 }
3199 }
3200 // this will decompose to a [wrapped_item_type, QModelIndex]
3201 return {*pitem, index};
3202 }
3203 MimeDataItemIterator &operator++() { ++m_it; return *this; }
3204 MimeDataItemIterator operator++(int) { auto tmp = *this; ++(*this); return tmp; }
3205
3206 MimeDataItemIterator &operator--() { --m_it; return *this; }
3207 MimeDataItemIterator operator--(int) { auto tmp = *this; --(*this); return tmp; }
3208
3209 MimeDataItemIterator operator-(difference_type n) const
3210 {
3211 auto tmp = *this; tmp.m_it -= n; return tmp;
3212 }
3213
3214 bool operator==(const MimeDataItemIterator &other) const { return m_it == other.m_it; }
3215 bool operator!=(const MimeDataItemIterator &other) const { return m_it != other.m_it; }
3216
3219 };
3220
3221 template <typename Iterator>
3223 Iterator begin() const { return m_begin; }
3224 Iterator end() const { return m_end; }
3225 auto rbegin() const { return std::reverse_iterator(m_end); }
3226 auto rend() const { return std::reverse_iterator(m_begin); }
3227 auto first() const { return *m_begin;}
3228 auto last() const { return *(m_end - 1);}
3229 bool isEmpty() const { return m_begin == m_end; }
3230 bool empty() const { return m_begin == m_end; }
3231 Iterator m_begin;
3232 Iterator m_end;
3233 };
3234
3235 QMimeData *mimeData(const QModelIndexList &indexes) const
3236 {
3237 QMimeData *result = nullptr;
3238 using RowOptions = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>;
3239 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
3240
3241 if constexpr (QRangeModelDetails::item_access<ItemType>::hasMimeData) {
3242 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<ItemType>;
3243 const auto begin = MimeDataItemIterator{indexes.begin(), this};
3244 const auto end = MimeDataItemIterator{indexes.end(), this};
3245 result = ItemAccess::mimeData(MimeDataRange<MimeDataItemIterator>{begin, end});
3246 } else if constexpr (QRangeModelDetails::hasMimeDataRowSpan<wrapped_row_type>) {
3247 const auto begin = MimeDataRowIterator(indexes.begin(), indexes.begin(), indexes.end(), this);
3248 const auto end = MimeDataRowIterator(indexes.end(), indexes.begin(), indexes.end(), this);
3249 result = RowOptions::mimeData(MimeDataRange<MimeDataRowIterator>{begin, end});
3250 } else if constexpr (QRangeModelDetails::hasMimeDataIndexList<wrapped_row_type>) {
3251 result = RowOptions::mimeData(indexes);
3252 }
3253
3254 return result;
3255 }
3256
3257 template <typename BaseMethod, typename BaseMethod::template Overridden<Self> overridden>
3258 using Override = typename Ancestor::template Override<BaseMethod, overridden>;
3259
3268
3283
3287
3295
3300
3301protected:
3303 {
3305 }
3306
3308 {
3309 // We delete row objects if we are not operating on a reference or pointer
3310 // to a range, as in that case, the owner of the referenced/pointed to
3311 // range also owns the row entries.
3312 // ### Problem: if we get a copy of a range (no matter if shared or not),
3313 // then adding rows will create row objects in the model's copy, and the
3314 // client can never delete those. But copied rows will be the same pointer,
3315 // which we must not delete (as we didn't create them).
3316
3317 static constexpr bool modelCopied = !QRangeModelDetails::is_wrapped<Range>() &&
3318 (std::is_reference_v<Range> || std::is_const_v<std::remove_reference_t<Range>>);
3319
3320 static constexpr bool modelShared = QRangeModelDetails::is_any_shared_ptr<Range>();
3321
3322 static constexpr bool default_row_deleter = protocol_traits::is_default &&
3323 protocol_traits::has_deleteRow;
3324
3325 static constexpr bool ambiguousRowOwnership = (modelCopied || modelShared) &&
3326 rows_are_raw_pointers && default_row_deleter;
3327
3328 static_assert(!ambiguousRowOwnership,
3329 "Using of copied and shared tree and table models with rows as raw pointers, "
3330 "and the default protocol is not allowed due to ambiguity of rows ownership. "
3331 "Move the model in, use another row type, or implement a custom tree protocol.");
3332
3333 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
3334 && !QRangeModelDetails::is_any_of<Range, std::reference_wrapper>()) {
3335 const auto begin = QRangeModelDetails::adl_begin(*m_data.model());
3336 const auto end = QRangeModelDetails::adl_end(*m_data.model());
3337 that().deleteRemovedRows(begin, end);
3338 }
3339 }
3340
3341 static constexpr bool canInsertRows()
3342 {
3343 if constexpr (dynamicColumns() && !row_features::has_resize) {
3344 // If we operate on dynamic columns and cannot resize a newly
3345 // constructed row, then we cannot insert.
3346 return false;
3347 } else if constexpr (!protocol_traits::has_newRow) {
3348 // We also cannot insert if we cannot create a new row element
3349 return false;
3350 } else if constexpr (!range_features::has_insert_range
3351 && !std::is_copy_constructible_v<row_type>) {
3352 // And if the row is a move-only type, then the range needs to be
3353 // backed by a container that can move-insert default-constructed
3354 // row elements.
3355 return false;
3356 } else {
3357 return Structure::canInsertRowsImpl();
3358 }
3359 }
3360
3361 static constexpr bool canRemoveRows()
3362 {
3363 return Structure::canRemoveRowsImpl();
3364 }
3365
3366 template <typename F>
3367 bool writeAt(const QModelIndex &index, F&& writer)
3368 {
3369 row_reference row = rowData(index);
3370 if (!QRangeModelDetails::isValid(row))
3371 return false;
3372 return row_traits::for_element_at(row, index.column(), [&writer](auto &&target) {
3373 using target_type = decltype(target);
3374 // we can only assign to an lvalue reference
3375 if constexpr (std::is_lvalue_reference_v<target_type>
3376 && !std::is_const_v<std::remove_reference_t<target_type>>) {
3377 return writer(std::forward<target_type>(target));
3378 } else {
3379 return false;
3380 }
3381 });
3382 }
3383
3384 template <typename F>
3385 bool readAt(const QModelIndex &index, F&& reader) const {
3386 const_row_reference row = rowData(index);
3387 if (!QRangeModelDetails::isValid(row))
3388 return false;
3389 return row_traits::for_element_at(row, index.column(), std::forward<F>(reader));
3390 }
3391
3392 template <typename Value>
3393 static QVariant read(const Value &value)
3394 {
3395 if constexpr (std::is_constructible_v<QVariant, Value>)
3396 return QVariant(value);
3397 else
3398 return QVariant::fromValue(value);
3399 }
3400 template <typename Value>
3401 static QVariant read(Value *value)
3402 {
3403 if (value) {
3404 if constexpr (std::is_constructible_v<QVariant, Value *>)
3405 return QVariant(value);
3406 else
3407 return read(*value);
3408 }
3409 return {};
3410 }
3411
3412 template <typename Target>
3413 static bool write(Target &target, const QVariant &value)
3414 {
3415 using Type = std::remove_reference_t<Target>;
3416 if constexpr (std::is_constructible_v<Target, QVariant>) {
3417 target = value;
3418 return true;
3419 } else if (value.canConvert<Type>()) {
3420 target = value.value<Type>();
3421 return true;
3422 }
3423 return false;
3424 }
3425 template <typename Target>
3426 static bool write(Target *target, const QVariant &value)
3427 {
3428 if (target)
3429 return write(*target, value);
3430 return false;
3431 }
3432
3433 template <typename ItemType>
3435 {
3436 struct {
3437 operator QMetaProperty() const {
3438 const QByteArray roleName = that.itemModel().roleNames().value(role);
3439 const QMetaObject &mo = ItemType::staticMetaObject;
3440 if (const int index = mo.indexOfProperty(roleName.data());
3441 index >= 0) {
3442 return mo.property(index);
3443 }
3444 return {};
3445 }
3446 const QRangeModelImpl &that;
3447 const int role;
3448 } findProperty{*this, role};
3449
3450 if constexpr (ModelData::cachesProperties)
3451 return *m_data.properties.tryEmplace(role, findProperty).iterator;
3452 else
3453 return findProperty;
3454 }
3455
3456 void connectPropertyOnRead(const QModelIndex &index, int role,
3457 const QObject *gadget, const QMetaProperty &prop) const
3458 {
3459 if (!index.isValid())
3460 return;
3461 const typename ModelData::Connection connection = {gadget, role};
3462 if (prop.hasNotifySignal() && this->autoConnectPolicy() == AutoConnectPolicy::OnRead
3463 && !m_data.connections.contains(connection)) {
3464 if constexpr (isMutable())
3465 Self::connectProperty(index, gadget, m_data.context, role, prop);
3466 else
3467 Self::connectPropertyConst(index, gadget, m_data.context, role, prop);
3468 m_data.connections.insert(connection);
3469 }
3470 }
3471
3472 template <typename ItemType>
3473 QVariant readRole(const QModelIndex &index, int role, ItemType *gadget) const
3474 {
3475 using item_type = std::remove_pointer_t<ItemType>;
3476 QVariant result;
3477 QMetaProperty prop = roleProperty<item_type>(role);
3478 if (!prop.isValid() && role == Qt::EditRole) {
3479 role = Qt::DisplayRole;
3480 prop = roleProperty<item_type>(Qt::DisplayRole);
3481 }
3482
3483 if (prop.isValid()) {
3484 if constexpr (itemsAreQObjects)
3485 connectPropertyOnRead(index, role, gadget, prop);
3486 result = readProperty(prop, gadget);
3487 }
3488 return result;
3489 }
3490
3491 template <typename ItemType>
3492 QVariant readRole(const QModelIndex &index, int role, const ItemType &gadget) const
3493 {
3494 return readRole(index, role, &gadget);
3495 }
3496
3497 template <typename ItemType>
3498 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
3499 {
3500 if constexpr (std::is_base_of_v<QObject, ItemType>)
3501 return prop.read(gadget);
3502 else
3503 return prop.readOnGadget(gadget);
3504 }
3505
3506 template <typename ItemType>
3507 QVariant readProperty(const QModelIndex &index, ItemType *gadget) const
3508 {
3509 using item_type = std::remove_pointer_t<ItemType>;
3510 const QMetaObject &mo = item_type::staticMetaObject;
3511 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
3512
3513 if constexpr (rowsAreQObjects)
3514 connectPropertyOnRead(index, Qt::DisplayRole, gadget, prop);
3515
3516 return readProperty(prop, gadget);
3517 }
3518
3519 template <typename ItemType>
3520 QVariant readProperty(const QModelIndex &index, const ItemType &gadget) const
3521 {
3522 return readProperty(index, &gadget);
3523 }
3524
3525 template <typename ItemType>
3526 bool writeRole(int role, ItemType *gadget, const QVariant &data)
3527 {
3528 using item_type = std::remove_pointer_t<ItemType>;
3529 auto prop = roleProperty<item_type>(role);
3530 if (!prop.isValid() && role == Qt::EditRole)
3531 prop = roleProperty<item_type>(Qt::DisplayRole);
3532
3533 return prop.isValid() ? writeProperty(prop, gadget, data) : false;
3534 }
3535
3536 template <typename ItemType>
3537 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
3538 {
3539 return writeRole(role, &gadget, data);
3540 }
3541
3542 template <typename ItemType>
3543 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
3544 {
3545 if constexpr (std::is_base_of_v<QObject, ItemType>)
3546 return prop.write(gadget, data);
3547 else
3548 return prop.writeOnGadget(gadget, data);
3549 }
3550 template <typename ItemType>
3551 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
3552 {
3553 using item_type = std::remove_pointer_t<ItemType>;
3554 const QMetaObject &mo = item_type::staticMetaObject;
3555 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
3556 }
3557
3558 template <typename ItemType>
3559 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
3560 {
3561 return writeProperty(property, &gadget, data);
3562 }
3563
3564 template <typename ItemType>
3565 static bool resetProperty(int property, ItemType *object)
3566 {
3567 using item_type = std::remove_pointer_t<ItemType>;
3568 const QMetaObject &mo = item_type::staticMetaObject;
3569 bool success = true;
3570 if (property == -1) {
3571 // reset all properties
3572 if constexpr (std::is_base_of_v<QObject, item_type>) {
3573 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
3574 success = writeProperty(mo.property(p), object, {}) && success;
3575 } else { // reset a gadget by assigning a default-constructed
3576 *object = {};
3577 }
3578 } else {
3579 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
3580 }
3581 return success;
3582 }
3583
3584 template <typename ItemType>
3585 static bool resetProperty(int property, ItemType &&object)
3586 {
3587 return resetProperty(property, &object);
3588 }
3589
3590 // helpers
3591 const_row_reference rowData(const QModelIndex &index) const
3592 {
3593 Q_ASSERT(index.isValid());
3594 return that().rowDataImpl(index);
3595 }
3596
3597 row_reference rowData(const QModelIndex &index)
3598 {
3599 Q_ASSERT(index.isValid());
3600 return that().rowDataImpl(index);
3601 }
3602
3603 const range_type *childRange(const QModelIndex &index) const
3604 {
3605 if (!index.isValid())
3606 return m_data.model();
3607 if (index.column()) // only items at column 0 can have children
3608 return nullptr;
3609 return that().childRangeImpl(index);
3610 }
3611
3612 range_type *childRange(const QModelIndex &index)
3613 {
3614 if (!index.isValid())
3615 return m_data.model();
3616 if (index.column()) // only items at column 0 can have children
3617 return nullptr;
3618 return that().childRangeImpl(index);
3619 }
3620
3621 template <typename, typename, typename> friend class QRangeModelAdapter;
3622
3624};
3625
3626// Implementations that depends on the model structure (flat vs tree) that will
3627// be specialized based on a protocol type. The main template implements tree
3628// support through a protocol type.
3629template <typename Range, typename Protocol>
3631 : public QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
3632{
3633 using Base = QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
3634 friend class QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
3635
3636 using range_type = typename Base::range_type;
3637 using range_features = typename Base::range_features;
3638 using row_type = typename Base::row_type;
3639 using row_ptr = typename Base::row_ptr;
3640 using const_row_ptr = typename Base::const_row_ptr;
3641
3642 using tree_traits = typename Base::protocol_traits;
3643 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
3644
3645 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
3646 QRangeModelDetails::is_smart_ptr<row_type>() ||
3647 QRangeModelDetails::is_any_of<row_type, std::reference_wrapper>();
3648 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
3649
3650public:
3651 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
3652 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
3653 {};
3654
3655 void setParentRow(range_type &children, row_ptr parent)
3656 {
3657 for (auto &&child : children)
3658 this->protocol().setParentRow(QRangeModelDetails::refTo(child), parent);
3659 resetParentInChildren(&children);
3660 }
3661
3662 void deleteRemovedRows(range_type &range)
3663 {
3664 deleteRemovedRows(QRangeModelDetails::adl_begin(range), QRangeModelDetails::adl_end(range));
3665 }
3666
3667 bool autoConnectProperties(const QModelIndex &parent) const
3668 {
3669 auto *children = this->childRange(parent);
3670 if (!children)
3671 return true;
3672 return autoConnectPropertiesRange(QRangeModelDetails::refTo(children), parent);
3673 }
3674
3675protected:
3676 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
3677 {
3678 if (!parent.isValid())
3679 return this->createIndex(row, column);
3680 // only items at column 0 can have children
3681 if (parent.column())
3682 return QModelIndex();
3683
3684 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
3685 const auto &parentSiblings = childrenOf(grandParent);
3686 const auto it = QRangeModelDetails::pos(parentSiblings, parent.row());
3687 return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
3688 }
3689
3690 QModelIndex parentImpl(const QModelIndex &child) const
3691 {
3692 if (!child.isValid())
3693 return {};
3694
3695 // no pointer to parent row - no parent
3696 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
3697 if (!parentRow)
3698 return {};
3699
3700 // get the siblings of the parent via the grand parent
3701 auto &&grandParent = this->protocol().parentRow(QRangeModelDetails::refTo(parentRow));
3702 const range_type &parentSiblings = childrenOf(QRangeModelDetails::pointerTo(grandParent));
3703 // find the index of parentRow
3704 const auto begin = QRangeModelDetails::adl_begin(parentSiblings);
3705 const auto end = QRangeModelDetails::adl_end(parentSiblings);
3706 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
3707 return QRangeModelDetails::pointerTo(std::forward<decltype(s)>(s)) == parentRow;
3708 });
3709 if (it != end)
3710 return this->createIndex(std::distance(begin, it), 0,
3711 QRangeModelDetails::pointerTo(grandParent));
3712 return {};
3713 }
3714
3715 int rowCountImpl(const QModelIndex &parent) const
3716 {
3717 return Base::size(this->childRange(parent));
3718 }
3719
3720 int columnCountImpl(const QModelIndex &) const
3721 {
3722 // All levels of a tree have to have the same, fixed, column count.
3723 // If static_column_count is -1 for a tree, static assert fires
3724 return Base::fixedColumnCount();
3725 }
3726
3727 static constexpr Qt::ItemFlags defaultFlags()
3728 {
3729 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
3730 }
3731
3732 static constexpr bool canInsertRowsImpl()
3733 {
3734 // We must not insert rows if we cannot adjust the parents of the
3735 // children of the following rows. We don't have to do that if the
3736 // range operates on pointers.
3737 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
3738 && Base::dynamicRows() && range_features::has_insert;
3739 }
3740
3741 static constexpr bool canRemoveRowsImpl()
3742 {
3743 // We must not remove rows if we cannot adjust the parents of the
3744 // children of the following rows. We don't have to do that if the
3745 // range operates on pointers.
3746 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
3747 && Base::dynamicRows() && range_features::has_erase;
3748 }
3749
3750 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
3751 {
3752 return true;
3753 }
3754
3755 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
3756 {
3757 return true;
3758 }
3759
3760 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
3761 const QModelIndex &destParent, int destRow)
3762 {
3763 // If rows are pointers, then reference to the parent row don't
3764 // change, so we can move them around freely. Otherwise we need to
3765 // be able to explicitly update the parent pointer.
3766 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
3767 return false;
3768 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
3769 return false;
3770 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
3771 destParent, destRow)) {
3772 return false;
3773 }
3774
3775 range_type *source = this->childRange(sourceParent);
3776 range_type *destination = this->childRange(destParent);
3777
3778 // If we can insert data from another range into, then
3779 // use that to move the old data over.
3780 const auto destStart = QRangeModelDetails::pos(destination, destRow);
3781 if constexpr (range_features::has_insert_range) {
3782 const auto sourceStart = QRangeModelDetails::pos(*source, sourceRow);
3783 const auto sourceEnd = std::next(sourceStart, count);
3784
3785 destination->insert(destStart, std::move_iterator(sourceStart),
3786 std::move_iterator(sourceEnd));
3787 } else if constexpr (std::is_copy_constructible_v<row_type>) {
3788 // otherwise we have to make space first, and copy later.
3789 destination->insert(destStart, count, row_type{});
3790 }
3791
3792 row_ptr parentRow = destParent.isValid()
3793 ? QRangeModelDetails::pointerTo(this->rowData(destParent))
3794 : nullptr;
3795
3796 // if the source's parent was already inside the new parent row,
3797 // then the source row might have become invalid, so reset it.
3798 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
3799 if (sourceParent.row() < destRow) {
3800 source = this->childRange(sourceParent);
3801 } else {
3802 // the source parent moved down within destination
3803 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
3804 sourceParent.internalPointer()));
3805 }
3806 }
3807
3808 // move the data over and update the parent pointer
3809 {
3810 const auto writeStart = QRangeModelDetails::pos(destination, destRow);
3811 const auto writeEnd = std::next(writeStart, count);
3812 const auto sourceStart = QRangeModelDetails::pos(source, sourceRow);
3813 const auto sourceEnd = std::next(sourceStart, count);
3814
3815 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
3816 // move data over if not already done, otherwise
3817 // only fix the parent pointer
3818 if constexpr (!range_features::has_insert_range)
3819 *write = std::move(*read);
3820 this->protocol().setParentRow(QRangeModelDetails::refTo(*write), parentRow);
3821 }
3822 // remove the old rows from the source parent
3823 source->erase(sourceStart, sourceEnd);
3824 }
3825
3826 // Fix the parent pointers in children of both source and destination
3827 // ranges, as the references to the entries might have become invalid.
3828 // We don't have to do that if the rows are pointers, as in that case
3829 // the references to the entries are stable.
3830 resetParentInChildren(destination);
3832
3833 this->endMoveRows();
3834 return true;
3835 }
3836
3837 auto makeEmptyRow(row_ptr parentRow)
3838 {
3839 // tree traversal protocol: if we are here, then it must be possible
3840 // to change the parent of a row.
3841 static_assert(tree_traits::has_setParentRow);
3842 row_type empty_row = this->protocol().newRow();
3843 if (QRangeModelDetails::isValid(empty_row) && parentRow)
3844 this->protocol().setParentRow(QRangeModelDetails::refTo(empty_row), parentRow);
3845 return empty_row;
3846 }
3847
3848 template <typename It, typename Sentinel>
3849 void deleteRemovedRows(It &&begin, Sentinel &&end)
3850 {
3851 if constexpr (tree_traits::has_deleteRow) {
3852 for (auto it = begin; it != end; ++it) {
3853 if constexpr (Base::isMutable()) {
3854 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
3855 if (QRangeModelDetails::isValid(children)) {
3856 deleteRemovedRows(QRangeModelDetails::adl_begin(children),
3857 QRangeModelDetails::adl_end(children));
3858 QRangeModelDetails::refTo(children) = range_type{ };
3859 }
3860 }
3861
3862 this->protocol().deleteRow(std::move(*it));
3863 }
3864 }
3865 }
3866
3867 void resetParentInChildren(range_type *children)
3868 {
3869 const auto persistentIndexList = this->persistentIndexList();
3870 const auto [firstColumn, lastColumn] = [&persistentIndexList]{
3871 int first = std::numeric_limits<int>::max();
3872 int last = -1;
3873 for (const auto &pmi : persistentIndexList) {
3874 first = (std::min)(pmi.column(), first);
3875 last = (std::max)(pmi.column(), last);
3876 }
3877 return std::pair(first, last);
3878 }();
3879
3880 resetParentInChildrenRecursive(children, firstColumn, lastColumn);
3881 }
3882
3883 void resetParentInChildrenRecursive(range_type *children, int pmiFromColumn, int pmiToColumn)
3884 {
3885 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
3886 const bool changePersistentIndexes = pmiToColumn >= pmiFromColumn;
3887 const auto begin = QRangeModelDetails::adl_begin(*children);
3888 const auto end = QRangeModelDetails::adl_end(*children);
3889 for (auto it = begin; it != end; ++it) {
3890 decltype(auto) maybeChildren = this->protocol().childRows(*it);
3891 if (QRangeModelDetails::isValid(maybeChildren)) {
3892 auto &childrenRef = QRangeModelDetails::refTo(maybeChildren);
3893 auto *parentRow = QRangeModelDetails::pointerTo(*it);
3894
3895 int row = 0;
3896 for (auto &child : childrenRef) {
3897 const_row_ptr oldParent = this->protocol().parentRow(child);
3898 if (oldParent != parentRow) {
3899 if (changePersistentIndexes) {
3900 for (int column = pmiFromColumn; column <= pmiToColumn; ++column) {
3901 this->changePersistentIndex(this->createIndex(row, column, oldParent),
3902 this->createIndex(row, column, parentRow));
3903 }
3904 }
3905 this->protocol().setParentRow(child, parentRow);
3906 }
3907 ++row;
3908 }
3909 resetParentInChildrenRecursive(&childrenRef, pmiFromColumn, pmiToColumn);
3910 }
3911 }
3912 }
3913 }
3914
3915 bool autoConnectPropertiesRange(const range_type &range, const QModelIndex &parent) const
3916 {
3917 int rowIndex = 0;
3918 for (const auto &row : range) {
3919 if (!this->autoConnectPropertiesInRow(row, rowIndex, parent))
3920 return false;
3921 Q_ASSERT(QRangeModelDetails::isValid(row));
3922 const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3923 if (QRangeModelDetails::isValid(children)) {
3924 if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
3925 this->itemModel().index(rowIndex, 0, parent))) {
3926 return false;
3927 }
3928 }
3929 ++rowIndex;
3930 }
3931 return true;
3932 }
3933
3935 {
3936 return autoConnectPropertiesRange(*this->m_data.model(), {});
3937 }
3938
3939 decltype(auto) rowDataImpl(const QModelIndex &index) const
3940 {
3941 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
3942 const range_type &siblings = childrenOf(parentRow);
3943 Q_ASSERT(index.row() < int(Base::size(siblings)));
3944 return *QRangeModelDetails::pos(siblings, index.row());
3945 }
3946
3947 decltype(auto) rowDataImpl(const QModelIndex &index)
3948 {
3949 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
3950 range_type &siblings = childrenOf(parentRow);
3951 Q_ASSERT(index.row() < int(Base::size(siblings)));
3952 return *QRangeModelDetails::pos(siblings, index.row());
3953 }
3954
3955 const range_type *childRangeImpl(const QModelIndex &index) const
3956 {
3957 const auto &row = this->rowData(index);
3958 if (!QRangeModelDetails::isValid(row))
3959 return static_cast<const range_type *>(nullptr);
3960
3961 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3962 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
3963 }
3964
3965 range_type *childRangeImpl(const QModelIndex &index)
3966 {
3967 auto &row = this->rowData(index);
3968 if (!QRangeModelDetails::isValid(row))
3969 return static_cast<range_type *>(nullptr);
3970
3971 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3972 using Children = std::remove_reference_t<decltype(children)>;
3973
3974 if constexpr (QRangeModelDetails::is_any_of<Children, std::optional>())
3975 if constexpr (std::is_default_constructible<typename Children::value_type>()) {
3976 if (!children)
3977 children.emplace(range_type{});
3978 }
3979
3980 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
3981 }
3982
3983 const range_type &childrenOf(const_row_ptr row) const
3984 {
3985 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
3986 : *this->m_data.model();
3987 }
3988
3989 range_type &childrenOf(row_ptr row)
3990 {
3991 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
3992 : *this->m_data.model();
3993 }
3994
3995 template <typename LessThan>
3996 void sortImplRecursive(range_type &range, row_ptr parentRow, const LessThan &lessThan)
3997 {
3998 for (auto &row : range) {
3999 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
4000 if (QRangeModelDetails::isValid(children)) {
4001 sortImplRecursive(QRangeModelDetails::refTo(children),
4002 QRangeModelDetails::pointerTo(row), lessThan);
4003 }
4004 }
4005 this->sortSubRange(range, parentRow, lessThan);
4006 }
4007
4008 template <typename LessThan>
4009 void sortImpl(const LessThan &lessThan)
4010 {
4011 sortImplRecursive(*this->m_data.model(), nullptr, lessThan);
4012 resetParentInChildren(this->m_data.model());
4013 }
4014
4015 void prunePersistentIndexList(QModelIndexList &list, row_ptr expectedParent)
4016 {
4017 erase_if(list, [expectedParent](const QModelIndex &index){
4018 return static_cast<row_ptr>(index.internalPointer()) != expectedParent;
4019 });
4020 }
4021
4022 void matchImplRecursive(const range_type &range, const_row_ptr parentPtr, int from, int to,
4023 int role, const QVariant &value, int hits, Qt::MatchFlags flags, int column,
4024 QModelIndexList &result, const QCollator &collator) const
4025 {
4026 auto it = QRangeModelDetails::pos(range, from);
4027 auto end = QRangeModelDetails::adl_end(range);
4028
4029 const bool recurse = flags.testAnyFlag(Qt::MatchRecursive);
4030 const bool allHits = (hits == -1);
4031
4032 for (int r = from; it != end && r < to && (allHits || result.size() < hits); ++it, ++r) {
4033 const QModelIndex index = this->createIndex(r, column, parentPtr);
4034 if (this->matchRow(*it, index, role, value, flags, collator))
4035 result.append(index);
4036
4037 if (recurse) {
4038 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
4039
4040 if (QRangeModelDetails::isValid(children)) {
4041 matchImplRecursive(QRangeModelDetails::refTo(children),
4042 QRangeModelDetails::pointerTo(*it), 0,
4043 int(QRangeModelDetails::size(QRangeModelDetails::refTo(children))),
4044 role, value, hits, flags, column, result, collator);
4045 }
4046 }
4047 }
4048 }
4049
4050 QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value, int hits,
4051 Qt::MatchFlags flags, const QCollator &collator) const
4052 {
4053 QModelIndexList result;
4054 const bool wrap = flags.testAnyFlag(Qt::MatchWrap);
4055 const int column = start.column();
4056 const int from = start.row();
4057 const int to = this->rowCount(start.parent());
4058
4059 for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) {
4060 const int fromRow = (i == 0) ? from : 0;
4061 const int toRow = (i == 0) ? to : from;
4062 matchImplRecursive(*this->m_data.model(), nullptr, fromRow, toRow,
4063 role, value, hits, flags, column, result, collator);
4064 }
4065 return result;
4066 }
4067
4068 // tree models don't overwrite the data at index, but instead insert a
4069 // child item
4070 bool dropOnItem(const QMimeData *, const QModelIndex &)
4071 {
4072 return false;
4073 }
4074};
4075
4076// specialization for flat models without protocol
4077template <typename Range>
4080{
4083
4084 static constexpr bool is_mutable_impl = true;
4085
4086public:
4087 using range_type = typename Base::range_type;
4089 using row_type = typename Base::row_type;
4090 using row_ptr = typename Base::row_ptr;
4092 using row_traits = typename Base::row_traits;
4093 using row_features = typename Base::row_features;
4094
4095 explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
4096 : Base(std::forward<Range>(model), {}, itemModel)
4097 {}
4098
4099protected:
4100 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
4101 {
4102 if constexpr (Base::dynamicColumns()) {
4103 if (column < int(Base::size(*QRangeModelDetails::pos(*this->m_data.model(), row))))
4104 return this->createIndex(row, column);
4105#ifndef QT_NO_DEBUG
4106 // if we got here, then column < columnCount(), but this row is too short
4107 qCritical("QRangeModel: Column-range at row %d is not large enough!", row);
4108#endif
4109 return {};
4110 } else {
4111 return this->createIndex(row, column);
4112 }
4113 }
4114
4115 QModelIndex parentImpl(const QModelIndex &) const
4116 {
4117 return {};
4118 }
4119
4120 int rowCountImpl(const QModelIndex &parent) const
4121 {
4122 if (parent.isValid())
4123 return 0;
4124 return int(Base::size(*this->m_data.model()));
4125 }
4126
4127 int columnCountImpl(const QModelIndex &parent) const
4128 {
4129 if (parent.isValid())
4130 return 0;
4131
4132 // in a table, all rows have the same number of columns (as the first row)
4133 if constexpr (Base::dynamicColumns()) {
4134 return int(Base::size(*this->m_data.model()) == 0
4135 ? 0
4136 : Base::size(*QRangeModelDetails::adl_begin(*this->m_data.model())));
4137 } else {
4138 return Base::fixedColumnCount();
4139 }
4140 }
4141
4142 static constexpr Qt::ItemFlags defaultFlags()
4143 {
4144 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
4145 }
4146
4147 static constexpr bool canInsertRowsImpl()
4148 {
4149 return Base::dynamicRows() && range_features::has_insert;
4150 }
4151
4152 static constexpr bool canRemoveRowsImpl()
4153 {
4154 return Base::dynamicRows() && range_features::has_erase;
4155 }
4156
4157 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
4158 {
4159 return !source.isValid() && !destination.isValid();
4160 }
4161
4162 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
4163 {
4164 return !source.isValid() && !destination.isValid();
4165 }
4166
4167 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
4168 const QModelIndex &, int) noexcept
4169 {
4170 // table/flat model: can't move rows between different parents
4171 return false;
4172 }
4173
4174 auto makeEmptyRow(typename Base::row_ptr)
4175 {
4176 row_type empty_row = this->protocol().newRow();
4177
4178 // dynamically sized rows all have to have the same column count
4179 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
4180 if (QRangeModelDetails::isValid(empty_row))
4181 QRangeModelDetails::refTo(empty_row).resize(this->columnCount({}));
4182 }
4183
4184 return empty_row;
4185 }
4186
4187 template <typename It, typename Sentinel>
4188 void deleteRemovedRows(It &&begin, Sentinel &&end)
4189 {
4190 if constexpr (Base::protocol_traits::has_deleteRow) {
4191 for (auto it = begin; it != end; ++it)
4192 this->protocol().deleteRow(std::move(*it));
4193 }
4194 }
4195
4196 decltype(auto) rowDataImpl(const QModelIndex &index) const
4197 {
4198 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
4199 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
4200 }
4201
4202 decltype(auto) rowDataImpl(const QModelIndex &index)
4203 {
4204 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
4205 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
4206 }
4207
4208 const range_type *childRangeImpl(const QModelIndex &) const
4209 {
4210 return nullptr;
4211 }
4212
4213 range_type *childRangeImpl(const QModelIndex &)
4214 {
4215 return nullptr;
4216 }
4217
4218 const range_type &childrenOf(const_row_ptr row) const
4219 {
4220 Q_ASSERT(!row);
4221 return *this->m_data.model();
4222 }
4223
4225 {
4226 Q_ASSERT(!row);
4227 return *this->m_data.model();
4228 }
4229
4230 void resetParentInChildren(range_type *)
4231 {
4232 }
4233
4235 {
4236 bool result = true;
4237 int rowIndex = 0;
4238 for (const auto &row : *this->m_data.model()) {
4239 result &= this->autoConnectPropertiesInRow(row, rowIndex, {});
4240 ++rowIndex;
4241 }
4242 return result;
4243 }
4244
4245 template <typename LessThan>
4246 void sortImpl(const LessThan &lessThan)
4247 {
4248 this->sortSubRange(*this->m_data.model(), nullptr, lessThan);
4249 }
4250
4251 void prunePersistentIndexList(QModelIndexList &, typename Base::row_ptr) {}
4252
4253 QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value,
4254 int hits, Qt::MatchFlags flags, const QCollator &collator) const
4255 {
4256 QModelIndexList result;
4257 const bool wrap = flags.testAnyFlag(Qt::MatchWrap);
4258 const bool allHits = (hits == -1);
4259 const int column = start.column();
4260 int from = start.row();
4261 int to = this->rowCount({});
4262 decltype(auto) siblings = *this->m_data.model();
4263
4264 for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) {
4265 auto it = QRangeModelDetails::pos(siblings, from);
4266 auto end = QRangeModelDetails::adl_end(siblings);
4267 for (int r = from; it != end && r < to && (allHits || result.size() < hits);
4268 ++it, ++r) {
4269 if (!QRangeModelDetails::isValid(*it))
4270 continue;
4271 const QModelIndex index = this->createIndex(r, column, nullptr);
4272 if (this->matchRow(*it, index, role, value, flags, collator))
4273 result.append(index);
4274 }
4275 from = 0;
4276 to = start.row();
4277 }
4278
4279 return result;
4280 }
4281
4282 // flat models can overwrite data of the dropped-on item
4283 bool dropOnItem(const QMimeData *data, const QModelIndex &index)
4284 {
4285 return this->dropDataOnItem(data, index);
4286 }
4287};
4288
4289QT_END_NAMESPACE
4290
4291#endif // Q_QDOC
4292
4293#endif // QRANGEMODEL_IMPL_H
QModelIndex parentImpl(const QModelIndex &) const
static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
void deleteRemovedRows(It &&begin, Sentinel &&end)
const range_type & childrenOf(const_row_ptr row) const
static constexpr bool canRemoveRowsImpl()
bool dropOnItem(const QMimeData *data, const QModelIndex &index)
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)
QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags, const QCollator &collator) const
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
range_type & childrenOf(row_ptr row)
static constexpr Qt::ItemFlags defaultFlags()
void sortImpl(const LessThan &lessThan)
auto makeEmptyRow(typename Base::row_ptr)
static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
bool dropOnItem(const QMimeData *, const QModelIndex &)
auto makeEmptyRow(row_ptr parentRow)
void setParentRow(range_type &children, row_ptr parent)
range_type * childRangeImpl(const QModelIndex &index)
bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow)
void resetParentInChildren(range_type *children)
int rowCountImpl(const QModelIndex &parent) const
range_type & childrenOf(row_ptr row)
QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags, const QCollator &collator) 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)
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 QCollator &collator) const
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)
QStringList mimeTypes() const
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::DropActions adjustSupportedDropActions(Qt::DropActions dropActions)
Qt::ItemFlags flags(const QModelIndex &index) const
bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destParent, int destRow)
void beginRemoveRows(const QModelIndex &parent, int start, int count)
bool clearItemData(const QModelIndex &index)
QVariant data(const QModelIndex &index, int role) const
QRangeModelImplBase(QRangeModel *itemModel)
QModelIndexList persistentIndexList() const
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) 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
static Q_CORE_EXPORT bool matchValue(const QString &itemData, const QVariant &value, Qt::MatchFlags flags, const QCollator &collator)
void beginInsertColumns(const QModelIndex &parent, int start, int count)
bool insertColumns(int column, int count, const QModelIndex &parent)
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
void sort(int column, Qt::SortOrder order)
bool removeRows(int row, int count, const QModelIndex &parent)
Qt::DropActions adjustSupportedDragActions(Qt::DropActions dragActions)
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)
Q_CORE_EXPORT bool dropDataOnItem(const QMimeData *data, const QModelIndex &index)
static Q_CORE_EXPORT bool connectPropertyConst(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, int role, const QMetaProperty &property)
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, typename C::AdjustSupportedDragActions, typename C::AdjustSupportedDropActions, typename C::MimeTypes, typename C::CanDropMimeData, typename C::DropMimeData, typename C::MimeData > MethodTemplates
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
QMimeData * mimeData(const QModelIndexList &indexes) 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)
bool doDropMimeData(std::vector< QRangeModelDetails::DroppedEntry< Entry > > &droppedEntries, DropOperation dropOperation, int row, int column, const QModelIndex &target)
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
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &target)
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 matchRow(const_row_reference row, const QModelIndex &index, int role, const QVariant &value, Qt::MatchFlags flags, const QCollator &collator) const
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
Qt::DropActions adjustSupportedDragActions(Qt::DropActions dragActions)
QRangeModelImpl< Structure, Range, Protocol > Self
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &target) const
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)
QStringList mimeTypes() const
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
QMimeData * mimeData(const QModelIndexList &indexes) const
Qt::DropActions adjustSupportedDropActions(Qt::DropActions dropActions)
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)
decltype(QRangeModelRowOptions< row_type >::mimeTypes()) hasMimeTypes_test
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 hasMimeDataRowSpan
static constexpr bool hasMimeTypes
static constexpr bool has_metaobject_v
static constexpr bool is_range_v
static constexpr bool array_like_v
static constexpr bool hasMimeDataIndexList
static void rotate(C &c, int src, int count, int dst)
static constexpr bool hasDropMimeData
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 bool hasDropMimeDataFull
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 constexpr bool hasCanDropMimeData
static decltype(auto) refTo(T &&t)
static constexpr bool hasRowFlags
static auto pointerTo(T &&t)
static constexpr bool hasCanDropMimeDataFull
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())
friend bool operator==(const Cell &lhs, const Cell &rhs) noexcept
const QModelIndex & index() const
friend decltype(auto) get(const MimeDataEntry &entry)
const wrapped_entry & entry() const
QHash< int, QMetaProperty > properties
static constexpr bool cachesProperties
std::remove_const_t< ModelStorage > m_model
static constexpr bool hasCanDropMimeDataFull
static constexpr bool hasMimeData
decltype(Access::flags(std::declval< const Test & >())) hasFlags_test
static constexpr bool hasReadRole
static constexpr bool hasWriteRole
static constexpr bool hasDropMimeData
decltype(Access::mimeTypes()) hasMimeTypes_test
static constexpr bool hasMimeTypes
static constexpr bool hasDropMimeDataFull
static constexpr bool hasCanDropMimeData
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
std::bidirectional_iterator_tag iterator_category
bool operator!=(const MimeDataItemIterator &other) const
MimeDataItemIterator operator-(difference_type n) const
bool operator==(const MimeDataItemIterator &other) const
MimeDataRowIterator operator-(difference_type n) const
bool operator!=(const MimeDataRowIterator &other) const
bool operator==(const MimeDataRowIterator &other) const
MimeDataRowIterator(base_iterator it, base_iterator begin, base_iterator end, const QRangeModelImpl *model)
std::bidirectional_iterator_tag iterator_category
friend constexpr bool operator<(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
friend constexpr bool operator>(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept