17class QRangeModelPrivate : QAbstractItemModelPrivate
19 Q_DECLARE_PUBLIC(QRangeModel)
22 explicit QRangeModelPrivate(std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl)
23 : impl(std::move(impl))
25 this->impl->call<QRangeModelImplBase::InterfaceVersion>(m_interfaceVersion);
26 if (m_interfaceVersion >= QT_VERSION_CHECK(6, 12, 0)) {
27 m_supportedDropActions =
this->impl->call<QRangeModelImplBase::AdjustSupportedDropActions>(
28 m_supportedDropActions
33 std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl;
34 friend class QRangeModelImplBase;
36 static QRangeModelPrivate *get(QRangeModel *model) {
return model->d_func(); }
37 static const QRangeModelPrivate *get(
const QRangeModel *model) {
return model->d_func(); }
39 mutable QHash<
int, QByteArray> m_roleNames;
40 QRangeModel::AutoConnectPolicy m_autoConnectPolicy = QRangeModel::AutoConnectPolicy::None;
41 bool m_dataChangedDispatchBlocked =
false;
42 int m_interfaceVersion = -1;
43 int m_sortRole = Qt::DisplayRole;
44 std::optional<QCollator> m_sortCollator;
45 std::optional<QCollator> m_matchCollator;
46 mutable std::optional<QStringList> m_mimeTypes;
47 Qt::DropActions m_supportedDragActions = Qt::CopyAction;
48 Qt::DropActions m_supportedDropActions = Qt::CopyAction;
50 static void emitDataChanged(
const QModelIndex &index,
int role)
52 const auto *model =
static_cast<
const QRangeModel *>(index.model());
53 if (!get(model)->m_dataChangedDispatchBlocked)
54 const_cast<QRangeModel *>(model)->dataChanged(index, index, {role});
57 static bool compareModelIndex(
const QModelIndex &left,
const QModelIndex &right);
104 Q_ASSERT(
std::holds_alternative<Data>(storage));
105 const auto &data =
std::get<Data>(storage);
106 if (!data.index.isValid()) {
107 if (!QObject::disconnect(connection))
108 qWarning() <<
"Failed to break connection for" << Qt::ItemDataRole(data.role);
110 QRangeModelPrivate::emitDataChanged(data.index, data.role);
167QVariant QRangeModelImplBase::convertMatchValue(
const QVariant &value, Qt::MatchFlags flags)
169 QVariant matchValue = value;
170 const Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive
171 ? Qt::CaseSensitive : Qt::CaseInsensitive;
172 switch ((flags & Qt::MatchTypeMask).toInt()) {
173#if QT_CONFIG(regularexpression)
174 case Qt::MatchRegularExpression:
175 case Qt::MatchWildcard:
176 if (value.metaType() != QMetaType::fromType<QRegularExpression>()) {
177 QRegularExpression rx;
178 if (flags & Qt::MatchWildcard) {
179 rx.setPattern(QRegularExpression::wildcardToRegularExpression(
180 value.toString(), QRegularExpression::NonPathWildcardConversion));
182 rx.setPattern(value.toString());
184 if (cs == Qt::CaseInsensitive)
185 rx.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
190 case Qt::MatchStartsWith:
191 case Qt::MatchEndsWith:
192 case Qt::MatchFixedString:
193 case Qt::MatchContains:
194 matchValue.convert(QMetaType::fromType<QString>());
203 Qt::MatchFlags flags,
const QCollator *collator)
206 const uint matchType = (flags & Qt::MatchTypeMask).toInt();
207 const Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive
208 ? Qt::CaseSensitive : Qt::CaseInsensitive;
210#if QT_CONFIG(regularexpression)
211 case Qt::MatchRegularExpression:
212 case Qt::MatchWildcard:
213 return itemData.contains(value.toRegularExpression());
215 case Qt::MatchStartsWith:
217 const QString needle = value.toString();
218 return needle.size() <= itemData.size()
219 && collator->compare(QStringView(itemData).left(needle.size()), needle) == 0;
221 return itemData.startsWith(value.toString(), cs);
222 case Qt::MatchEndsWith:
224 const QString needle = value.toString();
225 return needle.size() <= itemData.size()
226 && collator->compare(QStringView(itemData).right(needle.size()), needle) == 0;
228 return itemData.endsWith(value.toString(), cs);
229 case Qt::MatchFixedString:
231 return collator->compare(itemData, value.toString()) == 0;
232 return itemData.compare(value.toString(), cs) == 0;
233 case Qt::MatchContains: {
235 const QString needle = value.toString();
236 const QStringView needleView(needle);
237 const QStringView itemDataView(itemData);
238 const qsizetype scans = itemData.size() - needle.size();
239 for (qsizetype pos = 0; pos <= scans; ++pos) {
240 if (collator->compare(itemDataView.sliced(pos, needle.size()), needleView) == 0)
245 return itemData.contains(value.toString(), cs);
269QHash<
int, QMetaProperty> QRangeModelImplBase::roleProperties(
const QAbstractItemModel &model,
270 const QMetaObject &metaObject)
272 const auto roles = model.roleNames();
273 QHash<
int, QMetaProperty> result;
274 for (
auto &&[role, roleName] : roles.asKeyValueRange()) {
275 if (role == Qt::RangeModelDataRole)
277 result[role] = metaObject.property(metaObject.indexOfProperty(roleName));
282QHash<
int, QMetaProperty> QRangeModelImplBase::columnProperties(
const QMetaObject &metaObject)
284 QHash<
int, QMetaProperty> result;
285 const int propertyOffset = metaObject.propertyOffset();
286 for (
int p = propertyOffset; p < metaObject.propertyCount(); ++p)
287 result[p - propertyOffset] = metaObject.property(p);
295 QRangeModelDetails::AutoConnectContext *context,
296 const QHash<
int, QMetaProperty> &properties)
301 auto connect = [item, context](
const QModelIndex &cell,
int role,
const QMetaProperty &property) {
302 if (property.hasNotifySignal()) {
303 if (!Handler(cell, item, context, role, property))
306 qWarning() <<
"Property" << property.name() <<
"for" << Qt::ItemDataRole(role)
307 <<
"at" << cell <<
"has no notify signal";
312 if (context->mapping == QRangeModelDetails::AutoConnectContext::AutoConnectMapping::Roles) {
313 for (
auto &&[role, property] : properties.asKeyValueRange())
314 connect(index, role, property);
316 for (
auto &&[column, property] : properties.asKeyValueRange())
317 connect(index.siblingAtColumn(column), Qt::DisplayRole, property);
322bool QRangeModelImplBase::connectProperty(
const QModelIndex &index,
const QObject *item,
323 QRangeModelDetails::AutoConnectContext *context,
324 int role,
const QMetaProperty &property)
328 PropertyChangedHandler handler{index, role};
329 auto connection = property.enclosingMetaObject()->connect(item, property.notifySignal(),
330 context, std::move(handler));
332 qWarning() <<
"Failed to connect to" << item << property.name();
340 handler = std::move(connection);
345bool QRangeModelImplBase::connectProperties(
const QModelIndex &index,
const QObject *item,
346 QRangeModelDetails::AutoConnectContext *context,
347 const QHash<
int, QMetaProperty> &properties)
349 return connectPropertiesHelper<QRangeModelImplBase::connectProperty>(index, item, context, properties);
352bool QRangeModelImplBase::connectPropertyConst(
const QModelIndex &index,
const QObject *item,
353 QRangeModelDetails::AutoConnectContext *context,
354 int role,
const QMetaProperty &property)
358 ConstPropertyChangedHandler handler{index, role};
359 if (!property.enclosingMetaObject()->connect(item, property.notifySignal(),
360 context, std::move(handler))) {
361 qWarning() <<
"Failed to connect to" << item << property.name();
368bool QRangeModelImplBase::connectPropertiesConst(
const QModelIndex &index,
const QObject *item,
369 QRangeModelDetails::AutoConnectContext *context,
370 const QHash<
int, QMetaProperty> &properties)
372 return connectPropertiesHelper<QRangeModelImplBase::connectPropertyConst>(index, item, context, properties);
1523QVariant QRangeModel::headerData(
int section, Qt::Orientation orientation,
int role)
const
1525 Q_D(
const QRangeModel);
1526 return d->impl->call<QRangeModelImplBase::HeaderData>(section, orientation, role);
1532bool QRangeModel::setHeaderData(
int section, Qt::Orientation orientation,
const QVariant &data,
1535 return QAbstractItemModel::setHeaderData(section, orientation, data, role);
1717bool QRangeModel::moveColumns(
const QModelIndex &sourceParent,
int sourceColumn,
int count,
1718 const QModelIndex &destinationParent,
int destinationColumn)
1721 return d->impl->call<QRangeModelImplBase::MoveColumns>(
1722 sourceParent, sourceColumn, count,
1723 destinationParent, destinationColumn);
1777bool QRangeModel::moveRows(
const QModelIndex &sourceParent,
int sourceRow,
int count,
1778 const QModelIndex &destinationParent,
int destinationRow)
1781 return d->impl->call<QRangeModelImplBase::MoveRows>(
1782 sourceParent, sourceRow, count,
1783 destinationParent, destinationRow);
1876bool QRangeModel::canDropMimeData(
const QMimeData *data, Qt::DropAction action,
1877 int row,
int column,
const QModelIndex &parent)
const
1879 Q_D(
const QRangeModel);
1880 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0))
1881 return QAbstractItemModel::canDropMimeData(data, action, row, column, parent);
1882 return d->impl->call<QRangeModelImplBase::CanDropMimeData>(data, action, row, column, parent);
1892bool QRangeModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
1893 int row,
int column,
const QModelIndex &parent)
1897 if (action == Qt::IgnoreAction)
1901 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0))
1902 return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
1903 if (d->impl->call<QRangeModelImplBase::DropMimeData>(data, action, row, column, parent))
1909 row = rowCount(parent);
1910#if !defined(QT_NO_DATASTREAM)
1911 const QString defaultFormat = QAbstractItemModel::mimeTypes().at(0);
1912 if (data->hasFormat(defaultFormat)) {
1913 QByteArray encoded = data->data(defaultFormat);
1914 QDataStream stream(&encoded, QDataStream::ReadOnly);
1915 return decodeData(row, column, parent, stream);
1921bool QRangeModelImplBase::dropDataOnItem(
const QMimeData *data,
const QModelIndex &index)
1923#if !defined(QT_NO_DATASTREAM)
1924 const QString defaultFormat = m_rangeModel->QAbstractItemModel::mimeTypes().at(0);
1925 if (data->hasFormat(defaultFormat)) {
1926 QByteArray encoded = data->data(defaultFormat);
1927 QDataStream stream(&encoded, QDataStream::ReadOnly);
1928 return m_rangeModel->d_func()->dropOnItem(index, stream);
1937bool QRangeModelPrivate::compareModelIndex(
const QModelIndex &left,
const QModelIndex &right)
1939 if (!left.isValid())
1940 return right.isValid();
1941 if (!right.isValid())
1943 const QModelIndex leftCol0 = left.column() ? left.siblingAtColumn(0) : left;
1944 const QModelIndex rightCol0 = right.column() ? right.siblingAtColumn(0) : right;
1945 const QModelIndex leftParent = leftCol0.parent();
1946 const QModelIndex rightParent = rightCol0.parent();
1947 if (leftParent == rightParent) {
1948 if (left.row() == right.row())
1949 return left.column() < right.column();
1950 return left.row() < right.row();
1953 if (leftCol0 == rightParent)
1955 if (rightCol0 == leftParent)
1959 auto makePath = [](
const QModelIndex &index) {
1961 Q_ASSERT(index.column() == 0);
1962 QVarLengthArray<
int, 32> path{index.row()};
1963 QModelIndex parent = index.parent();
1964 while (parent.isValid()) {
1965 path.append(parent.row());
1966 parent = parent.parent();
1968 std::reverse(path.begin(), path.end());
1971 const auto leftPath = makePath(leftCol0);
1972 const auto rightPath = makePath(rightCol0);
1973 return leftPath < rightPath;
1983QMimeData *QRangeModel::mimeData(
const QModelIndexList &indexes)
const
1985 Q_D(
const QRangeModel);
1986 if (indexes.isEmpty())
1989 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0))
1990 return QAbstractItemModel::mimeData(indexes);
1993 QModelIndexList groupedList = indexes;
1994 std::sort(groupedList.begin(), groupedList.end(), QRangeModelPrivate::compareModelIndex);
1995 QMimeData *data = d->impl->call<QRangeModelImplBase::MimeData>(groupedList);
1998 const QString defaultFormat = QAbstractItemModel::mimeTypes().at(0);
1999 if (!mimeTypes().contains(defaultFormat))
2002 std::unique_ptr<QMimeData> defaultMimeData(QAbstractItemModel::mimeData(indexes));
2004 return defaultMimeData.release();
2006 if (defaultMimeData) {
2007 const QStringList defaultTypes = defaultMimeData->formats();
2008 for (
const auto &defaultType : defaultTypes)
2009 data->setData(defaultType, defaultMimeData->data(defaultType));
2021QStringList QRangeModel::mimeTypes()
const
2023 Q_D(
const QRangeModel);
2024 if (!d->m_mimeTypes) {
2025 d->m_mimeTypes = d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0)
2026 ? QAbstractItemModel::mimeTypes()
2027 : d->impl->call<QRangeModelImplBase::MimeTypes>();
2029 return *d->m_mimeTypes;
2042QModelIndexList QRangeModel::match(
const QModelIndex &start,
int role,
const QVariant &value,
2043 int hits, Qt::MatchFlags flags)
const
2045 Q_D(
const QRangeModel);
2046 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0))
2047 return QAbstractItemModel::match(start, role, value, hits, flags);
2048 return d->impl->call<QRangeModelImplBase::Match>(start, role, value, hits, flags);
2054void QRangeModel::multiData(
const QModelIndex &index, QModelRoleDataSpan roleDataSpan)
const
2056 Q_D(
const QRangeModel);
2057 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 11, 0))
2058 return QAbstractItemModel::multiData(index, roleDataSpan);
2059 d->impl->call<QRangeModelImplBase::MultiData>(index, roleDataSpan);
2080QHash<
int, QByteArray> QRangeModelImplBase::roleNamesForMetaObject(
const QAbstractItemModel &model,
2081 const QMetaObject &metaObject)
2083 const auto defaults = model.QAbstractItemModel::roleNames();
2084 QHash<
int, QByteArray> result = {{Qt::RangeModelDataRole,
"modelData"}};
2085 int offset = metaObject.propertyOffset();
2086 for (
int i = offset; i < metaObject.propertyCount(); ++i) {
2087 const auto name = metaObject.property(i).name();
2088 const int defaultRole = defaults.key(name, -1);
2089 if (defaultRole != -1) {
2091 result[defaultRole] = name;
2093 result[Qt::UserRole + i - offset] = name;
2124void QRangeModel::setRoleNames(
const QHash<
int, QByteArray> &names)
2127 if (d->m_roleNames == names)
2130 d->impl->call<QRangeModelImplBase::InvalidateCaches>();
2131 if (d->m_autoConnectPolicy != AutoConnectPolicy::None)
2132 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
2134 d->m_roleNames = names;
2136 Q_EMIT roleNamesChanged();
2203void QRangeModel::setAutoConnectPolicy(QRangeModel::AutoConnectPolicy policy)
2206 if (d->m_autoConnectPolicy == policy)
2209 d->m_autoConnectPolicy = policy;
2210 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
2211 Q_EMIT autoConnectPolicyChanged(policy);
2231void QRangeModel::sort(
int column, Qt::SortOrder order)
2234 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0))
2235 return QAbstractItemModel::sort(column, order);
2237 d->impl->call<QRangeModelImplBase::Sort>(column, order);
2238 } QT_CATCH(
const std::bad_alloc &) {
2239 qCritical(
"QRangeModel::sort ran out of memory, sort likely incomplete.");
2376void QRangeModel::setSupportedDragActions(Qt::DropActions actions)
2379 actions = d->impl->call<QRangeModelImplBase::AdjustSupportedDragActions>(actions);
2380 if (actions == d->m_supportedDragActions)
2382 d->m_supportedDragActions = actions;
2383 Q_EMIT supportedDragActionsChanged(d->m_supportedDragActions);
2409void QRangeModel::setSupportedDropActions(Qt::DropActions actions)
2412 actions = d->impl->call<QRangeModelImplBase::AdjustSupportedDropActions>(actions);
2413 if (actions == d->m_supportedDropActions)
2415 d->m_supportedDropActions = actions;
2416 Q_EMIT supportedDropActionsChanged(d->m_supportedDropActions);
static bool connectPropertiesHelper(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, const QHash< int, QMetaProperty > &properties)