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