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 inline 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 inline 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 inline 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 // Helper types 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
1016
1017 // implicit conversion from a single int is intentional
1019 Q_IMPLICIT DroppedEntryCell(int row, int column = 0) noexcept
1021 {}
1022
1023 friend bool operator==(const DroppedEntryCell &lhs, const DroppedEntryCell &rhs) noexcept
1024 {
1025 return lhs.m_row == rhs.m_row && lhs.m_column == rhs.m_column;
1026 }
1027 friend bool operator!=(const DroppedEntryCell &lhs, const DroppedEntryCell &rhs) noexcept
1028 {
1029 return !(lhs == rhs);
1030 }
1031 };
1032
1033 template <typename Entry>
1035 {
1037
1038 // implicit conversion from and to entry is intentional
1045
1046 // we only move the actual data out
1047 operator Entry&&() && { return std::move(m_entry); }
1048
1049 Entry m_entry;
1051 };
1052
1069
1070 template <bool cacheProperties, bool itemsAreQObjects>
1072 static constexpr bool cachesProperties = false;
1073
1075 };
1076
1078 {
1079 static constexpr bool cachesProperties = true;
1081
1083 {
1084 properties.clear();
1085 }
1086 protected:
1087 ~PropertyCache() = default;
1088 };
1089
1090 template <>
1091 struct PropertyData<true, false> : PropertyCache
1092 {};
1093
1095 {
1096 struct Connection {
1098 int role;
1099
1100 friend bool operator==(const Connection &lhs, const Connection &rhs) noexcept
1101 {
1102 return lhs.sender == rhs.sender && lhs.role == rhs.role;
1103 }
1104 friend size_t qHash(const Connection &c, size_t seed) noexcept
1105 {
1106 return qHashMulti(seed, c.sender, c.role);
1107 }
1108 };
1109
1112
1113 protected:
1114 ~ConnectionStorage() = default;
1115 };
1116
1117 template <>
1119 {};
1120
1121 template <>
1122 struct PropertyData<false, true> : PropertyData<false, false>, ConnectionStorage
1123 {
1125 };
1126
1127 // The storage of the model data. We might store it as a pointer, or as a
1128 // (copied- or moved-into) value (or smart pointer). But we always return a
1129 // raw pointer.
1130 template <typename ModelStorage, typename = void>
1138
1139 template <typename ModelStorage>
1148
1149 template <typename ModelStorage, typename PropertyStorage>
1151 PropertyStorage
1152 {
1156
1157 auto model() { return QRangeModelDetails::pointerTo(this->m_model); }
1158 auto model() const { return QRangeModelDetails::pointerTo(this->m_model); }
1159
1160 template <typename Model = ModelStorage>
1161 ModelData(Model &&model)
1163 {}
1164 };
1165} // namespace QRangeModelDetails
1166
1167class QRangeModel;
1168// forward declare so that we can declare friends
1169template <typename, typename, typename> class QRangeModelAdapter;
1170
1172{
1173 using Self = QRangeModelImplBase;
1175
1176public:
1177 // keep in sync with QRangeModel::AutoConnectPolicy
1183
1184 // keep in sync with QRangeModel::DropOperation
1193
1194 // overridable prototypes (quasi-pure-virtual methods)
1196 bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
1197 bool setData(const QModelIndex &index, const QVariant &data, int role);
1198 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data);
1199 bool clearItemData(const QModelIndex &index);
1200 bool insertColumns(int column, int count, const QModelIndex &parent);
1201 bool removeColumns(int column, int count, const QModelIndex &parent);
1202 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destParent, int destColumn);
1203 bool insertRows(int row, int count, const QModelIndex &parent);
1204 bool removeRows(int row, int count, const QModelIndex &parent);
1205 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destParent, int destRow);
1206
1207 QModelIndex index(int row, int column, const QModelIndex &parent) const;
1208 QModelIndex sibling(int row, int column, const QModelIndex &index) const;
1209 int rowCount(const QModelIndex &parent) const;
1210 int columnCount(const QModelIndex &parent) const;
1211 Qt::ItemFlags flags(const QModelIndex &index) const;
1212 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
1213 QVariant data(const QModelIndex &index, int role) const;
1214 QMap<int, QVariant> itemData(const QModelIndex &index) const;
1215 inline QHash<int, QByteArray> roleNames() const;
1216 QModelIndex parent(const QModelIndex &child) const;
1217
1218 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
1220
1221 void interfaceVersion(int &version) const;
1222 void sort(int column, Qt::SortOrder order);
1223 QModelIndexList match(const QModelIndex &start, int role, const QVariant &value,
1224 int hits, Qt::MatchFlags flags) const;
1225
1226 Qt::DropActions adjustSupportedDragActions(Qt::DropActions dragActions);
1227 Qt::DropActions adjustSupportedDropActions(Qt::DropActions dropActions);
1229 bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
1230 const QModelIndex &parent) const;
1231 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
1232 const QModelIndex &parent);
1233 QMimeData *mimeData(const QModelIndexList &indexes) const;
1234
1235 // bindings for overriding
1236
1248
1255 using Data = Method<&Self::data>;
1259
1260 // 6.11
1263
1264 // 6.12
1266 using Sort = Method<&Self::sort>;
1270
1275
1276 template <typename C>
1277 using MethodTemplates = std::tuple<
1278 typename C::Destroy,
1279 typename C::InvalidateCaches,
1280 typename C::SetHeaderData,
1281 typename C::SetData,
1282 typename C::SetItemData,
1283 typename C::ClearItemData,
1284 typename C::InsertColumns,
1285 typename C::RemoveColumns,
1286 typename C::MoveColumns,
1287 typename C::InsertRows,
1288 typename C::RemoveRows,
1289 typename C::MoveRows,
1290 typename C::Index,
1291 typename C::Parent,
1292 typename C::Sibling,
1293 typename C::RowCount,
1294 typename C::ColumnCount,
1295 typename C::Flags,
1296 typename C::HeaderData,
1297 typename C::Data,
1298 typename C::ItemData,
1299 typename C::RoleNames,
1300 typename C::MultiData,
1301 typename C::SetAutoConnectPolicy,
1302 typename C::InterfaceVersion,
1303 typename C::Sort,
1304 typename C::Match,
1305 typename C::AdjustSupportedDragActions,
1306 typename C::AdjustSupportedDropActions,
1307 typename C::MimeTypes,
1308 typename C::CanDropMimeData,
1309 typename C::DropMimeData,
1310 typename C::MimeData
1311 >;
1312
1313 static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
1314 static Q_CORE_EXPORT const QRangeModelImplBase *getImplementation(const QRangeModel *model);
1315
1316private:
1317 friend class QRangeModelPrivate;
1319
1320 QRangeModel *m_rangeModel;
1321
1322protected:
1323 explicit QRangeModelImplBase(QRangeModel *itemModel)
1324 : m_rangeModel(itemModel)
1325 {}
1326
1327 inline QModelIndex createIndex(int row, int column, const void *ptr = nullptr) const;
1328 inline QModelIndexList persistentIndexList() const;
1329 inline void changePersistentIndex(const QModelIndex &from, const QModelIndex &to);
1330 inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
1331 const QList<int> &roles);
1332 inline void beginResetModel();
1333 inline void endResetModel();
1334 inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
1335 inline void endInsertColumns();
1336 inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
1337 inline void endRemoveColumns();
1338 inline bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1339 const QModelIndex &destParent, int destRow);
1340 inline void endMoveColumns();
1341 inline void beginInsertRows(const QModelIndex &parent, int start, int count);
1342 inline void endInsertRows();
1343 inline void beginRemoveRows(const QModelIndex &parent, int start, int count);
1344 inline void endRemoveRows();
1345 inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
1346 const QModelIndex &destParent, int destRow);
1347 inline void endMoveRows();
1348 inline void beginLayoutChange();
1349 inline void endLayoutChange();
1350 inline AutoConnectPolicy autoConnectPolicy() const;
1351 inline static Qt::weak_ordering compareData(const QVariant &lhs, const QVariant &rhs,
1352 const QCollator *collator);
1353
1354public:
1355 inline QAbstractItemModel &itemModel();
1356 inline const QAbstractItemModel &itemModel() const;
1357
1358 // implemented in qrangemodel.cpp
1360 const QMetaObject &metaObject);
1362
1363protected:
1364 Q_CORE_EXPORT QScopedValueRollback<bool> blockDataChangedDispatch();
1365
1367 const QMetaObject &metaObject);
1369 Q_CORE_EXPORT static bool connectProperty(const QModelIndex &index, const QObject *item,
1370 QRangeModelDetails::AutoConnectContext *context,
1371 int role, const QMetaProperty &property);
1372 Q_CORE_EXPORT static bool connectPropertyConst(const QModelIndex &index, const QObject *item,
1373 QRangeModelDetails::AutoConnectContext *context,
1374 int role, const QMetaProperty &property);
1375 Q_CORE_EXPORT static bool connectProperties(const QModelIndex &index, const QObject *item,
1376 QRangeModelDetails::AutoConnectContext *context,
1377 const QHash<int, QMetaProperty> &properties);
1378 Q_CORE_EXPORT static bool connectPropertiesConst(const QModelIndex &index, const QObject *item,
1379 QRangeModelDetails::AutoConnectContext *context,
1380 const QHash<int, QMetaProperty> &properties);
1381 Q_CORE_EXPORT int sortRole() const;
1382 Q_CORE_EXPORT const QCollator *sortCollator() const;
1384
1385 Q_CORE_EXPORT static QVariant convertMatchValue(const QVariant &value, Qt::MatchFlags flags);
1386 Q_CORE_EXPORT static bool matchValue(const QString &itemData, const QVariant &value,
1387 Qt::MatchFlags flags);
1388 Q_CORE_EXPORT static bool matchValue(const QString &itemData, const QVariant &value,
1389 Qt::MatchFlags flags, const QCollator &collator);
1390 static bool matchValue(const QVariant &itemData, const QVariant &value, Qt::MatchFlags flags,
1391 const QCollator &collator)
1392 {
1393 if ((flags & Qt::MatchTypeMask) == Qt::MatchExactly)
1394 return itemData == value;
1395 return matchValue(itemData.toString(), value, flags, collator);
1396 }
1397
1398 Q_CORE_EXPORT bool dropDataOnItem(const QMimeData *data, const QModelIndex &index);
1399};
1400
1401template <typename Structure, typename Range,
1402 typename Protocol = QRangeModelDetails::table_protocol_t<Range>>
1407{
1408public:
1418
1420 typename row_traits::item_type>>;
1422 && row_traits::hasMetaObject; // not treated as tuple
1423
1427 >,
1430 >
1431 >;
1433
1434 using const_row_reference = decltype(*std::declval<typename ModelData::const_iterator&>());
1435
1436 static_assert(!QRangeModelDetails::is_any_of<range_type, std::optional>() &&
1438 "Currently, std::optional is not supported for ranges and rows, as "
1439 "it has range semantics in c++26. Once the required behavior is clarified, "
1440 "std::optional for ranges and rows will be supported.");
1441
1442protected:
1443
1444 using Self = QRangeModelImpl<Structure, Range, Protocol>;
1446
1447 Structure& that() { return static_cast<Structure &>(*this); }
1448 const Structure& that() const { return static_cast<const Structure &>(*this); }
1449
1450 template <typename C>
1451 static constexpr int size(const C &c)
1452 {
1453 if (!QRangeModelDetails::isValid(c))
1454 return 0;
1455
1456 if constexpr (QRangeModelDetails::test_size<C>()) {
1457 using std::size;
1458 return int(size(c));
1459 } else {
1460#if defined(__cpp_lib_ranges)
1461 using std::ranges::distance;
1462#else
1463 using std::distance;
1464#endif
1465 using container_type = std::conditional_t<QRangeModelDetails::range_traits<C>::has_cbegin,
1466 const QRangeModelDetails::wrapped_t<C>,
1467 QRangeModelDetails::wrapped_t<C>>;
1468 container_type& container = const_cast<container_type &>(QRangeModelDetails::refTo(c));
1469 return int(distance(QRangeModelDetails::adl_begin(container),
1470 QRangeModelDetails::adl_end(container)));
1471 }
1472 }
1473
1476 static constexpr bool rows_are_owning_or_raw_pointers =
1479 static constexpr bool one_dimensional_range = static_column_count == 0;
1480
1482 {
1483 if constexpr (itemsAreQObjects || rowsAreQObjects)
1484 return this->blockDataChangedDispatch();
1485 else
1486 return false;
1487 }
1488
1489 // A row might be a value (or range of values), or a pointer.
1490 // row_ptr is always a pointer, and const_row_ptr is a pointer to const.
1493
1494 template <typename T>
1497
1498 // A iterator type to use as the input iterator with the
1499 // range_type::insert(pos, start, end) overload if available (it is in
1500 // std::vector, but not in QList). Generates a prvalue when dereferenced,
1501 // which then gets moved into the newly constructed row, which allows us to
1502 // implement insertRows() for move-only row types.
1504 {
1508 using iterator_category = std::input_iterator_tag;
1509 using difference_type = int;
1510
1511 value_type operator*() { return impl->makeEmptyRow(parentRow); }
1512 EmptyRowGenerator &operator++() { ++n; return *this; }
1513 friend bool operator==(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1514 { return lhs.n == rhs.n; }
1515 friend bool operator!=(const EmptyRowGenerator &lhs, const EmptyRowGenerator &rhs) noexcept
1516 { return !(lhs == rhs); }
1517
1519 Structure *impl = nullptr;
1520 const row_ptr parentRow = nullptr;
1521 };
1522
1523 // If we have a move-only row_type and can add/remove rows, then the range
1524 // must have an insert-from-range overload.
1527 "The range holding a move-only row-type must support insert(pos, start, end)");
1528
1531
1532public:
1533 static constexpr bool isMutable()
1534 {
1535 return range_features::is_mutable && row_features::is_mutable
1536 && std::is_reference_v<row_reference>
1537 && Structure::is_mutable_impl;
1538 }
1539 static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
1540 static constexpr bool dynamicColumns() { return static_column_count < 0; }
1541
1542 explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
1546 {
1547 }
1548
1549
1550 // static interface, called by QRangeModelImplBase
1551
1552 void interfaceVersion(int &versionNumber) const
1553 {
1554 versionNumber = QT_VERSION;
1555 }
1556
1557 void invalidateCaches() { m_data.invalidateCaches(); }
1558
1559 // Not implemented
1560 bool setHeaderData(int , Qt::Orientation , const QVariant &, int ) { return false; }
1561
1562 // actual implementations
1563 QModelIndex index(int row, int column, const QModelIndex &parent) const
1564 {
1565 if (row < 0 || column < 0 || column >= columnCount(parent)
1566 || row >= rowCount(parent)) {
1567 return {};
1568 }
1569
1570 return that().indexImpl(row, column, parent);
1571 }
1572
1573 QModelIndex sibling(int row, int column, const QModelIndex &index) const
1574 {
1575 if (row == index.row() && column == index.column())
1576 return index;
1577
1578 // we use indexes at column -1 in drag'n'drop handling to mark full rows
1579 if (column >= this->columnCount({}))
1580 return {};
1581
1582 if (row == index.row())
1583 return this->createIndex(row, column, index.constInternalPointer());
1584
1585 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
1586 const auto siblingCount = size(that().childrenOf(parentRow));
1587 if (row < 0 || row >= int(siblingCount))
1588 return {};
1589 return this->createIndex(row, column, parentRow);
1590 }
1591
1592 Qt::ItemFlags flags(const QModelIndex &index) const
1593 {
1594 if (!index.isValid()) {
1595 if constexpr (isMutable())
1596 return Qt::ItemIsDropEnabled;
1597 else
1598 return Qt::NoItemFlags;
1599 }
1600
1601 // try customization
1602 std::optional<Qt::ItemFlags> customFlags = std::nullopt;
1603 if constexpr (QRangeModelDetails::hasRowFlags<wrapped_row_type>) {
1604 const_row_reference row = rowData(index);
1605 if (QRangeModelDetails::isValid(row)) {
1606 customFlags = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::flags(
1607 QRangeModelDetails::refTo(row)
1608 );
1609 }
1610 }
1611
1612 readAt(index, [&customFlags](auto &&ref){
1613 Q_UNUSED(ref);
1614 using wrapped_value_type = q20::remove_cvref_t<QRangeModelDetails::wrapped_t<decltype(ref)>>;
1615 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasFlags) {
1616 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1617 if (QRangeModelDetails::isValid(ref)) {
1618 customFlags = ItemAccess::flags(QRangeModelDetails::refTo(ref));
1619 return true;
1620 }
1621 }
1622 return false;
1623 });
1624
1625 Qt::ItemFlags f = customFlags ? *customFlags : Structure::defaultFlags();
1626 // adjust custom flags based on what is not possible
1627 if constexpr (!isMutable())
1628 f &= ~(Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
1629 if (index.column())
1630 f |= Qt::ItemNeverHasChildren;
1631 if (customFlags)
1632 return f;
1633
1634 // compute flags ourselves
1635 if (!this->itemModel().mimeTypes().isEmpty()) {
1636 f |= Qt::ItemIsDragEnabled;
1637 if constexpr (isMutable())
1638 f |= Qt::ItemIsDropEnabled;
1639 }
1640
1641 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<row_type>()) {
1642 // pointer rows might be null
1643 const_row_reference row = rowData(index);
1644 if (!QRangeModelDetails::isValid(row))
1645 f &= ~Qt::ItemIsDragEnabled;
1646 }
1647
1648 if constexpr (isMutable()) {
1649 // Note: Read-only items are still droppable - we can't know here
1650 // whether the model will insert data as new rows or children, or if
1651 // it will overwrite the data of the dropped-on item. So we allow
1652 // dropping on items that are not editable.
1653 if constexpr (row_traits::hasMetaObject) {
1654 if (index.column() < row_traits::fixed_size()) {
1655 const QMetaObject mo = wrapped_row_type::staticMetaObject;
1656 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
1657 if (prop.isWritable())
1658 f |= Qt::ItemIsEditable;
1659 }
1660 } else if constexpr (static_column_count <= 0) {
1661 using item_type = typename row_traits::item_type;
1662 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<item_type>()) {
1663 // pointer items might be null
1664 if (!readAt(index, [](auto &&i){ return QRangeModelDetails::isValid(i); }))
1665 f &= ~Qt::ItemIsDragEnabled;
1666 }
1667 f |= Qt::ItemIsEditable;
1668 } else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
1669 // we want to know if the elements in the tuple are const; they'd always be, if
1670 // we didn't remove the const of the range first.
1671 const_row_reference row = rowData(index);
1672 row_reference mutableRow = const_cast<row_reference>(row);
1673 if (QRangeModelDetails::isValid(mutableRow)) {
1674 row_traits::for_element_at(mutableRow, index.column(), [&f](auto &&ref){
1675 using target_type = decltype(ref);
1676 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<target_type>()) {
1677 // pointer items might be null
1678 if (!QRangeModelDetails::isValid(ref))
1679 f &= ~Qt::ItemIsDragEnabled;
1680 }
1681 if constexpr (std::is_const_v<std::remove_reference_t<target_type>>)
1682 f &= ~Qt::ItemIsEditable;
1683 else if constexpr (std::is_lvalue_reference_v<target_type>)
1684 f |= Qt::ItemIsEditable;
1685 });
1686 } else {
1687 // If there's no usable value stored in the row, then we can't
1688 // do anything with this item, except perhaps drop data into it
1689 f &= ~Qt::ItemIsEditable;
1690 }
1691 }
1692 }
1693 return f;
1694 }
1695
1696 QVariant headerData(int section, Qt::Orientation orientation, int role) const
1697 {
1698 QVariant result;
1699 if constexpr (QRangeModelDetails::hasHeaderData<wrapped_row_type>) {
1700 if (orientation == Qt::Horizontal) {
1701 result = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::headerData(
1702 section, role
1703 );
1704 if (result.isValid())
1705 return result;
1706 }
1707 }
1708
1709 if (role != Qt::DisplayRole || orientation != Qt::Horizontal
1710 || section < 0 || section >= columnCount({})) {
1711 return this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1712 }
1713
1714 result = row_traits::column_name(section);
1715 if (!result.isValid())
1716 result = this->itemModel().QAbstractItemModel::headerData(section, orientation, role);
1717 return result;
1718 }
1719
1720 QVariant data(const QModelIndex &index, int role) const
1721 {
1722 if (!index.isValid())
1723 return {};
1724
1725 QModelRoleData result(role);
1726 multiData(index, result);
1727 return std::move(result.data());
1728 }
1729
1730 static constexpr bool isRangeModelRole(int role)
1731 {
1732 return role == Qt::RangeModelDataRole
1733 || role == Qt::RangeModelAdapterRole;
1734 }
1735
1736 static constexpr bool isPrimaryRole(int role)
1737 {
1738 return role == Qt::DisplayRole || role == Qt::EditRole;
1739 }
1740
1741 QMap<int, QVariant> itemData(const QModelIndex &index) const
1742 {
1743 QMap<int, QVariant> result;
1744
1745 if (index.isValid()) {
1746 // optimisation for items backed by a QMap<int, QVariant> or equivalent
1747 if (!readAt(index, [&result](const auto &value) {
1748 if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) {
1749 result = value;
1750 return true;
1751 }
1752 return false;
1753 })) {
1754 const auto roles = this->itemModel().roleNames().keys();
1755 QVarLengthArray<QModelRoleData, 16> roleDataArray;
1756 roleDataArray.reserve(roles.size());
1757 for (auto role : roles) {
1758 if (isRangeModelRole(role))
1759 continue;
1760 roleDataArray.emplace_back(role);
1761 }
1762 QModelRoleDataSpan roleDataSpan(roleDataArray);
1763 multiData(index, roleDataSpan);
1764
1765 for (QModelRoleData &roleData : roleDataSpan) {
1766 if (roleData.data().isValid())
1767 result[roleData.role()] = std::move(roleData.data());
1768 }
1769 }
1770 }
1771 return result;
1772 }
1773
1775 {
1776 template <typename value_type>
1777 bool operator()(const value_type &value) const
1778 {
1779 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1780 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1781
1782 const auto readModelData = [&value](QModelRoleData &roleData){
1783 if (!QRangeModelDetails::isValid(value)) {
1784 roleData.clearData();
1785 return true;
1786 }
1787
1788 const int role = roleData.role();
1789 if (role == Qt::RangeModelDataRole) {
1790 // Qt QML support: "modelData" role returns the entire multi-role item.
1791 // QML can only use raw pointers to QObject (so we unwrap), and gadgets
1792 // only by value (so we take the reference).
1793 if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
1794 roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
1795 else
1796 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1797 } else if (role == Qt::RangeModelAdapterRole) {
1798 // for QRangeModelAdapter however, we want to respect smart pointer wrappers
1799 if constexpr (std::is_copy_assignable_v<value_type>)
1800 roleData.setData(QVariant::fromValue(value));
1801 else
1802 roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
1803 } else {
1804 return false;
1805 }
1806 return true;
1807 };
1808
1809 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasReadRole) {
1810 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1811 for (auto &roleData : roleDataSpan) {
1812 if (!readModelData(roleData)) {
1813 roleData.setData(ItemAccess::readRole(QRangeModelDetails::refTo(value),
1814 roleData.role()));
1815 }
1816 }
1817 } else if constexpr (multi_role()) {
1818 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1819 Q_UNUSED(this);
1820 if constexpr (!multi_role::int_key)
1821 return that->itemModel().roleNames();
1822 else
1823 return {};
1824 }();
1825 using key_type = typename value_type::key_type;
1826 for (auto &roleData : roleDataSpan) {
1827 const auto &it = [&roleNames, &value, role = roleData.role()]{
1828 Q_UNUSED(roleNames);
1829 if constexpr (multi_role::int_key)
1830 return value.find(key_type(role));
1831 else
1832 return value.find(roleNames.value(role));
1833 }();
1834 if (it != QRangeModelDetails::adl_end(value))
1835 roleData.setData(QRangeModelDetails::value(it));
1836 else
1837 roleData.clearData();
1838 }
1839 } else if constexpr (has_metaobject<value_type>) {
1840 if (row_traits::fixed_size() <= 1) {
1841 for (auto &roleData : roleDataSpan) {
1842 if (!readModelData(roleData)) {
1843 roleData.setData(that->readRole(index, roleData.role(),
1844 QRangeModelDetails::pointerTo(value)));
1845 }
1846 }
1847 } else if (index.column() <= row_traits::fixed_size()) {
1848 for (auto &roleData : roleDataSpan) {
1849 const int role = roleData.role();
1850 if (isPrimaryRole(role)) {
1851 roleData.setData(that->readProperty(index,
1852 QRangeModelDetails::pointerTo(value)));
1853 } else {
1854 roleData.clearData();
1855 }
1856 }
1857 }
1858 } else {
1859 for (auto &roleData : roleDataSpan) {
1860 const int role = roleData.role();
1861 if (isPrimaryRole(role) || isRangeModelRole(role))
1862 roleData.setData(read(value));
1863 else
1864 roleData.clearData();
1865 }
1866 }
1867 return true;
1868 }
1869
1872 const QRangeModelImpl * const that;
1873 };
1874
1875 void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1876 {
1877 if (!readAt(index, ItemReader{index, roleDataSpan, this})) {
1878 for (auto &roleData : roleDataSpan)
1879 roleData.clearData();
1880 }
1881 }
1882
1883 bool setData(const QModelIndex &index, const QVariant &data, int role)
1884 {
1885 if (!index.isValid())
1886 return false;
1887
1888 if constexpr (isMutable()) {
1889 auto emitDataChanged = qScopeGuard([this, &index, role]{
1890 Q_EMIT this->dataChanged(index, index,
1891 role == Qt::EditRole || role == Qt::RangeModelDataRole
1892 || role == Qt::RangeModelAdapterRole
1893 ? QList<int>{} : QList<int>{role});
1894 });
1895 // we emit dataChanged at the end, block dispatches from auto-connected properties
1896 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
1897
1898 const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
1899 using value_type = q20::remove_cvref_t<decltype(target)>;
1900 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
1901 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
1902
1903 if constexpr (std::conjunction_v<QRangeModelDetails::is_any_owning_ptr<value_type>,
1904 std::is_default_constructible<wrapped_value_type>>) {
1905 if (!QRangeModelDetails::isValid(target))
1906 target.reset(new wrapped_value_type);
1907 }
1908
1909 auto setRangeModelDataRole = [&target, &data]{
1910 constexpr auto targetMetaType = QMetaType::fromType<value_type>();
1911 const auto dataMetaType = data.metaType();
1912 constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
1913 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
1914 // we don't support replacing objects that are stored as raw pointers,
1915 // as this makes object ownership very messy. But we can replace objects
1916 // stored in smart pointers, and we can initialize raw nullptr objects.
1917 if constexpr (isWrapped) {
1918 constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
1919 if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
1920 if (data.canConvert(targetMetaType)) {
1921 target = data.value<value_type>();
1922 return true;
1923 }
1924 } else if constexpr (is_raw_pointer) {
1925 if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
1926 target = data.value<value_type>();
1927 return true;
1928 }
1929 } else {
1930 Q_UNUSED(target);
1931 }
1932 }
1933 // Otherwise we have a move-only or polymorph type. fall through to
1934 // error handling.
1935 } else if constexpr (isWrapped) {
1936 if (QRangeModelDetails::isValid(target)) {
1937 auto &targetRef = QRangeModelDetails::refTo(target);
1938 // we need to get a wrapped value type out of the QVariant, which
1939 // might carry a pointer. We have to try all alternatives.
1940 if (const auto mt = QMetaType::fromType<wrapped_value_type>();
1941 data.canConvert(mt)) {
1942 targetRef = data.value<wrapped_value_type>();
1943 return true;
1944 } else if (const auto mtp = QMetaType::fromType<wrapped_value_type *>();
1945 data.canConvert(mtp)) {
1946 targetRef = *data.value<wrapped_value_type *>();
1947 return true;
1948 }
1949 }
1950 } else if (targetMetaType == dataMetaType) {
1951 QRangeModelDetails::refTo(target) = data.value<value_type>();
1952 return true;
1953 } else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1954 QRangeModelDetails::refTo(target) = *data.value<value_type *>();
1955 return true;
1956 }
1957#ifndef QT_NO_DEBUG
1958 qCritical("Not able to assign %s to %s",
1959 qPrintable(QDebug::toString(data)), targetMetaType.name());
1960#endif
1961 return false;
1962 };
1963
1964 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasWriteRole) {
1965 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
1966 if (isRangeModelRole(role))
1967 return setRangeModelDataRole();
1968 return ItemAccess::writeRole(QRangeModelDetails::refTo(target), data, role);
1969 } else if constexpr (has_metaobject<value_type>) {
1970 if (row_traits::fixed_size() <= 1) { // multi-role value
1971 if (isRangeModelRole(role))
1972 return setRangeModelDataRole();
1973 return writeRole(role, QRangeModelDetails::pointerTo(target), data);
1974 } else if (column <= row_traits::fixed_size() // multi-column
1975 && (isPrimaryRole(role) || isRangeModelRole(role))) {
1976 return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
1977 }
1978 } else if constexpr (multi_role::value) {
1979 Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
1980 // If there is an entry for EditRole, overwrite that; otherwise,
1981 // set the entry for DisplayRole.
1982 const auto roleNames = [this]() -> QHash<int, QByteArray> {
1983 Q_UNUSED(this);
1984 if constexpr (!multi_role::int_key)
1985 return this->itemModel().roleNames();
1986 else
1987 return {};
1988 }();
1989 if (role == Qt::EditRole) {
1990 if constexpr (multi_role::int_key) {
1991 if (target.find(roleToSet) == target.end())
1992 roleToSet = Qt::DisplayRole;
1993 } else {
1994 if (target.find(roleNames.value(roleToSet)) == target.end())
1995 roleToSet = Qt::DisplayRole;
1996 }
1997 }
1998 if constexpr (multi_role::int_key)
1999 return write(target[roleToSet], data);
2000 else
2001 return write(target[roleNames.value(roleToSet)], data);
2002 } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
2003 return write(target, data);
2004 }
2005 return false;
2006 };
2007
2008 if (!writeAt(index, writeData)) {
2009 emitDataChanged.dismiss();
2010 return false;
2011 } else if constexpr (itemsAreQObjects || rowsAreQObjects) {
2012 if (isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2013 if (QObject *item = data.value<QObject *>())
2014 Self::connectProperties(index, item, m_data.context, m_data.properties);
2015 }
2016 }
2017 return true;
2018 }
2019 return false;
2020 }
2021
2022 template <typename LHS, typename RHS>
2023 void updateTarget(LHS &org, RHS &&copy) noexcept
2024 {
2025 if constexpr (std::is_pointer_v<RHS>)
2026 return;
2027 else if constexpr (std::is_assignable_v<LHS, RHS>)
2028 org = std::forward<RHS>(copy);
2029 else
2030 qSwap(org, copy);
2031 }
2032 template <typename LHS, typename RHS>
2033 void updateTarget(LHS *org, RHS &&copy) noexcept
2034 {
2035 updateTarget(*org, std::forward<RHS>(copy));
2036 }
2037
2038 bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
2039 {
2040 if (!index.isValid() || data.isEmpty())
2041 return false;
2042
2043 if constexpr (isMutable()) {
2044 auto emitDataChanged = qScopeGuard([this, &index, &data]{
2045 Q_EMIT this->dataChanged(index, index, data.keys());
2046 });
2047 // we emit dataChanged at the end, block dispatches from auto-connected properties
2048 [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
2049
2050 bool tried = false;
2051 auto writeItemData = [this, &tried, &data](auto &target) -> bool {
2052 Q_UNUSED(this);
2053 using value_type = q20::remove_cvref_t<decltype(target)>;
2054 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
2055 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
2056
2057 // transactional: if possible, modify a copy and only
2058 // update target if all values from data could be stored.
2059 auto makeCopy = [](const value_type &original){
2060 if constexpr (!std::is_copy_assignable_v<wrapped_value_type>)
2061 return QRangeModelDetails::pointerTo(original); // no transaction support
2062 else if constexpr (std::is_pointer_v<decltype(original)>)
2063 return *original;
2064 else if constexpr (std::is_copy_assignable_v<value_type>)
2065 return original;
2066 else
2067 return QRangeModelDetails::pointerTo(original);
2068 };
2069
2070 const auto roleNames = this->itemModel().roleNames();
2071
2072 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasWriteRole) {
2073 tried = true;
2074 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
2075 const auto roles = roleNames.keys();
2076 auto targetCopy = makeCopy(target);
2077 for (int role : roles) {
2078 if (!ItemAccess::writeRole(QRangeModelDetails::refTo(targetCopy),
2079 data.value(role), role)) {
2080 return false;
2081 }
2082 }
2083 updateTarget(target, std::move(targetCopy));
2084 return true;
2085 } else if constexpr (multi_role()) {
2086 using key_type = typename value_type::key_type;
2087 tried = true;
2088 const auto roleName = [&roleNames](int role) {
2089 return roleNames.value(role);
2090 };
2091
2092 // transactional: only update target if all values from data
2093 // can be stored. Storing never fails with int-keys.
2094 if constexpr (!multi_role::int_key)
2095 {
2096 auto invalid = std::find_if(data.keyBegin(), data.keyEnd(),
2097 [&roleName](int role) { return roleName(role).isEmpty(); }
2098 );
2099
2100 if (invalid != data.keyEnd()) {
2101#ifndef QT_NO_DEBUG
2102 qWarning("No role name set for %d", *invalid);
2103#endif
2104 return false;
2105 }
2106 }
2107
2108 for (auto &&[role, value] : data.asKeyValueRange()) {
2109 if constexpr (multi_role::int_key)
2110 target[static_cast<key_type>(role)] = value;
2111 else
2112 target[QString::fromUtf8(roleName(role))] = value;
2113 }
2114 return true;
2115 } else if constexpr (has_metaobject<value_type>) {
2116 if (row_traits::fixed_size() <= 1) {
2117 tried = true;
2118 auto targetCopy = makeCopy(target);
2119 for (auto &&[role, value] : data.asKeyValueRange()) {
2120 if (isRangeModelRole(role))
2121 continue;
2122 if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
2123 const QByteArray roleName = roleNames.value(role);
2124#ifndef QT_NO_DEBUG
2125 qWarning("Failed to write value '%s' to role '%s'",
2126 qPrintable(QDebug::toString(value)), roleName.data());
2127#endif
2128 return false;
2129 }
2130 }
2131 updateTarget(target, std::move(targetCopy));
2132 return true;
2133 }
2134 }
2135 return false;
2136 };
2137
2138 if (!writeAt(index, writeItemData)) {
2139 emitDataChanged.dismiss();
2140 if (!tried)
2141 return this->itemModel().QAbstractItemModel::setItemData(index, data);
2142 }
2143 return true;
2144 }
2145 return false;
2146 }
2147
2148 bool clearItemData(const QModelIndex &index)
2149 {
2150 if (!index.isValid())
2151 return false;
2152
2153 if constexpr (isMutable()) {
2154 auto emitDataChanged = qScopeGuard([this, &index]{
2155 Q_EMIT this->dataChanged(index, index, {});
2156 });
2157
2158 auto clearData = [column = index.column()](auto &&target) {
2159 if constexpr (row_traits::hasMetaObject) {
2160 if (row_traits::fixed_size() <= 1) {
2161 // multi-role object/gadget: reset all properties
2162 return resetProperty(-1, QRangeModelDetails::pointerTo(target));
2163 } else if (column <= row_traits::fixed_size()) {
2164 return resetProperty(column, QRangeModelDetails::pointerTo(target));
2165 }
2166 } else { // normal structs, values, associative containers
2167 target = {};
2168 return true;
2169 }
2170 return false;
2171 };
2172
2173 if (!writeAt(index, clearData)) {
2174 emitDataChanged.dismiss();
2175 return false;
2176 }
2177 return true;
2178 }
2179 return false;
2180 }
2181
2183 {
2184 // will be 'void' if columns don't all have the same type
2185 using item_type = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
2186 using item_traits = typename QRangeModelDetails::item_traits<item_type>;
2187 return item_traits::roleNames(this);
2188 }
2189
2190 bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
2191 {
2192 if (!QRangeModelDetails::isValid(row))
2193 return true; // nothing to do
2194 return row_traits::for_each_element(QRangeModelDetails::refTo(row),
2195 this->itemModel().index(rowIndex, 0, parent),
2196 [this](const QModelIndex &index, const QObject *item) {
2197 if constexpr (isMutable())
2198 return Self::connectProperties(index, item, m_data.context, m_data.properties);
2199 else
2200 return Self::connectPropertiesConst(index, item, m_data.context, m_data.properties);
2201 });
2202 }
2203
2204 void clearConnectionInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
2205 {
2206 if (!QRangeModelDetails::isValid(row))
2207 return;
2208 row_traits::for_each_element(QRangeModelDetails::refTo(row),
2209 this->itemModel().index(rowIndex, 0, parent),
2210 [this](const QModelIndex &, const QObject *item) {
2211 m_data.connections.removeIf([item](const auto &connection) {
2212 return connection.sender == item;
2213 });
2214 return true;
2215 });
2216 }
2217
2219 {
2220 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2221 using item_type = std::remove_pointer_t<typename row_traits::item_type>;
2222 using Mapping = QRangeModelDetails::AutoConnectContext::AutoConnectMapping;
2223
2224 delete m_data.context;
2225 m_data.connections = {};
2226 switch (this->autoConnectPolicy()) {
2227 case AutoConnectPolicy::None:
2228 m_data.context = nullptr;
2229 break;
2230 case AutoConnectPolicy::Full:
2231 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
2232 if constexpr (itemsAreQObjects) {
2233 m_data.properties = QRangeModelImplBase::roleProperties(this->itemModel(),
2234 item_type::staticMetaObject);
2235 m_data.context->mapping = Mapping::Roles;
2236 } else {
2237 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
2238 m_data.context->mapping = Mapping::Columns;
2239 }
2240 if (!m_data.properties.isEmpty())
2241 that().autoConnectPropertiesImpl();
2242 break;
2243 case AutoConnectPolicy::OnRead:
2244 m_data.context = new QRangeModelDetails::AutoConnectContext(&this->itemModel());
2245 if constexpr (itemsAreQObjects) {
2246 m_data.context->mapping = Mapping::Roles;
2247 } else {
2248 m_data.properties = QRangeModelImplBase::columnProperties(wrapped_row_type::staticMetaObject);
2249 m_data.context->mapping = Mapping::Columns;
2250 }
2251 break;
2252 }
2253 } else {
2254#ifndef QT_NO_DEBUG
2255 qWarning("All items in the range must be QObject subclasses");
2256#endif
2257 }
2258 }
2259
2260 struct unordered {
2261 friend constexpr bool operator<(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
2262 { return false; }
2263 friend constexpr bool operator>(unordered, QtPrivate::CompareAgainstLiteralZero) noexcept
2264 { return false; }
2265 };
2266
2267 struct Compare
2268 {
2269 template <typename C, typename LessThan>
2270 using sortMember_test = decltype(std::declval<C&>().sort(std::declval<LessThan &&>()));
2271 static constexpr bool hasSortMember = qxp::is_detected_v<sortMember_test, range_type, Compare>;
2272
2273 template <typename Stringish>
2274 using collatedCompare_test = decltype(
2275 std::declval<const QCollator&>().compare(std::declval<const Stringish&>(),
2276 std::declval<const Stringish&>())
2277 );
2278 template <typename Stringish>
2280 Stringish>;
2281
2282
2283 Compare(const QRangeModelImpl *impl, int column, Qt::SortOrder order)
2284 : that(impl), m_index(impl->createIndex(-1, column, nullptr))
2285 , collator(impl->sortCollator()), m_order(order), m_sortRole(impl->sortRole())
2286 {
2287 }
2288
2289 template <typename Item>
2290 auto operator()(const Item &lhs, const Item &rhs) const
2291 {
2292 auto ordering = compare(lhs, rhs);
2293 return m_order == Qt::AscendingOrder ? ordering < 0 : ordering > 0;
2294 }
2295
2296 template <typename Item>
2297 auto compare(const Item &lhs, const Item &rhs) const
2298 {
2299 using value_type = QRangeModelDetails::wrapped_t<Item>;
2300 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
2301
2302 if constexpr (QRangeModelDetails::item_access<value_type>::hasReadRole
2303 || multi_role() || has_metaobject<value_type>) {
2304 QModelRoleData result(m_sortRole);
2305 // Minor abuse of QModelIndex: the reader needs an index to implement
2306 // lazy auto-connections, but we only have a column. So we construct
2307 // an invalid QModelIndex that carries only that column value. That's
2308 // enough for reading values, and the auto-connection logic skips for
2309 // invalid indexes.
2310 ItemReader reader{m_index, result, that};
2311 Q_ASSERT(!reader.index.isValid());
2312 reader(lhs);
2313 const QVariant lhsVariant = std::move(result.data());
2314 reader(rhs);
2315 const QVariant rhsVariant = std::move(result.data());
2316 return QRangeModelImplBase::compareData(lhsVariant, rhsVariant, collator);
2317 } else if constexpr (std::is_same_v<QVariant, value_type>) {
2318 return QRangeModelImplBase::compareData(lhs, rhs, collator);
2319 } else if constexpr (QtOrderingPrivate::CompareThreeWayTester::hasCompareThreeWay_v
2320 <value_type, value_type>) {
2321 // all types supported by QCollator are also three-way comparable
2322 if constexpr (hasCollatedCompare<value_type>) {
2323 if (collator) {
2324 using ordering = decltype(qCompareThreeWay(lhs, rhs));
2325 int res = collator->compare(lhs, rhs);
2326 if (res < 0)
2327 return ordering::less;
2328 if (res > 0)
2329 return ordering::greater;
2330 return ordering::equal;
2331 }
2332 }
2333 return qCompareThreeWay(lhs, rhs);
2334 } else {
2335 return unordered{};
2336 }
2337 }
2338
2339 bool checkComparable() const
2340 {
2341 return that->readAt(that->index(0, 0, {}), [this](const auto &item){
2342 // before we call std::stable_sort, check that we can compare the
2343 // types we'll ultimately get called with. This doesn't catch cases
2344 // where we end up comparing QVariant, and we cannot make this a
2345 // compile time check as long as readAt etc are not constexpr.
2346 using ordering = decltype(compare(item, item));
2347 if constexpr (std::is_same_v<ordering, unordered>) {
2348#ifndef QT_NO_DEBUG
2349 const QMetaType itemtype = QMetaType::fromType<QRangeModelDetails::wrapped_t<
2350 q20::remove_cvref_t<decltype(item)>>
2351 >();
2352 qCritical("QRangeModel: Cannot compare items of type %s in column %d!",
2353 itemtype.name(), m_index.column());
2354#else
2355 Q_UNUSED(this);
2356#endif
2357 return false;
2358 } else {
2359 return true;
2360 }
2361 });
2362 }
2363
2364 template <typename Item>
2365 static std::optional<bool> compareInvalid(const Item &lhs, const Item &rhs)
2366 {
2367 // invalid data > valid data
2368 if (!QRangeModelDetails::isValid(lhs))
2369 return false;
2370 if (!QRangeModelDetails::isValid(rhs))
2371 return true;
2372 return std::nullopt;
2373 }
2374
2375 const QRangeModelImpl * const that;
2377 const QCollator * const collator;
2379 const int m_sortRole;
2380 };
2381
2382 void sort(int column, Qt::SortOrder order)
2383 {
2384 if constexpr (isMutable() && std::is_swappable_v<row_type>) {
2385 if (rowCount({}) < 2 || column >= columnCount({}))
2386 return;
2387 Compare compare(this, column, order);
2388 if (!compare.checkComparable())
2389 return;
2390
2391 this->beginLayoutChange();
2392 QScopeGuard endLayoutChange([this]{ this->endLayoutChange(); });
2393 that().sortImpl([&compare](const auto &leftRow, const auto &rightRow) {
2394 if (auto anyInvalid = Compare::compareInvalid(leftRow, rightRow))
2395 return *anyInvalid;
2396 return row_traits::for_element_at(leftRow, compare.m_index.column(),
2397 [&rightRow, &compare](const auto &leftItem){
2398 return row_traits::for_element_at(rightRow, compare.m_index.column(),
2399 [&leftItem, &compare](const auto &rightItem){
2400 // Called by std::stable_sort. Since "column" is a runtime value, we
2401 // can't statically assert that lhs and rhs are of the same type.
2402 if constexpr (std::is_same_v<decltype(leftItem), decltype(rightItem)>) {
2403 if (auto anyInvalid = Compare::compareInvalid(leftItem, rightItem))
2404 return *anyInvalid;
2405 return compare(QRangeModelDetails::refTo(leftItem),
2406 QRangeModelDetails::refTo(rightItem));
2407 } else {
2408 Q_UNREACHABLE();
2409 }
2410 return false;
2411 });
2412 });
2413 });
2414 }
2415 }
2416
2417 template <typename LessThan>
2418 void sortSubRange(range_type &range, row_ptr expectedParent, const LessThan &lessThan)
2419 {
2420 auto begin = QRangeModelDetails::adl_begin(range);
2421 auto end = QRangeModelDetails::adl_end(range);
2422 if (begin == end)
2423 return;
2424
2425 QModelIndexList persistentIndexes = this->persistentIndexList();
2426 that().prunePersistentIndexList(persistentIndexes, expectedParent);
2427
2428 if (persistentIndexes.isEmpty()) {
2429 using It = typename range_features::iterator;
2430 constexpr bool is_random_access = std::is_base_of_v<std::random_access_iterator_tag,
2431 typename std::iterator_traits<It>::iterator_category>;
2432 // fast path if we have no persistent indexes: sort the range in place
2433 if constexpr (Compare::hasSortMember) {
2434 range.sort(lessThan);
2435 return;
2436 } else if constexpr (is_random_access) {
2437 std::stable_sort(begin, end, lessThan);
2438 return;
2439 }
2440 }
2441
2442 // slow path: create an indexed version of the range by adding a
2443 // column that records row movements.
2444 struct SortTracker
2445 {
2446 row_type row;
2447 int index;
2448 };
2449
2450 const int rangeSize = size(range);
2451 // Allocate all necessary memory here so that a potential exception
2452 // gets thrown before we have made any modifications.
2453 std::vector<SortTracker> tracked;
2454 tracked.reserve(rangeSize);
2455 std::vector<int> newRows;
2456 newRows.resize(rangeSize);
2457
2458 // move all rows into that index paired with its unsorted position
2459 int row = -1;
2460 for (auto &&it = std::move_iterator(begin); it != std::move_iterator(end); ++it)
2461 tracked.emplace_back(SortTracker{*it, ++row});
2462
2463 // sort the index based on a comparions of the data
2464 std::stable_sort(tracked.begin(), tracked.end(),
2465 [&lessThan](const SortTracker &lhs, const SortTracker &rhs){
2466 return lessThan(lhs.row, rhs.row);
2467 });
2468
2469 // write the values back to the range in (now ordered) sequence,
2470 // and create a mapping from old to new row
2471 auto sorted = std::move_iterator(tracked.begin());
2472 auto write = QRangeModelDetails::adl_begin(range);
2473 qsizetype changedIndexCount = 0;
2474 for (int newIndex = 0; newIndex < rangeSize; ++write, ++sorted, ++newIndex) {
2475 auto &&tracker = *sorted;
2476 changedIndexCount += (tracker.index != newIndex);
2477 *write = std::move(tracker.row);
2478 newRows[tracker.index] = newIndex;
2479 }
2480
2481 // free memory from intermediate vector
2482 tracked.clear();
2483 tracked.shrink_to_fit();
2484
2485 // update relevant persistent model indexes
2486 for (const auto &fromIndex : std::as_const(persistentIndexes)) {
2487 const int newRow = newRows.at(fromIndex.row());
2488 if (fromIndex.row() == newRow)
2489 continue;
2490 const QModelIndex toIndex = that().indexImpl(newRow,
2491 fromIndex.column(),
2492 fromIndex.parent());
2493 this->changePersistentIndex(fromIndex, toIndex);
2494 }
2495 }
2496
2497 QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits,
2498 Qt::MatchFlags flags) const
2499 {
2500 QCollator collator = this->matchCollator();
2501 collator.setCaseSensitivity(flags.testFlag(Qt::MatchCaseSensitive) ? Qt::CaseSensitive
2502 : Qt::CaseInsensitive);
2503 return that().matchImpl(start, role,
2504 QRangeModelImplBase::convertMatchValue(value, flags), hits,
2505 flags, collator);
2506 }
2507
2508 bool matchRow(const_row_reference row, const QModelIndex &index, int role, const QVariant &value,
2509 Qt::MatchFlags flags, const QCollator &collator) const
2510 {
2511 const uint matchType = (flags & Qt::MatchTypeMask).toInt();
2512
2513 return row_traits::for_element_at(row, index.column(), [&](const auto &element) {
2514 using value_type = q20::remove_cvref_t<decltype(element)>;
2515 using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
2516 using multi_role = QRangeModelDetails::is_multi_role<value_type>;
2517
2518 if constexpr (QRangeModelDetails::item_access<wrapped_value_type>::hasReadRole
2519 || multi_role() || has_metaobject<value_type>) {
2520 QModelRoleData roleData(role);
2521 ItemReader reader{index, roleData, this};
2522 reader(element);
2523 return QRangeModelImplBase::matchValue(roleData.data(), value, flags, collator);
2524 } else if constexpr (std::is_same_v<wrapped_value_type, QVariant>) {
2525 return QRangeModelImplBase::matchValue(element, value, flags, collator);
2526 } else {
2527 constexpr QMetaType mt = QMetaType::fromType<wrapped_value_type>();
2528 if (mt == value.metaType()) {
2529 if (matchType == Qt::MatchExactly)
2530 return mt.equals(QRangeModelDetails::pointerTo(element), value.constData());
2531 else if constexpr (std::is_same_v<wrapped_value_type, QString>)
2532 return QRangeModelImplBase::matchValue(element, value, flags, collator);
2533 } else {
2534 return QRangeModelImplBase::matchValue(QVariant::fromValue(QRangeModelDetails::refTo(element)),
2535 value, flags, collator);
2536 }
2537 }
2538 return false;
2539 });
2540 }
2541
2542 template <typename InsertFn>
2543 bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
2544 {
2545 if (count == 0)
2546 return false;
2547 range_type * const children = childRange(parent);
2548 if (!children)
2549 return false;
2550
2551 this->beginInsertColumns(parent, column, column + count - 1);
2552
2553 for (auto &child : *children) {
2554 auto it = QRangeModelDetails::pos(child, column);
2555 (void)insertFn(QRangeModelDetails::refTo(child), it, count);
2556 }
2557
2558 this->endInsertColumns();
2559
2560 // endInsertColumns emits columnsInserted, at which point clients might
2561 // have populated the new columns with objects (if the columns aren't objects
2562 // themselves).
2563 if constexpr (itemsAreQObjects) {
2564 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2565 for (int r = 0; r < that().rowCount(parent); ++r) {
2566 for (int c = column; c < column + count; ++c) {
2567 const QModelIndex index = that().index(r, c, parent);
2568 writeAt(index, [this, &index](QObject *item){
2569 return Self::connectProperties(index, item,
2570 m_data.context, m_data.properties);
2571 });
2572 }
2573 }
2574 }
2575 }
2576
2577 return true;
2578 }
2579
2580 bool insertColumns(int column, int count, const QModelIndex &parent)
2581 {
2582 if constexpr (dynamicColumns() && isMutable() && row_features::has_insert) {
2583 return doInsertColumns(column, count, parent, [](auto &row, auto it, int n){
2584 row.insert(it, n, {});
2585 return true;
2586 });
2587 } else {
2588 return false;
2589 }
2590 }
2591
2592 bool removeColumns(int column, int count, const QModelIndex &parent)
2593 {
2594 if constexpr (dynamicColumns() && isMutable() && row_features::has_erase) {
2595 if (column < 0 || column + count > columnCount(parent))
2596 return false;
2597
2598 range_type * const children = childRange(parent);
2599 if (!children)
2600 return false;
2601
2602 if constexpr (itemsAreQObjects) {
2603 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2604 for (int r = 0; r < that().rowCount(parent); ++r) {
2605 for (int c = column; c < column + count; ++c) {
2606 const QModelIndex index = that().index(r, c, parent);
2607 writeAt(index, [this](QObject *item){
2608 m_data.connections.removeIf([item](const auto &connection) {
2609 return connection.sender == item;
2610 });
2611 return true;
2612 });
2613 }
2614 }
2615 }
2616 }
2617
2618 this->beginRemoveColumns(parent, column, column + count - 1);
2619 for (auto &child : *children) {
2620 const auto start = QRangeModelDetails::pos(child, column);
2621 QRangeModelDetails::refTo(child).erase(start, std::next(start, count));
2622 }
2623 this->endRemoveColumns();
2624 return true;
2625 }
2626 return false;
2627 }
2628
2629 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
2630 const QModelIndex &destParent, int destColumn)
2631 {
2632 // we only support moving columns within the same parent
2633 if (sourceParent != destParent)
2634 return false;
2635 if constexpr (isMutable() && (row_features::has_rotate || row_features::has_splice)) {
2636 if (!Structure::canMoveColumns(sourceParent, destParent))
2637 return false;
2638
2639 if constexpr (dynamicColumns()) {
2640 // we only support ranges as columns, as other types might
2641 // not have the same data type across all columns
2642 range_type * const children = childRange(sourceParent);
2643 if (!children)
2644 return false;
2645
2646 if (!this->beginMoveColumns(sourceParent, sourceColumn, sourceColumn + count - 1,
2647 destParent, destColumn)) {
2648 return false;
2649 }
2650
2651 for (auto &child : *children)
2652 QRangeModelDetails::rotate(child, sourceColumn, count, destColumn);
2653
2654 this->endMoveColumns();
2655 return true;
2656 }
2657 }
2658 return false;
2659 }
2660
2661 template <typename InsertFn>
2662 bool doInsertRows(int row, int count, const QModelIndex &parent, InsertFn &&insertFn)
2663 {
2664 range_type *children = childRange(parent);
2665 if (!children)
2666 return false;
2667
2668 this->beginInsertRows(parent, row, row + count - 1);
2669
2670 row_ptr parentRow = parent.isValid()
2671 ? QRangeModelDetails::pointerTo(this->rowData(parent))
2672 : nullptr;
2673 (void)std::forward<InsertFn>(insertFn)(*children, parentRow, row, count);
2674
2675 // fix the parent in all children of the modified row, as the
2676 // references back to the parent might have become invalid.
2677 that().resetParentInChildren(children);
2678
2679 this->endInsertRows();
2680
2681 // endInsertRows emits rowsInserted, at which point clients might
2682 // have populated the new row with objects (if the rows aren't objects
2683 // themselves).
2684 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2685 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
2686 const auto begin = QRangeModelDetails::pos(children, row);
2687 const auto end = std::next(begin, count);
2688 int rowIndex = row;
2689 for (auto it = begin; it != end; ++it, ++rowIndex)
2690 autoConnectPropertiesInRow(*it, rowIndex, parent);
2691 }
2692 }
2693
2694 return true;
2695 }
2696
2697 bool insertRows(int row, int count, const QModelIndex &parent)
2698 {
2699 if constexpr (canInsertRows()) {
2700 return doInsertRows(row, count, parent,
2701 [this](range_type &children, row_ptr parentRow, int r, int n){
2702 EmptyRowGenerator generator{0, &that(), parentRow};
2703
2704 const auto pos = QRangeModelDetails::pos(children, r);
2705 if constexpr (range_features::has_insert_range) {
2706 children.insert(pos, std::move(generator), EmptyRowGenerator{n});
2707 } else if constexpr (rows_are_owning_or_raw_pointers) {
2708 auto start = children.insert(pos, n, nullptr); // MSVC doesn't like row_type{}
2709 std::copy(std::move(generator), EmptyRowGenerator{n}, start);
2710 } else {
2711 children.insert(pos, n, std::move(*generator));
2712 }
2713 return true;
2714 });
2715 } else {
2716 return false;
2717 }
2718 }
2719
2720 bool removeRows(int row, int count, const QModelIndex &parent = {})
2721 {
2722 if constexpr (canRemoveRows()) {
2723 const int prevRowCount = rowCount(parent);
2724 if (row < 0 || row + count > prevRowCount)
2725 return false;
2726
2727 range_type *children = childRange(parent);
2728 if (!children)
2729 return false;
2730
2731 if constexpr (itemsAreQObjects || rowsAreQObjects) {
2732 if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
2733 const auto begin = QRangeModelDetails::pos(children, row);
2734 const auto end = std::next(begin, count);
2735 int rowIndex = row;
2736 for (auto it = begin; it != end; ++it, ++rowIndex)
2737 clearConnectionInRow(*it, rowIndex, parent);
2738 }
2739 }
2740
2741 this->beginRemoveRows(parent, row, row + count - 1);
2742 [[maybe_unused]] bool callEndRemoveColumns = false;
2743 if constexpr (dynamicColumns()) {
2744 // if we remove the last row in a dynamic model, then we no longer
2745 // know how many columns we should have, so they will be reported as 0.
2746 if (prevRowCount == count) {
2747 if (const int columns = columnCount(parent)) {
2748 callEndRemoveColumns = true;
2749 this->beginRemoveColumns(parent, 0, columns - 1);
2750 }
2751 }
2752 }
2753 { // erase invalidates iterators
2754 const auto begin = QRangeModelDetails::pos(children, row);
2755 const auto end = std::next(begin, count);
2756 that().deleteRemovedRows(begin, end);
2757 children->erase(begin, end);
2758 }
2759 // fix the parent in all children of the modified row, as the
2760 // references back to the parent might have become invalid.
2761 that().resetParentInChildren(children);
2762
2763 if constexpr (dynamicColumns()) {
2764 if (callEndRemoveColumns) {
2765 Q_ASSERT(columnCount(parent) == 0);
2766 this->endRemoveColumns();
2767 }
2768 }
2769 this->endRemoveRows();
2770 return true;
2771 } else {
2772 return false;
2773 }
2774 }
2775
2776 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
2777 const QModelIndex &destParent, int destRow)
2778 {
2779 if constexpr (isMutable() && (range_features::has_rotate || range_features::has_splice)) {
2780 if (!Structure::canMoveRows(sourceParent, destParent))
2781 return false;
2782
2783 if (sourceParent != destParent) {
2784 return that().moveRowsAcross(sourceParent, sourceRow, count,
2785 destParent, destRow);
2786 }
2787
2788 if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
2789 || sourceRow < 0 || sourceRow + count - 1 >= this->rowCount(sourceParent)
2790 || destRow < 0 || destRow > this->rowCount(destParent)) {
2791 return false;
2792 }
2793
2794 range_type *source = childRange(sourceParent);
2795 // moving within the same range
2796 if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
2797 return false;
2798
2799 QRangeModelDetails::rotate(source, sourceRow, count, destRow);
2800
2801 that().resetParentInChildren(source);
2802
2803 this->endMoveRows();
2804 return true;
2805 } else {
2806 return false;
2807 }
2808 }
2809
2810 const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2811 protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
2812
2813 QModelIndex parent(const QModelIndex &child) const { return that().parentImpl(child); }
2814
2815 int rowCount(const QModelIndex &parent) const { return that().rowCountImpl(parent); }
2816
2817 static constexpr int fixedColumnCount()
2818 {
2819 if constexpr (one_dimensional_range)
2820 return row_traits::fixed_size();
2821 else
2822 return static_column_count;
2823 }
2824 int columnCount(const QModelIndex &parent) const { return that().columnCountImpl(parent); }
2825
2826 void destroy() { delete std::addressof(that()); }
2827
2828 Qt::DropActions adjustSupportedDragActions(Qt::DropActions dragActions) {
2829 if constexpr (!isMutable())
2830 dragActions &= ~Qt::MoveAction;
2831 return dragActions;
2832 }
2833 Qt::DropActions adjustSupportedDropActions(Qt::DropActions dropActions)
2834 {
2835 if constexpr (!isMutable())
2836 dropActions = Qt::IgnoreAction;
2837
2838 return dropActions;
2839 }
2840
2842 {
2843 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
2844 if constexpr (QRangeModelDetails::item_access<ItemType>::hasMimeTypes)
2845 return QRangeModelDetails::QRangeModelItemAccess<ItemType>::mimeTypes();
2846 else if constexpr (QRangeModelDetails::hasMimeTypes<wrapped_row_type>)
2847 return QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>::mimeTypes();
2848 else
2849 return this->itemModel().QAbstractItemModel::mimeTypes();
2850 }
2851
2852 bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
2853 const QModelIndex &target) const
2854 {
2855 if constexpr (isMutable()) {
2856 bool canDrop;
2857 using RowOptions = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>;
2858 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
2859 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<ItemType>;
2860 if constexpr (QRangeModelDetails::item_access<ItemType>::hasCanDropMimeDataFull) {
2861 canDrop = ItemAccess::canDropMimeData(data, action, row, column, target);
2862 } else if constexpr (QRangeModelDetails::hasCanDropMimeDataFull<wrapped_row_type>) {
2863 canDrop = RowOptions::canDropMimeData(data, action, row, column, target);
2864 } else {
2865 canDrop = this->itemModel().QAbstractItemModel::canDropMimeData(data, action, row,
2866 column, target);
2867 if constexpr (QRangeModelDetails::item_access<ItemType>::hasCanDropMimeData)
2868 canDrop &= ItemAccess::canDropMimeData(data);
2869 else if constexpr (QRangeModelDetails::hasCanDropMimeData<wrapped_row_type>)
2870 canDrop &= RowOptions::canDropMimeData(data);
2871 }
2872 return canDrop;
2873 } else {
2874 return false;
2875 }
2876 }
2877
2878 // orientation == vertical: we drop rows; otherwise we drop individual items
2879 template <Qt::Orientation orient, typename Entry>
2880 bool doDropMimeData(std::vector<QRangeModelDetails::DroppedEntry<Entry>> &droppedEntries,
2881 DropOperation dropOperation, int row, int column, const QModelIndex &target)
2882 {
2883 using DroppedEntry = QRangeModelDetails::DroppedEntry<Entry>;
2884 using Cell = typename DroppedEntry::Cell;
2885 if (dropOperation == DropOperation::DontDrop)
2886 return false;
2887
2888 const bool dropOnTarget = row == -1 && column == -1 && target.isValid();
2889 const QModelIndex parent = dropOperation == DropOperation::InsertAsChildren
2890 ? target.siblingAtColumn(0) : target.parent();
2891
2892 Cell lastCell;
2893 if constexpr (orient == Qt::Horizontal)
2894 lastCell = {0, -1};
2895 else
2896 lastCell = {-1, 0};
2897 int bottomRow = -1;
2898 int rightColumn = -1;
2899 // set the target cell for all dropped entries and find the bottom-right
2900 // cell relative to the drop position
2901 int maxColumn = that().columnCount(parent) - 1;
2902 for (auto &droppedEntry : droppedEntries) {
2903 if (droppedEntry.m_cell == Cell{-1, -1}) {
2904 if constexpr (orient == Qt::Horizontal) {
2905 // auto-inserted items fill all columns before moving to
2906 // the next row
2907 droppedEntry.m_cell = {lastCell.m_row, lastCell.m_column + 1};
2908 if (droppedEntry.m_cell.m_column > maxColumn) {
2909 droppedEntry.m_cell.m_column = 0;
2910 ++droppedEntry.m_cell.m_row;
2911 }
2912 } else {
2913 droppedEntry.m_cell = {lastCell.m_row + 1, lastCell.m_column};
2914 }
2915 }
2916 lastCell = droppedEntry.m_cell;
2917 bottomRow = std::max(lastCell.m_row, bottomRow);
2918 rightColumn = std::max(lastCell.m_column, rightColumn);
2919 }
2920
2921 if (dropOperation == DropOperation::InsertAsChildren) {
2922 row = rowCount(parent);
2923 column = 0;
2924 } else if (dropOnTarget) {
2925 row = target.row();
2926 column = target.column();
2927 } else {
2928 if (row < 0)
2929 row = rowCount(parent);
2930 // dropping into empty space to the right of a table doesn't widen
2931 if (column < 0)
2932 column = 0;
2933 }
2934 const bool overwrite = dropOperation == DropOperation::OverwriteAndIgnore
2935 || dropOperation == DropOperation::OverwriteAndExtend;
2936
2937 // Compute if we need more rows, and try to add them. Abort if that fails.
2938 const int overwriteRows = overwrite
2939 ? std::min(bottomRow + 1, rowCount(parent) - row)
2940 : 0;
2941 const int newRows = dropOperation == DropOperation::OverwriteAndExtend
2942 ? bottomRow - overwriteRows + 1
2943 : (dropOperation == DropOperation::InsertAsChildren
2944 || dropOperation == DropOperation::InsertAsSiblings)
2945 ? bottomRow + 1
2946 : 0;
2947 if (newRows > 0 && !insertRows(row, newRows, parent))
2948 return false;
2949
2950 // Ditto for columns, but InsertAsSiblings/Children only applies to rows
2951 const int overwriteColumns = overwrite
2952 ? std::min(rightColumn + 1, columnCount(parent) - column)
2953 : 0;
2954 const int newColumns = dropOperation == DropOperation::OverwriteAndExtend
2955 ? rightColumn - overwriteColumns + 1 : 0;
2956 if (newColumns > 0 && !insertColumns(column, newColumns, parent))
2957 return false;
2958
2959 // access the target range
2960 range_type *parentRange = that().childRange(parent);
2961 if (!parentRange)
2962 return false;
2963 range_type &targetRange = *parentRange;
2964
2965 int maxRow = that().rowCount(parent) - 1;
2966 maxColumn = that().columnCount(parent) - 1;
2967 auto begin = std::move_iterator(droppedEntries.begin());
2968 auto end = std::move_iterator(droppedEntries.end());
2969 for (; begin != end; ++begin) {
2970 DroppedEntry droppedEntry = *begin;
2971 const Cell cell = {droppedEntry.m_cell.m_row + row, droppedEntry.m_cell.m_column + column};
2972 if (cell.m_row > maxRow || cell.m_column > maxColumn) // Ignore
2973 continue;
2974 auto writeRow = QRangeModelDetails::pos(targetRange, cell.m_row);
2975 if constexpr (orient == Qt::Vertical) { // complete rows
2976 if constexpr (QRangeModelDetails::is_owning_or_raw_pointer<row_type>()) {
2977 if (!*writeRow)
2978 *writeRow = this->protocol().newRow();
2979 **writeRow = std::move(droppedEntry);
2980 } else {
2981 *writeRow = std::move(droppedEntry);
2982 }
2983 } else {
2984 row_traits::for_element_at(*writeRow, cell.m_column, [&](auto &item){
2985 using item_type = q20::remove_cvref_t<decltype(item)>;
2986 using wrapped_item_type = QRangeModelDetails::wrapped_t<item_type>;
2987 if constexpr (QRangeModelDetails::is_any_owning_ptr<item_type>()) {
2988 if (!QRangeModelDetails::isValid(item))
2989 item.reset(new wrapped_item_type{std::move(droppedEntry)});
2990 else
2991 *item = std::move(droppedEntry);
2992 } else if (!QRangeModelDetails::isValid(item)) {
2993 return false;
2994 } else {
2995 item = std::move(droppedEntry);
2996 }
2997 return true;
2998 });
2999 }
3000 }
3001
3002 that().resetParentInChildren(&targetRange);
3003
3004 const QModelIndex topLeft = index(row, column, parent);
3005 const QModelIndex bottomRight = orient == Qt::Horizontal
3006 ? sibling(row + bottomRow, column + rightColumn, topLeft)
3007 : sibling(row + bottomRow, maxColumn, topLeft);
3008 this->dataChanged(topLeft, bottomRight, {});
3009
3010 return true;
3011 }
3012
3013 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
3014 const QModelIndex &target)
3015 {
3016 if constexpr (isMutable()) {
3017 if (!canDropMimeData(data, action, row, column, target))
3018 return false;
3019
3020 const bool dropOnTarget = row == -1 && column == -1 && target.isValid();
3021
3022 auto automaticDropOption = [=](auto dropResult){
3023 DropOperation dropOperation;
3024 if constexpr (std::is_same_v<bool, decltype(dropResult)>) {
3025 dropOperation = dropResult ? DropOperation::Automatic
3026 : DropOperation::DontDrop;
3027 } else { // it's a QRangeModel::DropOperation
3028 dropOperation = static_cast<DropOperation>(dropResult);
3029 }
3030
3031 if (dropOperation == DropOperation::Automatic) {
3032 if constexpr (!canInsertRows())
3033 dropOperation = DropOperation::OverwriteAndIgnore;
3034 else if (!dropOnTarget)
3035 dropOperation = DropOperation::InsertAsSiblings;
3036 else if (target.siblingAtColumn(0).flags().testFlag(Qt::ItemNeverHasChildren))
3037 dropOperation = DropOperation::OverwriteAndExtend;
3038 else
3039 dropOperation = DropOperation::InsertAsChildren;
3040 }
3041 return dropOperation;
3042 };
3043
3044 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
3045 if constexpr (QRangeModelDetails::item_access<ItemType>::hasDropMimeDataFull
3046 || QRangeModelDetails::item_access<ItemType>::hasDropMimeData) {
3047 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<ItemType>;
3048 using DroppedItem = QRangeModelDetails::DroppedEntry<ItemType>;
3049 std::vector<DroppedItem> droppedItems;
3050 DropOperation dropOperation = automaticDropOption([&]{
3051 auto inserter = std::back_inserter(droppedItems);
3052 if constexpr (QRangeModelDetails::item_access<ItemType>::hasDropMimeDataFull)
3053 return ItemAccess::dropMimeData(data, action, row, column, target, inserter);
3054 else
3055 return ItemAccess::dropMimeData(data, inserter);
3056 }());
3057 if (doDropMimeData<Qt::Horizontal>(droppedItems, dropOperation, row, column, target))
3058 return true;
3059 // fall through to try the default mime type
3060 } else if constexpr (QRangeModelDetails::hasDropMimeDataFull<wrapped_row_type>
3061 || QRangeModelDetails::hasDropMimeData<wrapped_row_type>) {
3062 using RowOptions = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>;
3063 using DroppedRow = QRangeModelDetails::DroppedEntry<wrapped_row_type>;
3064 std::vector<DroppedRow> droppedRows;
3065 DropOperation dropOperation = automaticDropOption([&]{
3066 auto inserter = std::back_inserter(droppedRows);
3067 if constexpr (QRangeModelDetails::hasDropMimeDataFull<wrapped_row_type>)
3068 return RowOptions::dropMimeData(data, action, row, column, target, inserter);
3069 else
3070 return RowOptions::dropMimeData(data, inserter);
3071 }());
3072 if (doDropMimeData<Qt::Vertical>(droppedRows, dropOperation, row, column, target))
3073 return true;
3074 }
3075 // default mime type handling: dropping on item -> try to set the data
3076 if (dropOnTarget && that().dropOnItem(data, target))
3077 return true;
3078 }
3079 return false;
3080 }
3081
3082 // A bidirectional-iterator that, given a list of QModelIndex, dereferences
3083 // to a list of rows or items, plus QModelIndex, without copying any data.
3084 // For segments in indexes covering full rows, we skip over the individual
3085 // indexes and give the dereferenced index a column value of -1.
3087 {
3090 using iterator_category = std::bidirectional_iterator_tag;
3092 using reference [[maybe_unused]] = value_type;
3094 using pointer [[maybe_unused]] = void;
3095
3097 MimeDataRowIterator(base_iterator it, base_iterator begin, base_iterator end,
3098 const QRangeModelImpl *model)
3099 : m_it(it), m_begin(begin), m_end(end), m_model(model)
3100 , m_columnCount(model->columnCount({}))
3101 {
3102 updateCurrentIndexFullRow();
3103 }
3104
3106 {
3107 const QModelIndex &index = *m_it;
3108 return {m_model->rowData(index),
3109 m_currentIndexIsFullRow
3110 ? m_model->createIndex(m_it->row(), -1, m_it->internalPointer()) : index};
3111 }
3112
3114 if (m_currentIndexIsFullRow)
3115 m_it += m_columnCount;
3116 else
3117 ++m_it;
3118 updateCurrentIndexFullRow();
3119 return *this;
3120 }
3121 MimeDataRowIterator operator++(int) { auto tmp = *this; ++(*this); return tmp; }
3122
3124 --m_it;
3125 m_currentIndexIsFullRow = false;
3126 const int lastColumn = m_columnCount - 1;
3127 if (m_it - m_begin >= lastColumn && m_it->column() == lastColumn) {
3128 const QModelIndex &firstInRow = m_it[-lastColumn];
3129 if (m_it->row() == firstInRow.row()
3130 && firstInRow.internalPointer() == m_it->internalPointer()) {
3131 m_currentIndexIsFullRow = true;
3132 m_it -= lastColumn;
3133 }
3134 }
3135 return *this;
3136 }
3137 MimeDataRowIterator operator--(int) { auto tmp = *this; --(*this); return tmp; }
3138
3139 MimeDataRowIterator operator-(difference_type n) const
3140 {
3141 auto tmp = *this; tmp.m_it -= n; return tmp;
3142 }
3143
3144 bool operator==(const MimeDataRowIterator &other) const { return m_it == other.m_it; }
3145 bool operator!=(const MimeDataRowIterator &other) const { return m_it != other.m_it; }
3146
3147 private:
3148 void updateCurrentIndexFullRow()
3149 {
3150 m_currentIndexIsFullRow = false;
3151 if (m_it == m_end || m_it->column() || m_end - m_it < m_columnCount)
3152 return;
3153 const QModelIndex &lastInRow = m_it[m_columnCount - 1];
3154 m_currentIndexIsFullRow = lastInRow.row() == m_it->row()
3155 && lastInRow.internalPointer() == m_it->internalPointer();
3156 }
3157
3158 base_iterator m_it;
3159 base_iterator m_begin;
3160 base_iterator m_end;
3161 const QRangeModelImpl *m_model;
3162 int m_columnCount = 0;
3163 bool m_currentIndexIsFullRow = false;
3164 };
3165
3167 {
3169 // row_traits::item_type is wrapped, and void if not the same for all columns
3171 void *, typename row_traits::item_type>;
3173 using iterator_category = std::bidirectional_iterator_tag;
3175 using reference [[maybe_unused]] = value_type;
3177 using pointer [[maybe_unused]] = void;
3178
3180 {
3181 const QModelIndex &index = *m_it;
3182 // pointer to the item as stored, including wrapping
3183 const item_type *pitem = nullptr;
3184 const auto &row = m_model->rowData(index);
3185 if (QRangeModelDetails::isValid(row)) {
3186 row_traits::for_element_at(QRangeModelDetails::refTo(row), index.column(),
3187 [&pitem](const auto &item){
3188 pitem = &item;
3189 return true;
3190 });
3191 }
3192 if constexpr (std::disjunction_v<QRangeModelDetails::is_owning_or_raw_pointer<row_type>,
3193 QRangeModelDetails::is_owning_or_raw_pointer<item_type>>) {
3194 if (Q_UNLIKELY(!QRangeModelDetails::isValid(pitem))) {
3195 // no use in warning about invalid item here, as the user
3196 // can't check for validity without dereferencing the iterator.
3197 constexpr bool is_constexpr_default_constructible_v =
3198 value_type::template is_constexpr_default_constructible<item_type>::value;
3199 if constexpr (is_constexpr_default_constructible_v) {
3200 Q_CONSTINIT static const item_type emptyDefault;
3201 return {emptyDefault, index};
3202 } else {
3203 // known to cause runtime initialization
3204 static const item_type emptyDefault;
3205 return {emptyDefault, index};
3206 }
3207 }
3208 }
3209 // this will decompose to a [wrapped_item_type, QModelIndex]
3210 return {*pitem, index};
3211 }
3212 MimeDataItemIterator &operator++() { ++m_it; return *this; }
3213 MimeDataItemIterator operator++(int) { auto tmp = *this; ++(*this); return tmp; }
3214
3215 MimeDataItemIterator &operator--() { --m_it; return *this; }
3216 MimeDataItemIterator operator--(int) { auto tmp = *this; --(*this); return tmp; }
3217
3218 MimeDataItemIterator operator-(difference_type n) const
3219 {
3220 auto tmp = *this; tmp.m_it -= n; return tmp;
3221 }
3222
3223 bool operator==(const MimeDataItemIterator &other) const { return m_it == other.m_it; }
3224 bool operator!=(const MimeDataItemIterator &other) const { return m_it != other.m_it; }
3225
3228 };
3229
3230 template <typename Iterator>
3232 Iterator begin() const { return m_begin; }
3233 Iterator end() const { return m_end; }
3234 auto rbegin() const { return std::reverse_iterator(m_end); }
3235 auto rend() const { return std::reverse_iterator(m_begin); }
3236 auto first() const { return *m_begin;}
3237 auto last() const { return *(m_end - 1);}
3238 bool isEmpty() const { return m_begin == m_end; }
3239 bool empty() const { return m_begin == m_end; }
3240 Iterator m_begin;
3241 Iterator m_end;
3242 };
3243
3244 QMimeData *mimeData(const QModelIndexList &indexes) const
3245 {
3246 QMimeData *result = nullptr;
3247 using RowOptions = QRangeModelDetails::QRangeModelRowOptions<wrapped_row_type>;
3248 using ItemType = QRangeModelDetails::wrapped_t<typename row_traits::item_type>;
3249
3250 if constexpr (QRangeModelDetails::item_access<ItemType>::hasMimeData) {
3251 using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<ItemType>;
3252 const auto begin = MimeDataItemIterator{indexes.begin(), this};
3253 const auto end = MimeDataItemIterator{indexes.end(), this};
3254 result = ItemAccess::mimeData(MimeDataRange<MimeDataItemIterator>{begin, end});
3255 } else if constexpr (QRangeModelDetails::hasMimeDataRowSpan<wrapped_row_type>) {
3256 const auto begin = MimeDataRowIterator(indexes.begin(), indexes.begin(), indexes.end(), this);
3257 const auto end = MimeDataRowIterator(indexes.end(), indexes.begin(), indexes.end(), this);
3258 result = RowOptions::mimeData(MimeDataRange<MimeDataRowIterator>{begin, end});
3259 } else if constexpr (QRangeModelDetails::hasMimeDataIndexList<wrapped_row_type>) {
3260 result = RowOptions::mimeData(indexes);
3261 }
3262
3263 return result;
3264 }
3265
3266 template <typename BaseMethod, typename BaseMethod::template Overridden<Self> overridden>
3267 using Override = typename Ancestor::template Override<BaseMethod, overridden>;
3268
3277
3292
3296
3304
3309
3310protected:
3312 {
3314 }
3315
3317 {
3318 // We delete row objects if we are not operating on a reference or pointer
3319 // to a range, as in that case, the owner of the referenced/pointed to
3320 // range also owns the row entries.
3321 // ### Problem: if we get a copy of a range (no matter if shared or not),
3322 // then adding rows will create row objects in the model's copy, and the
3323 // client can never delete those. But copied rows will be the same pointer,
3324 // which we must not delete (as we didn't create them).
3325
3326 static constexpr bool modelCopied = !QRangeModelDetails::is_wrapped<Range>() &&
3327 (std::is_reference_v<Range> || std::is_const_v<std::remove_reference_t<Range>>);
3328
3329 static constexpr bool modelShared = QRangeModelDetails::is_any_shared_ptr<Range>();
3330
3331 static constexpr bool default_row_deleter = protocol_traits::is_default &&
3332 protocol_traits::has_deleteRow;
3333
3334 static constexpr bool ambiguousRowOwnership = (modelCopied || modelShared) &&
3335 rows_are_raw_pointers && default_row_deleter;
3336
3337 static_assert(!ambiguousRowOwnership,
3338 "Using of copied and shared tree and table models with rows as raw pointers, "
3339 "and the default protocol is not allowed due to ambiguity of rows ownership. "
3340 "Move the model in, use another row type, or implement a custom tree protocol.");
3341
3342 if constexpr (protocol_traits::has_deleteRow && !std::is_pointer_v<Range>
3343 && !QRangeModelDetails::is_any_of<Range, std::reference_wrapper>()) {
3344 const auto begin = QRangeModelDetails::adl_begin(*m_data.model());
3345 const auto end = QRangeModelDetails::adl_end(*m_data.model());
3346 that().deleteRemovedRows(begin, end);
3347 }
3348 }
3349
3350 static constexpr bool canInsertRows()
3351 {
3352 if constexpr (dynamicColumns() && !row_features::has_resize) {
3353 // If we operate on dynamic columns and cannot resize a newly
3354 // constructed row, then we cannot insert.
3355 return false;
3356 } else if constexpr (!protocol_traits::has_newRow) {
3357 // We also cannot insert if we cannot create a new row element
3358 return false;
3359 } else if constexpr (!range_features::has_insert_range
3360 && !std::is_copy_constructible_v<row_type>) {
3361 // And if the row is a move-only type, then the range needs to be
3362 // backed by a container that can move-insert default-constructed
3363 // row elements.
3364 return false;
3365 } else {
3366 return Structure::canInsertRowsImpl();
3367 }
3368 }
3369
3370 static constexpr bool canRemoveRows()
3371 {
3372 return Structure::canRemoveRowsImpl();
3373 }
3374
3375 template <typename F>
3376 bool writeAt(const QModelIndex &index, F&& writer)
3377 {
3378 row_reference row = rowData(index);
3379 if (!QRangeModelDetails::isValid(row))
3380 return false;
3381 return row_traits::for_element_at(row, index.column(), [&writer](auto &&target) {
3382 using target_type = decltype(target);
3383 // we can only assign to an lvalue reference
3384 if constexpr (std::is_lvalue_reference_v<target_type>
3385 && !std::is_const_v<std::remove_reference_t<target_type>>) {
3386 return writer(std::forward<target_type>(target));
3387 } else {
3388 return false;
3389 }
3390 });
3391 }
3392
3393 template <typename F>
3394 bool readAt(const QModelIndex &index, F&& reader) const {
3395 const_row_reference row = rowData(index);
3396 if (!QRangeModelDetails::isValid(row))
3397 return false;
3398 return row_traits::for_element_at(row, index.column(), std::forward<F>(reader));
3399 }
3400
3401 template <typename Value>
3402 static QVariant read(const Value &value)
3403 {
3404 if constexpr (std::is_constructible_v<QVariant, Value>)
3405 return QVariant(value);
3406 else
3407 return QVariant::fromValue(value);
3408 }
3409 template <typename Value>
3410 static QVariant read(Value *value)
3411 {
3412 if (value) {
3413 if constexpr (std::is_constructible_v<QVariant, Value *>)
3414 return QVariant(value);
3415 else
3416 return read(*value);
3417 }
3418 return {};
3419 }
3420
3421 template <typename Target>
3422 static bool write(Target &target, const QVariant &value)
3423 {
3424 using Type = std::remove_reference_t<Target>;
3425 if constexpr (std::is_constructible_v<Target, QVariant>) {
3426 target = value;
3427 return true;
3428 } else if (value.canConvert<Type>()) {
3429 target = value.value<Type>();
3430 return true;
3431 }
3432 return false;
3433 }
3434 template <typename Target>
3435 static bool write(Target *target, const QVariant &value)
3436 {
3437 if (target)
3438 return write(*target, value);
3439 return false;
3440 }
3441
3442 template <typename ItemType>
3444 {
3445 struct {
3446 operator QMetaProperty() const {
3447 const QByteArray roleName = that.itemModel().roleNames().value(role);
3448 const QMetaObject &mo = ItemType::staticMetaObject;
3449 if (const int index = mo.indexOfProperty(roleName.data());
3450 index >= 0) {
3451 return mo.property(index);
3452 }
3453 return {};
3454 }
3455 const QRangeModelImpl &that;
3456 const int role;
3457 } findProperty{*this, role};
3458
3459 if constexpr (ModelData::cachesProperties)
3460 return *m_data.properties.tryEmplace(role, findProperty).iterator;
3461 else
3462 return findProperty;
3463 }
3464
3465 void connectPropertyOnRead(const QModelIndex &index, int role,
3466 const QObject *gadget, const QMetaProperty &prop) const
3467 {
3468 if (!index.isValid())
3469 return;
3470 const typename ModelData::Connection connection = {gadget, role};
3471 if (prop.hasNotifySignal() && this->autoConnectPolicy() == AutoConnectPolicy::OnRead
3472 && !m_data.connections.contains(connection)) {
3473 if constexpr (isMutable())
3474 Self::connectProperty(index, gadget, m_data.context, role, prop);
3475 else
3476 Self::connectPropertyConst(index, gadget, m_data.context, role, prop);
3477 m_data.connections.insert(connection);
3478 }
3479 }
3480
3481 template <typename ItemType>
3482 QVariant readRole(const QModelIndex &index, int role, ItemType *gadget) const
3483 {
3484 using item_type = std::remove_pointer_t<ItemType>;
3485 QVariant result;
3486 QMetaProperty prop = roleProperty<item_type>(role);
3487 if (!prop.isValid() && role == Qt::EditRole) {
3488 role = Qt::DisplayRole;
3489 prop = roleProperty<item_type>(Qt::DisplayRole);
3490 }
3491
3492 if (prop.isValid()) {
3493 if constexpr (itemsAreQObjects)
3494 connectPropertyOnRead(index, role, gadget, prop);
3495 result = readProperty(prop, gadget);
3496 }
3497 return result;
3498 }
3499
3500 template <typename ItemType>
3501 QVariant readRole(const QModelIndex &index, int role, const ItemType &gadget) const
3502 {
3503 return readRole(index, role, &gadget);
3504 }
3505
3506 template <typename ItemType>
3507 static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
3508 {
3509 if constexpr (std::is_base_of_v<QObject, ItemType>)
3510 return prop.read(gadget);
3511 else
3512 return prop.readOnGadget(gadget);
3513 }
3514
3515 template <typename ItemType>
3516 QVariant readProperty(const QModelIndex &index, ItemType *gadget) const
3517 {
3518 using item_type = std::remove_pointer_t<ItemType>;
3519 const QMetaObject &mo = item_type::staticMetaObject;
3520 const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
3521
3522 if constexpr (rowsAreQObjects)
3523 connectPropertyOnRead(index, Qt::DisplayRole, gadget, prop);
3524
3525 return readProperty(prop, gadget);
3526 }
3527
3528 template <typename ItemType>
3529 QVariant readProperty(const QModelIndex &index, const ItemType &gadget) const
3530 {
3531 return readProperty(index, &gadget);
3532 }
3533
3534 template <typename ItemType>
3535 bool writeRole(int role, ItemType *gadget, const QVariant &data)
3536 {
3537 using item_type = std::remove_pointer_t<ItemType>;
3538 auto prop = roleProperty<item_type>(role);
3539 if (!prop.isValid() && role == Qt::EditRole)
3540 prop = roleProperty<item_type>(Qt::DisplayRole);
3541
3542 return prop.isValid() ? writeProperty(prop, gadget, data) : false;
3543 }
3544
3545 template <typename ItemType>
3546 bool writeRole(int role, ItemType &&gadget, const QVariant &data)
3547 {
3548 return writeRole(role, &gadget, data);
3549 }
3550
3551 template <typename ItemType>
3552 static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
3553 {
3554 if constexpr (std::is_base_of_v<QObject, ItemType>)
3555 return prop.write(gadget, data);
3556 else
3557 return prop.writeOnGadget(gadget, data);
3558 }
3559 template <typename ItemType>
3560 static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
3561 {
3562 using item_type = std::remove_pointer_t<ItemType>;
3563 const QMetaObject &mo = item_type::staticMetaObject;
3564 return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
3565 }
3566
3567 template <typename ItemType>
3568 static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
3569 {
3570 return writeProperty(property, &gadget, data);
3571 }
3572
3573 template <typename ItemType>
3574 static bool resetProperty(int property, ItemType *object)
3575 {
3576 using item_type = std::remove_pointer_t<ItemType>;
3577 const QMetaObject &mo = item_type::staticMetaObject;
3578 bool success = true;
3579 if (property == -1) {
3580 // reset all properties
3581 if constexpr (std::is_base_of_v<QObject, item_type>) {
3582 for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
3583 success = writeProperty(mo.property(p), object, {}) && success;
3584 } else { // reset a gadget by assigning a default-constructed
3585 *object = {};
3586 }
3587 } else {
3588 success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
3589 }
3590 return success;
3591 }
3592
3593 template <typename ItemType>
3594 static bool resetProperty(int property, ItemType &&object)
3595 {
3596 return resetProperty(property, &object);
3597 }
3598
3599 // helpers
3600 const_row_reference rowData(const QModelIndex &index) const
3601 {
3602 Q_ASSERT(index.isValid());
3603 return that().rowDataImpl(index);
3604 }
3605
3606 row_reference rowData(const QModelIndex &index)
3607 {
3608 Q_ASSERT(index.isValid());
3609 return that().rowDataImpl(index);
3610 }
3611
3612 const range_type *childRange(const QModelIndex &index) const
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 range_type *childRange(const QModelIndex &index)
3622 {
3623 if (!index.isValid())
3624 return m_data.model();
3625 if (index.column()) // only items at column 0 can have children
3626 return nullptr;
3627 return that().childRangeImpl(index);
3628 }
3629
3630 template <typename, typename, typename> friend class QRangeModelAdapter;
3631
3633};
3634
3635// Implementations that depends on the model structure (flat vs tree) that will
3636// be specialized based on a protocol type. The main template implements tree
3637// support through a protocol type.
3638template <typename Range, typename Protocol>
3640 : public QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>
3641{
3642 using Base = QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
3643 friend class QRangeModelImpl<QGenericTreeItemModelImpl<Range, Protocol>, Range, Protocol>;
3644
3645 using range_type = typename Base::range_type;
3646 using range_features = typename Base::range_features;
3647 using row_type = typename Base::row_type;
3648 using row_ptr = typename Base::row_ptr;
3649 using const_row_ptr = typename Base::const_row_ptr;
3650
3651 using tree_traits = typename Base::protocol_traits;
3652 static constexpr bool is_mutable_impl = tree_traits::has_mutable_childRows;
3653
3654 static constexpr bool rows_are_any_refs_or_pointers = Base::rows_are_raw_pointers ||
3655 QRangeModelDetails::is_smart_ptr<row_type>() ||
3656 QRangeModelDetails::is_any_of<row_type, std::reference_wrapper>();
3657 static_assert(!Base::dynamicColumns(), "A tree must have a static number of columns!");
3658
3659public:
3660 QGenericTreeItemModelImpl(Range &&model, Protocol &&p, QRangeModel *itemModel)
3661 : Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
3662 {};
3663
3664 void setParentRow(range_type &children, row_ptr parent)
3665 {
3666 for (auto &&child : children)
3667 this->protocol().setParentRow(QRangeModelDetails::refTo(child), parent);
3668 resetParentInChildren(&children);
3669 }
3670
3671 void deleteRemovedRows(range_type &range)
3672 {
3673 deleteRemovedRows(QRangeModelDetails::adl_begin(range), QRangeModelDetails::adl_end(range));
3674 }
3675
3676 bool autoConnectProperties(const QModelIndex &parent) const
3677 {
3678 auto *children = this->childRange(parent);
3679 if (!children)
3680 return true;
3681 return autoConnectPropertiesRange(QRangeModelDetails::refTo(children), parent);
3682 }
3683
3684protected:
3685 QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
3686 {
3687 if (!parent.isValid())
3688 return this->createIndex(row, column);
3689 // only items at column 0 can have children
3690 if (parent.column())
3691 return QModelIndex();
3692
3693 const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
3694 const auto &parentSiblings = childrenOf(grandParent);
3695 const auto it = QRangeModelDetails::pos(parentSiblings, parent.row());
3696 return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
3697 }
3698
3699 QModelIndex parentImpl(const QModelIndex &child) const
3700 {
3701 if (!child.isValid())
3702 return {};
3703
3704 // no pointer to parent row - no parent
3705 const_row_ptr parentRow = static_cast<const_row_ptr>(child.constInternalPointer());
3706 if (!parentRow)
3707 return {};
3708
3709 // get the siblings of the parent via the grand parent
3710 auto &&grandParent = this->protocol().parentRow(QRangeModelDetails::refTo(parentRow));
3711 const range_type &parentSiblings = childrenOf(QRangeModelDetails::pointerTo(grandParent));
3712 // find the index of parentRow
3713 const auto begin = QRangeModelDetails::adl_begin(parentSiblings);
3714 const auto end = QRangeModelDetails::adl_end(parentSiblings);
3715 const auto it = std::find_if(begin, end, [parentRow](auto &&s){
3716 return QRangeModelDetails::pointerTo(std::forward<decltype(s)>(s)) == parentRow;
3717 });
3718 if (it != end)
3719 return this->createIndex(std::distance(begin, it), 0,
3720 QRangeModelDetails::pointerTo(grandParent));
3721 return {};
3722 }
3723
3724 int rowCountImpl(const QModelIndex &parent) const
3725 {
3726 return Base::size(this->childRange(parent));
3727 }
3728
3729 int columnCountImpl(const QModelIndex &) const
3730 {
3731 // All levels of a tree have to have the same, fixed, column count.
3732 // If static_column_count is -1 for a tree, static assert fires
3733 return Base::fixedColumnCount();
3734 }
3735
3736 static constexpr Qt::ItemFlags defaultFlags()
3737 {
3738 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
3739 }
3740
3741 static constexpr bool canInsertRowsImpl()
3742 {
3743 // We must not insert 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_insert;
3748 }
3749
3750 static constexpr bool canRemoveRowsImpl()
3751 {
3752 // We must not remove rows if we cannot adjust the parents of the
3753 // children of the following rows. We don't have to do that if the
3754 // range operates on pointers.
3755 return (rows_are_any_refs_or_pointers || tree_traits::has_setParentRow)
3756 && Base::dynamicRows() && range_features::has_erase;
3757 }
3758
3759 static constexpr bool canMoveColumns(const QModelIndex &, const QModelIndex &)
3760 {
3761 return true;
3762 }
3763
3764 static constexpr bool canMoveRows(const QModelIndex &, const QModelIndex &)
3765 {
3766 return true;
3767 }
3768
3769 bool moveRowsAcross(const QModelIndex &sourceParent, int sourceRow, int count,
3770 const QModelIndex &destParent, int destRow)
3771 {
3772 // If rows are pointers, then reference to the parent row don't
3773 // change, so we can move them around freely. Otherwise we need to
3774 // be able to explicitly update the parent pointer.
3775 if constexpr (!rows_are_any_refs_or_pointers && !tree_traits::has_setParentRow) {
3776 return false;
3777 } else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
3778 return false;
3779 } else if (!this->beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
3780 destParent, destRow)) {
3781 return false;
3782 }
3783
3784 range_type *source = this->childRange(sourceParent);
3785 range_type *destination = this->childRange(destParent);
3786
3787 // If we can insert data from another range into, then
3788 // use that to move the old data over.
3789 const auto destStart = QRangeModelDetails::pos(destination, destRow);
3790 if constexpr (range_features::has_insert_range) {
3791 const auto sourceStart = QRangeModelDetails::pos(*source, sourceRow);
3792 const auto sourceEnd = std::next(sourceStart, count);
3793
3794 destination->insert(destStart, std::move_iterator(sourceStart),
3795 std::move_iterator(sourceEnd));
3796 } else if constexpr (std::is_copy_constructible_v<row_type>) {
3797 // otherwise we have to make space first, and copy later.
3798 destination->insert(destStart, count, row_type{});
3799 }
3800
3801 row_ptr parentRow = destParent.isValid()
3802 ? QRangeModelDetails::pointerTo(this->rowData(destParent))
3803 : nullptr;
3804
3805 // if the source's parent was already inside the new parent row,
3806 // then the source row might have become invalid, so reset it.
3807 if (parentRow == static_cast<row_ptr>(sourceParent.internalPointer())) {
3808 if (sourceParent.row() < destRow) {
3809 source = this->childRange(sourceParent);
3810 } else {
3811 // the source parent moved down within destination
3812 source = this->childRange(this->createIndex(sourceParent.row() + count, 0,
3813 sourceParent.internalPointer()));
3814 }
3815 }
3816
3817 // move the data over and update the parent pointer
3818 {
3819 const auto writeStart = QRangeModelDetails::pos(destination, destRow);
3820 const auto writeEnd = std::next(writeStart, count);
3821 const auto sourceStart = QRangeModelDetails::pos(source, sourceRow);
3822 const auto sourceEnd = std::next(sourceStart, count);
3823
3824 for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
3825 // move data over if not already done, otherwise
3826 // only fix the parent pointer
3827 if constexpr (!range_features::has_insert_range)
3828 *write = std::move(*read);
3829 this->protocol().setParentRow(QRangeModelDetails::refTo(*write), parentRow);
3830 }
3831 // remove the old rows from the source parent
3832 source->erase(sourceStart, sourceEnd);
3833 }
3834
3835 // Fix the parent pointers in children of both source and destination
3836 // ranges, as the references to the entries might have become invalid.
3837 // We don't have to do that if the rows are pointers, as in that case
3838 // the references to the entries are stable.
3839 resetParentInChildren(destination);
3841
3842 this->endMoveRows();
3843 return true;
3844 }
3845
3846 auto makeEmptyRow(row_ptr parentRow)
3847 {
3848 // tree traversal protocol: if we are here, then it must be possible
3849 // to change the parent of a row.
3850 static_assert(tree_traits::has_setParentRow);
3851 row_type empty_row = this->protocol().newRow();
3852 if (QRangeModelDetails::isValid(empty_row) && parentRow)
3853 this->protocol().setParentRow(QRangeModelDetails::refTo(empty_row), parentRow);
3854 return empty_row;
3855 }
3856
3857 template <typename It, typename Sentinel>
3858 void deleteRemovedRows(It &&begin, Sentinel &&end)
3859 {
3860 if constexpr (tree_traits::has_deleteRow) {
3861 for (auto it = begin; it != end; ++it) {
3862 if constexpr (Base::isMutable()) {
3863 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
3864 if (QRangeModelDetails::isValid(children)) {
3865 deleteRemovedRows(QRangeModelDetails::adl_begin(children),
3866 QRangeModelDetails::adl_end(children));
3867 QRangeModelDetails::refTo(children) = range_type{ };
3868 }
3869 }
3870
3871 this->protocol().deleteRow(std::move(*it));
3872 }
3873 }
3874 }
3875
3876 void resetParentInChildren(range_type *children)
3877 {
3878 const auto persistentIndexList = this->persistentIndexList();
3879 const auto [firstColumn, lastColumn] = [&persistentIndexList]{
3880 int first = std::numeric_limits<int>::max();
3881 int last = -1;
3882 for (const auto &pmi : persistentIndexList) {
3883 first = (std::min)(pmi.column(), first);
3884 last = (std::max)(pmi.column(), last);
3885 }
3886 return std::pair(first, last);
3887 }();
3888
3889 resetParentInChildrenRecursive(children, firstColumn, lastColumn);
3890 }
3891
3892 void resetParentInChildrenRecursive(range_type *children, int pmiFromColumn, int pmiToColumn)
3893 {
3894 if constexpr (tree_traits::has_setParentRow && !rows_are_any_refs_or_pointers) {
3895 const bool changePersistentIndexes = pmiToColumn >= pmiFromColumn;
3896 const auto begin = QRangeModelDetails::adl_begin(*children);
3897 const auto end = QRangeModelDetails::adl_end(*children);
3898 for (auto it = begin; it != end; ++it) {
3899 decltype(auto) maybeChildren = this->protocol().childRows(*it);
3900 if (QRangeModelDetails::isValid(maybeChildren)) {
3901 auto &childrenRef = QRangeModelDetails::refTo(maybeChildren);
3902 auto *parentRow = QRangeModelDetails::pointerTo(*it);
3903
3904 int row = 0;
3905 for (auto &child : childrenRef) {
3906 const_row_ptr oldParent = this->protocol().parentRow(child);
3907 if (oldParent != parentRow) {
3908 if (changePersistentIndexes) {
3909 for (int column = pmiFromColumn; column <= pmiToColumn; ++column) {
3910 this->changePersistentIndex(this->createIndex(row, column, oldParent),
3911 this->createIndex(row, column, parentRow));
3912 }
3913 }
3914 this->protocol().setParentRow(child, parentRow);
3915 }
3916 ++row;
3917 }
3918 resetParentInChildrenRecursive(&childrenRef, pmiFromColumn, pmiToColumn);
3919 }
3920 }
3921 }
3922 }
3923
3924 bool autoConnectPropertiesRange(const range_type &range, const QModelIndex &parent) const
3925 {
3926 int rowIndex = 0;
3927 for (const auto &row : range) {
3928 if (!this->autoConnectPropertiesInRow(row, rowIndex, parent))
3929 return false;
3930 Q_ASSERT(QRangeModelDetails::isValid(row));
3931 const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3932 if (QRangeModelDetails::isValid(children)) {
3933 if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
3934 this->itemModel().index(rowIndex, 0, parent))) {
3935 return false;
3936 }
3937 }
3938 ++rowIndex;
3939 }
3940 return true;
3941 }
3942
3944 {
3945 return autoConnectPropertiesRange(*this->m_data.model(), {});
3946 }
3947
3948 decltype(auto) rowDataImpl(const QModelIndex &index) const
3949 {
3950 const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
3951 const range_type &siblings = childrenOf(parentRow);
3952 Q_ASSERT(index.row() < int(Base::size(siblings)));
3953 return *QRangeModelDetails::pos(siblings, index.row());
3954 }
3955
3956 decltype(auto) rowDataImpl(const QModelIndex &index)
3957 {
3958 row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
3959 range_type &siblings = childrenOf(parentRow);
3960 Q_ASSERT(index.row() < int(Base::size(siblings)));
3961 return *QRangeModelDetails::pos(siblings, index.row());
3962 }
3963
3964 const range_type *childRangeImpl(const QModelIndex &index) const
3965 {
3966 const auto &row = this->rowData(index);
3967 if (!QRangeModelDetails::isValid(row))
3968 return static_cast<const range_type *>(nullptr);
3969
3970 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3971 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
3972 }
3973
3974 range_type *childRangeImpl(const QModelIndex &index)
3975 {
3976 auto &row = this->rowData(index);
3977 if (!QRangeModelDetails::isValid(row))
3978 return static_cast<range_type *>(nullptr);
3979
3980 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
3981 using Children = std::remove_reference_t<decltype(children)>;
3982
3983 if constexpr (QRangeModelDetails::is_any_of<Children, std::optional>())
3984 if constexpr (std::is_default_constructible<typename Children::value_type>()) {
3985 if (!children)
3986 children.emplace(range_type{});
3987 }
3988
3989 return QRangeModelDetails::pointerTo(std::forward<decltype(children)>(children));
3990 }
3991
3992 const range_type &childrenOf(const_row_ptr row) const
3993 {
3994 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
3995 : *this->m_data.model();
3996 }
3997
3998 range_type &childrenOf(row_ptr row)
3999 {
4000 return row ? QRangeModelDetails::refTo(this->protocol().childRows(*row))
4001 : *this->m_data.model();
4002 }
4003
4004 template <typename LessThan>
4005 void sortImplRecursive(range_type &range, row_ptr parentRow, const LessThan &lessThan)
4006 {
4007 for (auto &row : range) {
4008 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(row));
4009 if (QRangeModelDetails::isValid(children)) {
4010 sortImplRecursive(QRangeModelDetails::refTo(children),
4011 QRangeModelDetails::pointerTo(row), lessThan);
4012 }
4013 }
4014 this->sortSubRange(range, parentRow, lessThan);
4015 }
4016
4017 template <typename LessThan>
4018 void sortImpl(const LessThan &lessThan)
4019 {
4020 sortImplRecursive(*this->m_data.model(), nullptr, lessThan);
4021 resetParentInChildren(this->m_data.model());
4022 }
4023
4024 void prunePersistentIndexList(QModelIndexList &list, row_ptr expectedParent)
4025 {
4026 erase_if(list, [expectedParent](const QModelIndex &index){
4027 return static_cast<row_ptr>(index.internalPointer()) != expectedParent;
4028 });
4029 }
4030
4031 void matchImplRecursive(const range_type &range, const_row_ptr parentPtr, int from, int to,
4032 int role, const QVariant &value, int hits, Qt::MatchFlags flags, int column,
4033 QModelIndexList &result, const QCollator &collator) const
4034 {
4035 auto it = QRangeModelDetails::pos(range, from);
4036 auto end = QRangeModelDetails::adl_end(range);
4037
4038 const bool recurse = flags.testAnyFlag(Qt::MatchRecursive);
4039 const bool allHits = (hits == -1);
4040
4041 for (int r = from; it != end && r < to && (allHits || result.size() < hits); ++it, ++r) {
4042 const QModelIndex index = this->createIndex(r, column, parentPtr);
4043 if (this->matchRow(*it, index, role, value, flags, collator))
4044 result.append(index);
4045
4046 if (recurse) {
4047 decltype(auto) children = this->protocol().childRows(QRangeModelDetails::refTo(*it));
4048
4049 if (QRangeModelDetails::isValid(children)) {
4050 matchImplRecursive(QRangeModelDetails::refTo(children),
4051 QRangeModelDetails::pointerTo(*it), 0,
4052 int(QRangeModelDetails::size(QRangeModelDetails::refTo(children))),
4053 role, value, hits, flags, column, result, collator);
4054 }
4055 }
4056 }
4057 }
4058
4059 QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value, int hits,
4060 Qt::MatchFlags flags, const QCollator &collator) const
4061 {
4062 QModelIndexList result;
4063 const bool wrap = flags.testAnyFlag(Qt::MatchWrap);
4064 const int column = start.column();
4065 const int from = start.row();
4066 const int to = this->rowCount(start.parent());
4067
4068 for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) {
4069 const int fromRow = (i == 0) ? from : 0;
4070 const int toRow = (i == 0) ? to : from;
4071 matchImplRecursive(*this->m_data.model(), nullptr, fromRow, toRow,
4072 role, value, hits, flags, column, result, collator);
4073 }
4074 return result;
4075 }
4076
4077 // tree models don't overwrite the data at index, but instead insert a
4078 // child item
4079 bool dropOnItem(const QMimeData *, const QModelIndex &)
4080 {
4081 return false;
4082 }
4083};
4084
4085// specialization for flat models without protocol
4086template <typename Range>
4089{
4092
4093 static constexpr bool is_mutable_impl = true;
4094
4095public:
4096 using range_type = typename Base::range_type;
4098 using row_type = typename Base::row_type;
4099 using row_ptr = typename Base::row_ptr;
4101 using row_traits = typename Base::row_traits;
4102 using row_features = typename Base::row_features;
4103
4104 explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
4105 : Base(std::forward<Range>(model), {}, itemModel)
4106 {}
4107
4108protected:
4109 QModelIndex indexImpl(int row, int column, const QModelIndex &) const
4110 {
4111 if constexpr (Base::dynamicColumns()) {
4112 if (column < int(Base::size(*QRangeModelDetails::pos(*this->m_data.model(), row))))
4113 return this->createIndex(row, column);
4114#ifndef QT_NO_DEBUG
4115 // if we got here, then column < columnCount(), but this row is too short
4116 qCritical("QRangeModel: Column-range at row %d is not large enough!", row);
4117#endif
4118 return {};
4119 } else {
4120 return this->createIndex(row, column);
4121 }
4122 }
4123
4124 QModelIndex parentImpl(const QModelIndex &) const
4125 {
4126 return {};
4127 }
4128
4129 int rowCountImpl(const QModelIndex &parent) const
4130 {
4131 if (parent.isValid())
4132 return 0;
4133 return int(Base::size(*this->m_data.model()));
4134 }
4135
4136 int columnCountImpl(const QModelIndex &parent) const
4137 {
4138 if (parent.isValid())
4139 return 0;
4140
4141 // in a table, all rows have the same number of columns (as the first row)
4142 if constexpr (Base::dynamicColumns()) {
4143 return int(Base::size(*this->m_data.model()) == 0
4144 ? 0
4145 : Base::size(*QRangeModelDetails::adl_begin(*this->m_data.model())));
4146 } else {
4147 return Base::fixedColumnCount();
4148 }
4149 }
4150
4151 static constexpr Qt::ItemFlags defaultFlags()
4152 {
4153 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
4154 }
4155
4156 static constexpr bool canInsertRowsImpl()
4157 {
4158 return Base::dynamicRows() && range_features::has_insert;
4159 }
4160
4161 static constexpr bool canRemoveRowsImpl()
4162 {
4163 return Base::dynamicRows() && range_features::has_erase;
4164 }
4165
4166 static constexpr bool canMoveColumns(const QModelIndex &source, const QModelIndex &destination)
4167 {
4168 return !source.isValid() && !destination.isValid();
4169 }
4170
4171 static constexpr bool canMoveRows(const QModelIndex &source, const QModelIndex &destination)
4172 {
4173 return !source.isValid() && !destination.isValid();
4174 }
4175
4176 constexpr bool moveRowsAcross(const QModelIndex &, int , int,
4177 const QModelIndex &, int) noexcept
4178 {
4179 // table/flat model: can't move rows between different parents
4180 return false;
4181 }
4182
4183 auto makeEmptyRow(typename Base::row_ptr)
4184 {
4185 row_type empty_row = this->protocol().newRow();
4186
4187 // dynamically sized rows all have to have the same column count
4188 if constexpr (Base::dynamicColumns() && row_features::has_resize) {
4189 if (QRangeModelDetails::isValid(empty_row))
4190 QRangeModelDetails::refTo(empty_row).resize(this->columnCount({}));
4191 }
4192
4193 return empty_row;
4194 }
4195
4196 template <typename It, typename Sentinel>
4197 void deleteRemovedRows(It &&begin, Sentinel &&end)
4198 {
4199 if constexpr (Base::protocol_traits::has_deleteRow) {
4200 for (auto it = begin; it != end; ++it)
4201 this->protocol().deleteRow(std::move(*it));
4202 }
4203 }
4204
4205 decltype(auto) rowDataImpl(const QModelIndex &index) const
4206 {
4207 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
4208 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
4209 }
4210
4211 decltype(auto) rowDataImpl(const QModelIndex &index)
4212 {
4213 Q_ASSERT(q20::cmp_less(index.row(), Base::size(*this->m_data.model())));
4214 return *QRangeModelDetails::pos(*this->m_data.model(), index.row());
4215 }
4216
4217 const range_type *childRangeImpl(const QModelIndex &) const
4218 {
4219 return nullptr;
4220 }
4221
4222 range_type *childRangeImpl(const QModelIndex &)
4223 {
4224 return nullptr;
4225 }
4226
4227 const range_type &childrenOf(const_row_ptr row) const
4228 {
4229 Q_ASSERT(!row);
4230 return *this->m_data.model();
4231 }
4232
4234 {
4235 Q_ASSERT(!row);
4236 return *this->m_data.model();
4237 }
4238
4239 void resetParentInChildren(range_type *)
4240 {
4241 }
4242
4244 {
4245 bool result = true;
4246 int rowIndex = 0;
4247 for (const auto &row : *this->m_data.model()) {
4248 result &= this->autoConnectPropertiesInRow(row, rowIndex, {});
4249 ++rowIndex;
4250 }
4251 return result;
4252 }
4253
4254 template <typename LessThan>
4255 void sortImpl(const LessThan &lessThan)
4256 {
4257 this->sortSubRange(*this->m_data.model(), nullptr, lessThan);
4258 }
4259
4260 void prunePersistentIndexList(QModelIndexList &, typename Base::row_ptr) {}
4261
4262 QModelIndexList matchImpl(const QModelIndex &start, int role, const QVariant &value,
4263 int hits, Qt::MatchFlags flags, const QCollator &collator) const
4264 {
4265 QModelIndexList result;
4266 const bool wrap = flags.testAnyFlag(Qt::MatchWrap);
4267 const bool allHits = (hits == -1);
4268 const int column = start.column();
4269 int from = start.row();
4270 int to = this->rowCount({});
4271 decltype(auto) siblings = *this->m_data.model();
4272
4273 for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) {
4274 auto it = QRangeModelDetails::pos(siblings, from);
4275 auto end = QRangeModelDetails::adl_end(siblings);
4276 for (int r = from; it != end && r < to && (allHits || result.size() < hits);
4277 ++it, ++r) {
4278 if (!QRangeModelDetails::isValid(*it))
4279 continue;
4280 const QModelIndex index = this->createIndex(r, column, nullptr);
4281 if (this->matchRow(*it, index, role, value, flags, collator))
4282 result.append(index);
4283 }
4284 from = 0;
4285 to = start.row();
4286 }
4287
4288 return result;
4289 }
4290
4291 // flat models can overwrite data of the dropped-on item
4292 bool dropOnItem(const QMimeData *data, const QModelIndex &index)
4293 {
4294 return this->dropDataOnItem(data, index);
4295 }
4296};
4297
4298QT_END_NAMESPACE
4299
4300#endif // Q_QDOC
4301
4302#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
static Qt::weak_ordering compareData(const QVariant &lhs, const QVariant &rhs, const QCollator *collator)
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)
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))))
constexpr bool isValid(const T &t) noexcept
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
decltype(auto) refTo(T &&t)
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 auto pos(C &&c, int i)
static constexpr bool hasHeaderData
static constexpr bool hasCanDropMimeData
static constexpr bool hasRowFlags
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 DroppedEntryCell &lhs, const DroppedEntryCell &rhs) noexcept
friend bool operator==(const DroppedEntryCell &lhs, const DroppedEntryCell &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