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