Revision control
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QRANGEMODELADAPTER_IMPL_H
++ #define QRANGEMODELADAPTER_IMPL_H
++
++ #ifndef Q_QDOC
++
++ #ifndef QRANGEMODELADAPTER_H
++ #error Do not include qrangemodeladapter_impl.h directly
++ #endif
++
++ #if 0
++ #pragma qt_sync_skip_header_check
++ #pragma qt_sync_stop_processing
++ #endif
++
++ #include <QtCore/qrangemodel.h>
++ #include <QtCore/qspan.h>
++ #include <set>
++ #include <memory>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QRangeModelDetails
++ {
++ template <typename Range, typename Protocol>
++ using RangeImplementation = std::conditional_t<std::is_void_v<Protocol>,
++ std::conditional_t<is_tree_range<Range>::value,
++ QGenericTreeItemModelImpl<Range, DefaultTreeProtocol<Range>>,
++ QGenericTableItemModelImpl<Range>
++ >,
++ QGenericTreeItemModelImpl<Range, Protocol>
++ >;
++
++ template <typename ...Args>
++ using construct_rangeModel_test = decltype(QRangeModel(std::declval<Args &&>()...));
++
++ template <typename ...Args>
++ static constexpr bool can_construct_rangeModel = qxp::is_detected_v<construct_rangeModel_test,
++ Args...>;
++
++ template <typename ...Args>
++ using if_can_construct = std::enable_if_t<can_construct_rangeModel<Args...>, bool>;
++
++ template <typename Output, typename Input>
++ decltype(auto) forwardOrConvert(Input&& input)
++ {
++ if constexpr (std::is_same_v<q20::remove_cvref<Output>, q20::remove_cvref<Input>>)
++ return std::forward<Input>(input);
++ else
++ return Output(std::forward<Input>(input));
++ }
++
++ // we can't use wrapped_t, we only want to unpack smart pointers, and maintain
++ // the pointer nature of the type.
++ template <typename T, typename = void>
++ struct data_type { using type = T; };
++ template <>
++ struct data_type<void> { using type = QVariant; };
++
++ // pointer types of iterators use QtPrivate::ArrowProxy if the type does not
++ // provide operator->() (or is a pointer).
++ template <typename T, typename = void> struct test_pointerAccess : std::false_type {};
++ template <typename T> struct test_pointerAccess<T *> : std::true_type {};
++ template <typename T>
++ struct test_pointerAccess<T, std::void_t<decltype(std::declval<T>().operator->())>>
++ : std::true_type
++ {};
++
++ template <typename T>
++ using data_pointer_t = std::conditional_t<test_pointerAccess<T>::value,
++ T, QtPrivate::ArrowProxy<T>>;
++
++ // Helpers to make a type const "in depth", taking into account raw pointers
++ // and wrapping types, like smart pointers and std::reference_wrapper.
++
++ // We need to return data by value, not by reference, as we might only have
++ // temporary values (i.e. a QVariant returned by QAIM::data).
++ template <typename T, typename = void> struct AsConstData { using type = T; };
++ template <typename T> struct AsConstData<const T &> { using type = T; };
++ template <typename T> struct AsConstData<T *> { using type = const T *; };
++ template <template <typename> typename U, typename T>
++ struct AsConstData<U<T>, std::enable_if_t<is_any_shared_ptr<U<T>>::value>>
++ { using type = U<const T>; };
++ template <typename T> struct AsConstData<std::reference_wrapper<T>>
++ { using type = std::reference_wrapper<const T>; };
++
++ template <typename T> using asConst_t = typename AsConstData<T>::type;
++
++ // Rows get wrapped into a "view", as a begin/end iterator/sentinel pair.
++ // The iterator dereferences to the const version of the value returned by
++ // the underlying iterator.
++ // Could be replaced with std::views::sub_range in C++ 20.
++ template <typename const_row_type, typename Iterator, typename Sentinel>
++ struct RowView
++ {
++ // this is similar to C++23's std::basic_const_iterator, but we don't want
++ // to convert to the underlying const_iterator.
++ struct iterator
++ {
++ using value_type = asConst_t<typename Iterator::value_type>;
++ using difference_type = typename Iterator::difference_type;
++ using pointer = QRangeModelDetails::data_pointer_t<value_type>;
++ using reference = value_type;
++ using const_reference = value_type;
++ using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
++
++ template <typename I, typename Category>
++ static constexpr bool is_atLeast = std::is_base_of_v<Category,
++ typename std::iterator_traits<I>::iterator_category>;
++ template <typename I, typename Category>
++ using if_atLeast = std::enable_if_t<is_atLeast<I, Category>, bool>;
++
++ reference operator*() const { return *m_it; }
++ pointer operator->() const { return operator*(); }
++
++ // QRM requires at least forward_iterator, so we provide both post- and
++ // prefix increment unconditionally
++ friend constexpr iterator &operator++(iterator &it)
++ noexcept(noexcept(++std::declval<Iterator&>()))
++ {
++ ++it.m_it;
++ return it;
++ }
++ friend constexpr iterator operator++(iterator &it, int)
++ noexcept(noexcept(std::declval<Iterator&>()++))
++ {
++ iterator copy = it;
++ ++copy.m_it;
++ return copy;
++ }
++
++ template <typename I = Iterator, if_atLeast<I, std::bidirectional_iterator_tag> = true>
++ friend constexpr iterator &operator--(iterator &it)
++ noexcept(noexcept(--std::declval<I&>()))
++ {
++ --it.m_it;
++ return it;
++ }
++ template <typename I = Iterator, if_atLeast<I, std::bidirectional_iterator_tag> = true>
++ friend constexpr iterator operator--(iterator &it, int)
++ noexcept(noexcept(std::declval<I&>()--))
++ {
++ iterator copy = it;
++ --it.m_it;
++ return copy;
++ }
++
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr iterator &operator+=(iterator &it, difference_type n)
++ noexcept(noexcept(std::declval<I&>() += 1))
++ {
++ it.m_it += n;
++ return it;
++ }
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr iterator &operator-=(iterator &it, difference_type n)
++ noexcept(noexcept(std::declval<I&>() -= 1))
++ {
++ it.m_it -= n;
++ return it;
++ }
++
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr iterator operator+(const iterator &it, difference_type n)
++ noexcept(noexcept(std::declval<I&>() + 1))
++ {
++ iterator copy = it;
++ copy.m_it += n;
++ return copy;
++ }
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr iterator operator+(difference_type n, const iterator &it)
++ noexcept(noexcept(1 + std::declval<I&>()))
++ {
++ return it + n;
++ }
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr iterator operator-(const iterator &it, difference_type n)
++ noexcept(noexcept(std::declval<I&>() - 1))
++ {
++ iterator copy = it;
++ copy.m_it = it.m_it - n;
++ return copy;
++ }
++
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ constexpr reference operator[](difference_type n) const
++ noexcept(noexcept(I::operator[]()))
++ {
++ return m_it[n];
++ }
++
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr difference_type operator-(const iterator &lhs, const iterator &rhs)
++ noexcept(noexcept(std::declval<I&>() - std::declval<I&>()))
++ {
++ return lhs.m_it - rhs.m_it;
++ }
++
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr bool operator<(const iterator &lhs, const iterator &rhs)
++ noexcept(noexcept(std::declval<I&>() < std::declval<I&>()))
++ {
++ return lhs.m_it < rhs.m_it;
++ }
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr bool operator<=(const iterator &lhs, const iterator &rhs)
++ noexcept(noexcept(std::declval<I&>() <= std::declval<I&>()))
++ {
++ return lhs.m_it <= rhs.m_it;
++ }
++
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr bool operator>(const iterator &lhs, const iterator &rhs)
++ noexcept(noexcept(std::declval<I&>() > std::declval<I&>()))
++ {
++ return lhs.m_it > rhs.m_it;
++ }
++ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
++ friend constexpr bool operator>=(const iterator &lhs, const iterator &rhs)
++ noexcept(noexcept(std::declval<I&>() >= std::declval<I&>()))
++ {
++ return lhs.m_it >= rhs.m_it;
++ }
++
++ // This would implement the P2836R1 fix from std::basic_const_iterator,
++ // but a const_iterator on a range<pointer> would again allow us to
++ // mutate the pointed-to object, which is exactly what we want to
++ // prevent.
++ /*
++ template <typename CI, std::enable_if_t<std::is_convertible_v<const Iterator &, CI>, bool> = true>
++ operator CI() const
++ {
++ return CI{m_it};
++ }
++
++ template <typename CI, std::enable_if_t<std::is_convertible_v<Iterator, CI>, bool> = true>
++ operator CI() &&
++ {
++ return CI{std::move(m_it)};
++ }
++ */
++
++ friend bool comparesEqual(const iterator &lhs, const iterator &rhs) noexcept
++ {
++ return lhs.m_it == rhs.m_it;
++ }
++ Q_DECLARE_EQUALITY_COMPARABLE(iterator)
++
++ Iterator m_it;
++ };
++
++ using value_type = typename iterator::value_type;
++ using difference_type = typename iterator::difference_type;
++
++ friend bool comparesEqual(const RowView &lhs, const RowView &rhs) noexcept
++ {
++ return lhs.m_begin == rhs.m_begin;
++ }
++ Q_DECLARE_EQUALITY_COMPARABLE(RowView)
++
++ template <typename RHS>
++ bool operator==(const RHS &rhs) const noexcept
++ {
++ return m_begin == QRangeModelDetails::begin(rhs);
++ }
++ template <typename RHS>
++ bool operator!=(const RHS &rhs) const noexcept
++ {
++ return !operator==(rhs);
++ }
++
++ value_type at(difference_type n) const { return *std::next(m_begin, n); }
++
++ iterator begin() const { return iterator{m_begin}; }
++ iterator end() const { return iterator{m_end}; }
++
++ Iterator m_begin;
++ Sentinel m_end;
++ };
++
++ // Const-in-depth mapping for row types. We do store row types, and they might
++ // be move-only, so we return them by const reference.
++ template <typename T, typename = void> struct AsConstRow { using type = const T &; };
++ // Otherwise the mapping for basic row types is the same as for data.
++ template <typename T> struct AsConstRow<T *> : AsConstData<T *> {};
++ template <template <typename> typename U, typename T>
++ struct AsConstRow<U<T>, std::enable_if_t<is_any_shared_ptr<U<T>>::value>> : AsConstData<U<T>> {};
++ template <typename T> struct AsConstRow<std::reference_wrapper<T>>
++ : AsConstData<std::reference_wrapper<T>> {};
++
++ template <typename T> using if_range = std::enable_if_t<is_range_v<T>, bool>;
++ // If the row type is a range, then we assume that the first type is the
++ // element type.
++ template <template <typename, typename ...> typename R, typename T, typename ...Args>
++ struct AsConstRow<R<T, Args...>, if_range<R<T, Args...>>>
++ {
++ using type = const R<T, Args...> &;
++ };
++
++ // specialize for range of pointers and smart pointers
++ template <template <typename, typename ...> typename R, typename T, typename ...Args>
++ struct AsConstRow<R<T *, Args...>, if_range<R<T *, Args...>>>
++ {
++ using row_type = R<T, Args...>;
++ using const_iterator = typename row_type::const_iterator;
++ using const_row_type = R<asConst_t<T>>;
++ using type = RowView<const_row_type, const_iterator, const_iterator>;
++ };
++
++ template <template <typename, typename ...> typename R, typename T, typename ...Args>
++ struct AsConstRow<R<T, Args...>,
++ std::enable_if_t<std::conjunction_v<is_range<R<T, Args...>>, is_any_shared_ptr<T>>>
++ >
++ {
++ using row_type = R<T, Args...>;
++ using const_iterator = typename row_type::const_iterator;
++ using const_row_type = R<asConst_t<T>>;
++ using type = RowView<const_row_type, const_iterator, const_iterator>;
++ };
++
++ template <typename T>
++ using asConstRow_t = typename AsConstRow<T>::type;
++
++ Q_CORE_EXPORT QVariant qVariantAtIndex(const QModelIndex &index);
++
++ template <typename Type>
++ static inline Type dataAtIndex(const QModelIndex &index)
++ {
++ Q_ASSERT_X(index.isValid(), "QRangeModelAdapter::dataAtIndex", "Index at position is invalid");
++ QVariant variant = qVariantAtIndex(index);
++
++ if constexpr (std::is_same_v<QVariant, Type>)
++ return variant;
++ else
++ return variant.value<Type>();
++ }
++
++ template <typename Type>
++ static inline Type dataAtIndex(const QModelIndex &index, int role)
++ {
++ Q_ASSERT_X(index.isValid(), "QRangeModelAdapter::dataAtIndex", "Index at position is invalid");
++ QVariant variant = index.data(role);
++
++ if constexpr (std::is_same_v<QVariant, Type>)
++ return variant;
++ else
++ return variant.value<Type>();
++ }
++
++ template <bool isTree = false>
++ struct ParentIndex
++ {
++ ParentIndex(const QModelIndex &dummy = {}) { Q_ASSERT(!dummy.isValid()); }
++ QModelIndex root() const { return {}; }
++ };
++
++ template <>
++ struct ParentIndex<true>
++ {
++ QModelIndex m_rootIndex;
++ QModelIndex root() const { return m_rootIndex; }
++ };
++
++ template <typename Model, typename Impl>
++ struct AdapterStorage : ParentIndex<Impl::protocol_traits::is_tree>
++ {
++ // If it is, then we can shortcut the model and operate on the container.
++ // Otherwise we have to go through the model's vtable. For now, this is always
++ // the case.
++ static constexpr bool isRangeModel = std::is_same_v<Model, QRangeModel>;
++ static_assert(isRangeModel, "The model must be a QRangeModel (not a subclass).");
++ std::shared_ptr<QRangeModel> m_model;
++
++ template <typename I = Impl, std::enable_if_t<I::protocol_traits::is_tree, bool> = true>
++ explicit AdapterStorage(const std::shared_ptr<QRangeModel> &model, const QModelIndex &root)
++ : ParentIndex<Impl::protocol_traits::is_tree>{root}, m_model(model)
++ {
++ }
++
++ explicit AdapterStorage(Model *model)
++ : m_model{model}
++ {}
++
++ const Impl *implementation() const
++ {
++ return static_cast<const Impl *>(QRangeModelImplBase::getImplementation(m_model.get()));
++ }
++
++ Impl *implementation()
++ {
++ return static_cast<Impl *>(QRangeModelImplBase::getImplementation(m_model.get()));
++ }
++
++ auto *operator->()
++ {
++ if constexpr (isRangeModel)
++ return implementation();
++ else
++ return m_model.get();
++ }
++
++ const auto *operator->() const
++ {
++ if constexpr (isRangeModel)
++ return implementation();
++ else
++ return m_model.get();
++ }
++ };
++
++ } // QRangeModelDetails
++
++ QT_END_NAMESPACE
++
++ #endif // Q_QDOC
++
++ #endif // QRANGEMODELADAPTER_IMPL_H