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.cpp
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#include "qrangemodel.h"
6#include <QtCore/qcollator.h>
7#include <QtCore/qsize.h>
8
9#include <QtCore/private/qabstractitemmodel_p.h>
10
11#include <variant>
12
13QT_BEGIN_NAMESPACE
14
15class QRangeModelPrivate : QAbstractItemModelPrivate
16{
17 Q_DECLARE_PUBLIC(QRangeModel)
18
19public:
20 explicit QRangeModelPrivate(std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl)
21 : impl(std::move(impl))
22 {
23 this->impl->call<QRangeModelImplBase::InterfaceVersion>(m_interfaceVersion);
24 }
25
26 std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl;
27 friend class QRangeModelImplBase;
28
29 static QRangeModelPrivate *get(QRangeModel *model) { return model->d_func(); }
30 static const QRangeModelPrivate *get(const QRangeModel *model) { return model->d_func(); }
31
32 mutable QHash<int, QByteArray> m_roleNames;
33 QRangeModel::AutoConnectPolicy m_autoConnectPolicy = QRangeModel::AutoConnectPolicy::None;
34 bool m_dataChangedDispatchBlocked = false;
35 int m_interfaceVersion = -1;
36 int m_sortRole = Qt::DisplayRole;
37 std::optional<QCollator> m_sortCollator;
38
39 static void emitDataChanged(const QModelIndex &index, int role)
40 {
41 const auto *model = static_cast<const QRangeModel *>(index.model());
42 if (!get(model)->m_dataChangedDispatchBlocked)
43 const_cast<QRangeModel *>(model)->dataChanged(index, index, {role});
44 }
45};
46
48{
49 PropertyChangedHandler(const QPersistentModelIndex &index, int role)
51 {}
52
53 // move-only
57 {
58 Q_ASSERT(std::holds_alternative<Data>(storage));
59 // A moved-from handler is essentially a reference to the moved-to
60 // handler (which lives inside QSlotObject/QCallableObject). This
61 // way we can update the stored handler with the created connection.
62 other.storage = this;
63 }
67
68 // we can assign a connection to a moved-from handler to update the
69 // handler stored in the QSlotObject/QCallableObject.
70 PropertyChangedHandler &operator=(QMetaObject::Connection &&connection)
71 {
72 Q_ASSERT(std::holds_alternative<PropertyChangedHandler *>(storage));
73 std::get<PropertyChangedHandler *>(storage)->connection = std::move(connection);
74 return *this;
75 }
76
77 void operator()();
78
79private:
80 QMetaObject::Connection connection;
81 struct Data
82 {
83 QPersistentModelIndex index;
84 int role = -1;
85 };
86 std::variant<PropertyChangedHandler *, Data> storage;
87};
88
90{
91 Q_ASSERT(std::holds_alternative<Data>(storage));
92 const auto &data = std::get<Data>(storage);
93 if (!data.index.isValid()) {
94 if (!QObject::disconnect(connection))
95 qWarning() << "Failed to break connection for" << Qt::ItemDataRole(data.role);
96 } else {
97 QRangeModelPrivate::emitDataChanged(data.index, data.role);
98 }
99}
100
102{
103 ConstPropertyChangedHandler(const QModelIndex &index, int role)
104 : index(index), role(role)
105 {}
106
107 // move-only
110
111 void operator()() { QRangeModelPrivate::emitDataChanged(index, role); }
112
113private:
114 QModelIndex index;
115 int role = -1;
116};
117
118QRangeModel::QRangeModel(QRangeModelImplBase *impl, QObject *parent)
119 : QAbstractItemModel(*new QRangeModelPrivate({impl, {}}), parent)
120{
121}
122
123QRangeModelImplBase *QRangeModelImplBase::getImplementation(QRangeModel *model)
124{
125 return model->d_func()->impl.get();
126}
127
128const QRangeModelImplBase *QRangeModelImplBase::getImplementation(const QRangeModel *model)
129{
130 return model->d_func()->impl.get();
131}
132
133QScopedValueRollback<bool> QRangeModelImplBase::blockDataChangedDispatch()
134{
135 return QScopedValueRollback(m_rangeModel->d_func()->m_dataChangedDispatchBlocked, true);
136}
137
138int QRangeModelImplBase::sortRole() const
139{
140 return m_rangeModel->sortRole();
141}
142
143const QCollator *QRangeModelImplBase::sortCollator() const
144{
145 const QRangeModelPrivate *d = QRangeModelPrivate::get(m_rangeModel);
146 return d->m_sortCollator ? &d->m_sortCollator.value() : nullptr;
147}
148
149QVariant QRangeModelImplBase::convertMatchValue(const QVariant &value, Qt::MatchFlags flags)
150{
151 QVariant matchValue = value;
152 const Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive
153 ? Qt::CaseSensitive : Qt::CaseInsensitive;
154 switch ((flags & Qt::MatchTypeMask).toInt()) {
155#if QT_CONFIG(regularexpression)
156 case Qt::MatchRegularExpression:
157 case Qt::MatchWildcard:
158 if (value.metaType() != QMetaType::fromType<QRegularExpression>()) {
159 QRegularExpression rx;
160 if (flags & Qt::MatchWildcard) {
161 rx.setPattern(QRegularExpression::wildcardToRegularExpression(
162 value.toString(), QRegularExpression::NonPathWildcardConversion));
163 } else {
164 rx.setPattern(value.toString());
165 }
166 if (cs == Qt::CaseInsensitive)
167 rx.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
168 matchValue = rx;
169 }
170 break;
171#endif // QT_CONFIG(regularexpression)
172 case Qt::MatchStartsWith:
173 case Qt::MatchEndsWith:
174 case Qt::MatchFixedString:
175 case Qt::MatchContains:
176 matchValue.convert(QMetaType::fromType<QString>());
177 break;
178 default:
179 break;
180 }
181 return matchValue;
182}
183
184bool QRangeModelImplBase::matchValue(const QString &itemData, const QVariant &value,
185 Qt::MatchFlags flags)
186{
187 // QString or regular expression based matching
188 const uint matchType = (flags & Qt::MatchTypeMask).toInt();
189 const Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive
190 ? Qt::CaseSensitive : Qt::CaseInsensitive;
191 switch (matchType) {
192#if QT_CONFIG(regularexpression)
193 case Qt::MatchRegularExpression:
194 case Qt::MatchWildcard:
195 return itemData.contains(value.toRegularExpression());
196#endif // QT_CONFIG(regularexpression)
197 case Qt::MatchStartsWith:
198 return itemData.startsWith(value.toString(), cs);
199 case Qt::MatchEndsWith:
200 return itemData.endsWith(value.toString(), cs);
201 case Qt::MatchFixedString:
202 return itemData.compare(value.toString(), cs) == 0;
203 case Qt::MatchContains:
204 return itemData.contains(value.toString(), cs);
205 default:
206 return false;
207 }
208}
209
210/*!
211 \internal
212
213 Using \a metaObject, return a mapping of roles to the matching QMetaProperties.
214*/
215QHash<int, QMetaProperty> QRangeModelImplBase::roleProperties(const QAbstractItemModel &model,
216 const QMetaObject &metaObject)
217{
218 const auto roles = model.roleNames();
219 QHash<int, QMetaProperty> result;
220 for (auto &&[role, roleName] : roles.asKeyValueRange()) {
221 if (role == Qt::RangeModelDataRole)
222 continue;
223 result[role] = metaObject.property(metaObject.indexOfProperty(roleName));
224 }
225 return result;
226}
227
228QHash<int, QMetaProperty> QRangeModelImplBase::columnProperties(const QMetaObject &metaObject)
229{
230 QHash<int, QMetaProperty> result;
231 const int propertyOffset = metaObject.propertyOffset();
232 for (int p = propertyOffset; p < metaObject.propertyCount(); ++p)
233 result[p - propertyOffset] = metaObject.property(p);
234 return result;
235}
236
237QRangeModelDetails::AutoConnectContext::~AutoConnectContext() = default;
238
239template <auto Handler>
240static bool connectPropertiesHelper(const QModelIndex &index, const QObject *item,
241 QRangeModelDetails::AutoConnectContext *context,
242 const QHash<int, QMetaProperty> &properties)
243{
244 if (!item)
245 return true;
246
247 auto connect = [item, context](const QModelIndex &cell, int role, const QMetaProperty &property) {
248 if (property.hasNotifySignal()) {
249 if (!Handler(cell, item, context, role, property))
250 return false;
251 } else {
252 qWarning() << "Property" << property.name() << "for" << Qt::ItemDataRole(role)
253 << "at" << cell << "has no notify signal";
254 }
255 return true;
256 };
257
258 if (context->mapping == QRangeModelDetails::AutoConnectContext::AutoConnectMapping::Roles) {
259 for (auto &&[role, property] : properties.asKeyValueRange())
260 connect(index, role, property);
261 } else {
262 for (auto &&[column, property] : properties.asKeyValueRange())
263 connect(index.siblingAtColumn(column), Qt::DisplayRole, property);
264 }
265 return true;
266}
267
268bool QRangeModelImplBase::connectProperty(const QModelIndex &index, const QObject *item,
269 QRangeModelDetails::AutoConnectContext *context,
270 int role, const QMetaProperty &property)
271{
272 if (!item)
273 return true; // nothing to do, continue
274 PropertyChangedHandler handler{index, role};
275 auto connection = property.enclosingMetaObject()->connect(item, property.notifySignal(),
276 context, std::move(handler));
277 if (!connection) {
278 qWarning() << "Failed to connect to" << item << property.name();
279 return false;
280 } else {
281 // handler is now in moved-from state, and acts like a reference to
282 // the handler that is stored in the QSlotObject/QCallableObject.
283 // This assignment updates the stored handler's connection with the
284 // QMetaObject::Connection handle, and should look harmless for
285 // static analyzers.
286 handler = std::move(connection);
287 }
288 return true;
289}
290
291bool QRangeModelImplBase::connectProperties(const QModelIndex &index, const QObject *item,
292 QRangeModelDetails::AutoConnectContext *context,
293 const QHash<int, QMetaProperty> &properties)
294{
295 return connectPropertiesHelper<QRangeModelImplBase::connectProperty>(index, item, context, properties);
296}
297
298bool QRangeModelImplBase::connectPropertyConst(const QModelIndex &index, const QObject *item,
299 QRangeModelDetails::AutoConnectContext *context,
300 int role, const QMetaProperty &property)
301{
302 if (!item)
303 return true; // nothing to do, continue
304 ConstPropertyChangedHandler handler{index, role};
305 if (!property.enclosingMetaObject()->connect(item, property.notifySignal(),
306 context, std::move(handler))) {
307 qWarning() << "Failed to connect to" << item << property.name();
308 return false;
309 } else {
310 return true;
311 }
312}
313
314bool QRangeModelImplBase::connectPropertiesConst(const QModelIndex &index, const QObject *item,
315 QRangeModelDetails::AutoConnectContext *context,
316 const QHash<int, QMetaProperty> &properties)
317{
318 return connectPropertiesHelper<QRangeModelImplBase::connectPropertyConst>(index, item, context, properties);
319}
320
322{
323Q_CORE_EXPORT QVariant qVariantAtIndex(const QModelIndex &index)
324{
329 };
332 size_t r = 0;
333 do {
334 variant = result[r].data();
335 ++r;
336 } while (!variant.isValid() && r < std::size(result));
337
338 return variant;
339}
340}
341
342/*!
343 \class QRangeModel
344 \inmodule QtCore
345 \since 6.10
346 \ingroup model-view
347 \brief QRangeModel implements QAbstractItemModel for any C++ range.
348 \reentrant
349
350 QRangeModel can make the data in any sequentially iterable C++ type
351 available to the \l{Model/View Programming}{model/view framework} of Qt.
352 This makes it easy to display existing data structures in the Qt Widgets
353 and Qt Quick item views, and to allow the user of the application to
354 manipulate the data using a graphical user interface.
355
356 To use QRangeModel, instantiate it with a C++ range and set it as
357 the model of one or more views:
358
359 \snippet qrangemodel/main.cpp array
360
361 \section1 Constructing the model
362
363 The range can be any C++ type for which the standard methods
364 \c{std::begin} and \c{std::end} are implemented, and for which the
365 returned iterator type satisfies \c{std::forward_iterator}. Certain model
366 operations will perform better if \c{std::size} is available, and if the
367 iterator satisfies \c{std::random_access_iterator}.
368
369 The range must be provided when constructing the model and can be provided
370 by value, reference wrapper, or pointer. How the model was constructed
371 defines whether changes through the model API will modify the original
372 data. Use QRangeModelAdapter to implicitly construct a model while also
373 having direct, type-safe, and convenient access to the model as a range.
374
375 When constructed by value, the model makes a copy of the range, and
376 QAbstractItemModel APIs that modify the model, such as setData() or
377 insertRows(), have no impact on the original range.
378
379 \snippet qrangemodel/main.cpp value
380
381 Changes made to the data can be monitored by connecting to the signals
382 emitted by the model, such as \l{QAbstractItemModel}{dataChanged()}.
383
384 To make modifications of the model affect the original range, provide the
385 range either by pointer:
386
387 \snippet qrangemodel/main.cpp pointer
388
389 or through a reference wrapper:
390
391 \snippet qrangemodel/main.cpp reference_wrapper
392
393 In this case, QAbstractItemModel APIs that modify the model also modify the
394 range. Methods that modify the structure of the range, such as insertRows()
395 or removeColumns(), use standard C++ container APIs \c{resize()},
396 \c{insert()}, \c{erase()}, in addition to dereferencing a mutating iterator
397 to set or clear the data.
398
399 \note Once the model has been constructed and passed on to a view, the
400 range that the model operates on must no longer be modified directly. Views
401 on the model wouldn't be informed about the changes, and structural changes
402 are likely to corrupt instances of QPersistentModelIndex that the model
403 maintains. Use QRangeModelAdapter to safely interact with the underlying
404 range while keeping the model updated.
405
406 The caller must make sure that the range's lifetime exceeds the lifetime of
407 the model.
408
409 Use smart pointers to make sure that the range is only deleted when all
410 clients are done with it.
411
412 \snippet qrangemodel/main.cpp smart_pointer
413
414 QRangeModel supports both shared and unique pointers.
415
416 \section2 Read-only or mutable
417
418 For ranges that are const objects, for which access always yields constant
419 values, or where the required container APIs are not available,
420 QRangeModel implements write-access APIs to do nothing and return
421 \c{false}. In the example using \c{std::array}, the model cannot add or
422 remove rows, as the number of entries in a C++ array is fixed. But the
423 values can be changed using setData(), and the user can trigger editing of
424 the values in the list view. By making the array const, the values also
425 become read-only.
426
427 \snippet qrangemodel/main.cpp const_array
428
429 The values are also read-only if the element type is const, like in
430
431 \snippet qrangemodel/main.cpp const_values
432
433 In the above examples using \c{std::vector}, the model can add or remove
434 rows, and the data can be changed. Passing the range as a constant
435 reference will make the model read-only.
436
437 \snippet qrangemodel/main.cpp const_ref
438
439 \note If the values in the range are const, then it's also not possible
440 to remove or insert columns and rows through the QAbstractItemModel API.
441 For more granular control, implement \l{the C++ tuple protocol}.
442
443 \section1 Rows and columns
444
445 The elements in the range are interpreted as rows of the model. Depending
446 on the type of these row elements, QRangeModel exposes the range as a
447 list, a table, or a tree.
448
449 If the row elements are simple values, then the range gets represented as a
450 list.
451
452 \snippet qrangemodel/main.cpp list_of_int
453
454 If the type of the row elements is an iterable range, such as a vector,
455 list, or array, then the range gets represented as a table.
456
457 \snippet qrangemodel/main.cpp grid_of_numbers
458
459 If the row type provides the standard C++ container APIs \c{resize()},
460 \c{insert()}, \c{erase()}, then columns can be added and removed via
461 insertColumns() and removeColumns(). All rows are required to have
462 the same number of columns.
463
464 \section2 Structs and gadgets as rows
465
466 If the row type implements \l{the C++ tuple protocol}, then the range gets
467 represented as a table with a fixed number of columns.
468
469 \snippet qrangemodel/main.cpp pair_int_QString
470
471 An easier and more flexible alternative to implementing the tuple protocol
472 for a C++ type is to use Qt's \l{Meta-Object System}{meta-object system} to
473 declare a type with \l{Qt's Property System}{properties}. This can be a
474 value type that is declared as a \l{Q_GADGET}{gadget}, or a QObject subclass.
475
476 \snippet qrangemodel/main.cpp gadget
477
478 Using QObject subclasses allows properties to be \l{Qt Bindable Properties}
479 {bindable}, or to have change notification signals. However, using QObject
480 instances for items has significant memory overhead.
481
482 Using Qt gadgets or objects is more convenient and can be more flexible
483 than implementing the tuple protocol. Those types are also directly
484 accessible from within QML. However, the access through \l{the property system}
485 comes with some runtime overhead. For performance critical models, consider
486 implementing the tuple protocol for compile-time generation of the access
487 code.
488
489 \section2 Multi-role items
490
491 The type of the items that the implementations of data(), setData(),
492 clearItemData() etc. operate on can be the same across the entire model -
493 like in the \c{gridOfNumbers} example above. But the range can also have
494 different item types for different columns, like in the \c{numberNames}
495 case.
496
497 By default, the value gets used for the Qt::DisplayRole and Qt::EditRole
498 roles. Most views expect the value to be
499 \l{QVariant::canConvert}{convertible to and from a QString} (but a custom
500 delegate might provide more flexibility).
501
502 \section3 Associative containers with multiple roles
503
504 If the item is an associative container that uses \c{int},
505 \l{Qt::ItemDataRole}, or QString as the key type, and QVariant as the
506 mapped type, then QRangeModel interprets that container as the storage
507 of the data for multiple roles. The data() and setData() functions return
508 and modify the mapped value in the container, and setItemData() modifies all
509 provided values, itemData() returns all stored values, and clearItemData()
510 clears the entire container.
511
512 \snippet qrangemodel/main.cpp color_map
513
514 The most efficient data type to use as the key is Qt::ItemDataRole or
515 \c{int}. When using \c{int}, itemData() returns the container as is, and
516 doesn't have to create a copy of the data.
517
518 \section3 Gadgets and Objects as multi-role items
519
520 Gadgets and QObject types can also be represented as multi-role items. The
521 \l{The Property System}{properties} of those items will be used for the
522 role for which the \l{roleNames()}{name of a role} matches. If all items
523 hold the same type of gadget or QObject, then the \l{roleNames()}
524 implementation in QRangeModel will return the list of properties of that
525 type.
526
527 \snippet qrangemodel/main.cpp color_gadget_decl
528 \snippet qrangemodel/main.cpp color_gadget_impl
529 \snippet qrangemodel/main.cpp color_gadget_end
530
531 When used in a table, this is the default representation for gadgets:
532
533 \snippet qrangemodel/main.cpp color_gadget_table
534
535 When used in a list, these types are however by default represented as
536 multi-column rows, with each property represented as a separate column. To
537 force a gadget to be represented as a multi-role item in a list, declare
538 the gadget as a multi-role type by specializing QRoleModel::RowOptions,
539 with a \c{static constexpr auto rowCategory} member variable set to
540 MultiRoleItem.
541
542 \snippet qrangemodel/main.cpp color_gadget_decl
543 \dots
544 \snippet qrangemodel/main.cpp color_gadget_end
545 \snippet qrangemodel/main.cpp color_gadget_multi_role_gadget
546
547 You can also wrap such types into a single-element tuple, turning the list
548 into a table with a single column:
549
550 \snippet qrangemodel/main.cpp color_gadget_single_column
551
552 In this case, note that direct access to the elements in the list data
553 needs to use \c{std::get}:
554
555 \snippet qrangemodel/main.cpp color_gadget_single_column_access_get
556
557 or alternatively a structured binding:
558
559 \snippet qrangemodel/main.cpp color_gadget_single_column_access_sb
560
561 \section2 Rows as values or pointers
562
563 In the examples so far, we have always used QRangeModel with ranges that
564 hold values. QRangeModel can also operate on ranges that hold pointers,
565 including smart pointers. This allows QRangeModel to operate on ranges of
566 polymorph types, such as QObject subclasses.
567
568 \snippet qrangemodel/main.cpp object_0
569 \dots
570 \snippet qrangemodel/main.cpp object_1
571
572 \snippet qrangemodel/main.cpp vector_of_objects_0
573 \dots
574 \snippet qrangemodel/main.cpp vector_of_objects_1
575 \snippet qrangemodel/main.cpp vector_of_objects_2
576
577 As with values, the type of the row defines whether the range is
578 represented as a list, table, or tree. Rows that are QObjects will present
579 each property as a column, unless the QRangeModel::RowOptions template is
580 specialized to declare the type as a multi-role item.
581
582 \snippet qrangemodel/main.cpp vector_of_multirole_objects_0
583 \snippet qrangemodel/main.cpp vector_of_multirole_objects_1
584 \dots
585 \snippet qrangemodel/main.cpp vector_of_multirole_objects_2
586
587 \note If the range holds raw pointers, then you have to construct
588 QRangeModel from a pointer or reference wrapper of the range. Otherwise the
589 ownership of the data becomes ambiguous, and a copy of the range would
590 still be operating on the same actual row data, resulting in unexpected
591 side effects.
592
593 \section2 Subclassing QRangeModel
594
595 Subclassing QRangeModel makes it possible to add convenient APIs that take
596 the data type and structure of the range into account.
597
598 \snippet qrangemodel/main.cpp subclass_header
599
600 When doing so, add the range as a private member, and call the QRangeModel
601 constructor with a reference wrapper or pointer to that member. This
602 properly encapsulates the data and avoids direct access.
603
604 \snippet qrangemodel/main.cpp subclass_API
605
606 Add member functions to provide type-safe access to the data, using the
607 QAbstractItemModel API to perform any operation that modifies the range.
608 Read-only access can directly operate on the data structure.
609
610 \section1 Trees of data
611
612 QRangeModel can represent a data structure as a tree model. Such a
613 tree data structure needs to be homomorphic: on all levels of the tree, the
614 list of child rows needs to use the exact same representation as the tree
615 itself. In addition, the row type needs be of a static size: either a gadget
616 or QObject type, or a type that implements \l{the C++ tuple protocol}.
617
618 To represent such data as a tree, QRangeModel has to be able to traverse the
619 data structure: for any given row, the model needs to be able to retrieve
620 the parent row, and the optional span of children. These traversal functions
621 can be provided implicitly through the row type, or through an explicit
622 protocol type.
623
624 \section2 Implicit tree traversal protocol
625
626 \snippet qrangemodel/main.cpp tree_protocol_0
627
628 The tree itself is a vector of \c{TreeRow} values. See \l{Tree Rows as
629 pointers or values} for the considerations on whether to use values or
630 pointers of items for the rows.
631
632 \snippet qrangemodel/main.cpp tree_protocol_1
633
634 The row class can be of any fixed-size type described above: a type that
635 implements the tuple protocol, a gadget, or a QObject. In this example, we
636 use a gadget.
637
638 Each row item needs to maintain a pointer to the parent row, as well as an
639 optional range of child rows. That range has to be identical to the range
640 structure used for the tree itself.
641
642 Making the row type default constructible is optional, and allows the model
643 to construct new row data elements, for instance in the insertRow() or
644 moveRows() implementations.
645
646 \snippet qrangemodel/main.cpp tree_protocol_2
647
648 The tree traversal protocol can then be implemented as member functions of
649 the row data type. A const \c{parentRow()} function has to return a pointer
650 to a row item; and the \c{childRows()} function has to return a reference
651 to a const \c{std::optional} that can hold the optional child range.
652
653 These two functions are sufficient for the model to navigate the tree as a
654 read-only data structure. To allow the user to edit data in a view, and the
655 model to implement mutating model APIs such as insertRows(), removeRows(),
656 and moveRows(), we have to implement additional functions for write-access:
657
658 \snippet qrangemodel/main.cpp tree_protocol_3
659
660 The model calls the \c{setParentRow()} function and mutable \c{childRows()}
661 overload to move or insert rows into an existing tree branch, and to update
662 the parent pointer should the old value have become invalid. The non-const
663 overload of \c{childRows()} provides in addition write-access to the row
664 data.
665
666 \note The model performs setting the parent of a row, removing that row
667 from the old parent, and adding it to the list of the new parent's children,
668 as separate steps. This keeps the protocol interface small.
669
670 \dots
671 \snippet qrangemodel/main.cpp tree_protocol_4
672
673 The rest of the class implementation is not relevant for the model, but
674 a \c{addChild()} helper provides us with a convenient way to construct the
675 initial state of the tree.
676
677 \snippet qrangemodel/main.cpp tree_protocol_5
678
679 A QRangeModel instantiated with an instance of such a range will
680 represent the data as a tree.
681
682 \snippet qrangemodel/main.cpp tree_protocol_6
683
684 \section2 Tree traversal protocol in a separate class
685
686 The tree traversal protocol can also be implemented in a separate class.
687
688 \snippet qrangemodel/main.cpp explicit_tree_protocol_0
689
690 Pass an instance of this protocol implementation to the QRangeModel
691 constructor:
692
693 \snippet qrangemodel/main.cpp explicit_tree_protocol_1
694
695 \section2 Tree Rows as pointers or values
696
697 The row type of the data range can be either a value, or a pointer. In
698 the code above we have been using the tree rows as values in a vector,
699 which avoids that we have to deal with explicit memory management. However,
700 a vector as a contiguous block of memory invalidates all iterators and
701 references when it has to reallocate the storage, or when inserting or
702 removing elements. This impacts the pointer to the parent item, which is
703 the location of the parent row within the vector. Making sure that this
704 parent (and QPersistentModelIndex instances referring to items within it)
705 stays valid can incurr substantial performance overhead. The
706 QRangeModel implementation has to assume that all references into the
707 range become invalid when modifying the range.
708
709 Alternatively, we can also use a range of row pointers as the tree type:
710
711 \snippet qrangemodel/main.cpp tree_of_pointers_0
712
713 In this case, we have to allocate all TreeRow instances explicitly using
714 operator \c{new}, and implement the destructor to \c{delete} all items in
715 the vector of children.
716
717 \snippet qrangemodel/main.cpp tree_of_pointers_1
718 \snippet qrangemodel/main.cpp tree_of_pointers_2
719
720 Before we can construct a model that represents this data as a tree, we need
721 to also implement the tree traversal protocol.
722
723 \snippet qrangemodel/main.cpp tree_of_pointers_3
724
725 An explicit protocol implementation for mutable trees of pointers has to
726 provide two additional member functions, \c{newRow()} and
727 \c{deleteRow(RowType *)}.
728
729 \snippet qrangemodel/main.cpp tree_of_pointers_4
730
731 The model will call those functions when creating new rows in insertRows(),
732 and when removing rows in removeRows(). In addition, if the model has
733 ownership of the data, then it will also delete all top-level rows upon
734 destruction. Note how in this example, we move the tree into the model, so
735 we must no longer perform any operations on it. QRangeModel, when
736 constructed by moving tree-data with row-pointers into it, will take
737 ownership of the data, and delete the row pointers in it's destructor.
738
739 Using pointers as rows comes with some memory allocation and management
740 overhead. However, the references to the row items remain stable, even when
741 they are moved around in the range, or when the range reallocates. This can
742 significantly reduce the cost of making modifications to the model's
743 structure when using insertRows(), removeRows(), or moveRows().
744
745 Each choice has different performance and memory overhead trade-offs. The
746 best option depends on the exact use case and data structure used.
747
748 \section2 The C++ tuple protocol
749
750 As seen in the \c{numberNames} example above, the row type can be a tuple,
751 and in fact any type that implements the tuple protocol. This protocol is
752 implemented by specializing \c{std::tuple_size} and \c{std::tuple_element},
753 and overloading the unqualified \c{get} function. Do so for your custom row
754 type to make existing structured data available to the model/view framework
755 in Qt.
756
757 \snippet qrangemodel/main.cpp tuple_protocol
758
759 In the above implementation, the \c{title} and \c{author} values of the
760 \c{Book} type are returned as \c{const}, so the model flags items in those
761 two columns as read-only. The user won't be able to trigger editing, and
762 setData() does nothing and returns false. For \c{summary} and \c{rating}
763 the implementation returns the same value category as the book, so when
764 \c{get} is called with a mutable reference to a \c{Book}, then it will
765 return a mutable reference of the respective variable. The model makes
766 those columns editable, both for the user and for programmatic access.
767
768 \note The implementation of \c{get} above requires C++23.
769
770 \section2 Binary compatibility considerations
771
772 QRangeModel is not a template class. Passing QRangeModel instances (by
773 pointer or reference, as with all QObject classes) through library APIs, or
774 storing QRangeModel by value in a public class of a library, is safe.
775
776 However, the QRangeModel constructor is a template and inline, and the
777 internal implementation that is specialized on the type of the range the
778 model operates on is instantiated in the constructor. You should not call
779 the constructor in an inline-implementation of a library API. It results in
780 ODR violations, and might break binary compatibility of that library if the
781 Qt version it gets built against is different from the Qt version an
782 application using that library is built against.
783
784 New and optimized implementations of virtual functions introduced in later
785 version of Qt might also not be used if QRangeModel detects that the
786 implementation was compiled against an older version of Qt. For instance,
787 the implementations of sort() and match() are new in Qt 6.12, but will not
788 be called by an application that was compiled against Qt 6.11, even if the
789 Qt library used is Qt 6.12. To benefit from such new overrides, recompile
790 your application.
791
792 \sa {Model/View Programming}
793*/
794
795/*!
796 \class QRangeModel::RowOptions
797 \inmodule QtCore
798 \ingroup model-view
799 \brief The RowOptions template provides a customization point to control
800 how QRangeModel represents types used as rows.
801 \since 6.10
802
803 RowOptions<T> is a struct template where \a T specifies the row type.
804 Specialize this template for the type used in your range, and add the
805 relevant members.
806
807 \table
808 \header
809 \li Member
810 \li Documentation
811 \row
812 \li static constexpr RowCategory rowCategory
813 \li RowCategory
814 \row
815 \li [since 6.12] static QVariant headerData(int section, int role)
816 \li \l{QAbstractItemModel::headerData}{Header data} with \a role for
817 the \c section of the horizontal header.
818 \row
819 \li [since 6.12] static Qt::ItemFlags flags(const T &row)
820 \li \l{QAbstractItemModel::flags}(Flags) for all items in \a row.
821 Will be overwritten by a customization of \l{ItemAccess}{ItemAccess::flags}
822 \endtable
823
824 \snippet qrangemodel/main.cpp color_gadget_decl
825 \dots
826 \snippet qrangemodel/main.cpp color_gadget_end
827 \snippet qrangemodel/main.cpp color_gadget_multi_role_gadget
828
829*/
830
831/*!
832 \enum QRangeModel::RowCategory
833
834 This enum describes how QRangeModel should present the elements of the
835 range it was constructed with.
836
837 \value Default
838 QRangeModel decides how to present the rows.
839 \value MultiRoleItem
840 QRangeModel will present items with a meta object as multi-role
841 items, also when used in a one-dimensional range.
842
843 Specialize the RowOptions template for your type, and add a public member
844 variable \c{static constexpr auto rowCategory} with one of the values from
845 this enum.
846
847 \sa RowOptions
848*/
849
850/*!
851 \class QRangeModel::ItemAccess
852 \inmodule QtCore
853 \ingroup model-view
854 \brief The ItemAccess template provides a customization point to control
855 how QRangeModel accesses role data of individual items.
856 \since 6.11
857
858 ItemAccess<T> is a struct template where \a T specifies the item type.
859 Specialize this template for the type used in your data structure, and
860 implement \c{readRole()} and (optionally since 6.12) \c{writeRole()} members
861 to access the role-specific data of your type.
862
863 \code
864 template <>
865 struct QRangeModel::ItemAccess<ItemType>
866 {
867 static QVariant readRole(const ItemType &item, int role)
868 {
869 switch (role) {
870 // ...
871 }
872 return {};
873 }
874
875 static bool writeRole(ItemType &item, const QVariant &data, int role)
876 {
877 bool ok = false;
878 switch (role) {
879 // ...
880 }
881
882 return ok;
883 }
884 };
885 \endcode
886
887 Optionally [since 6.12], a \c{flags()} implementation can return a combination of
888 Qt::ItemFlags:
889
890 \code
891 static Qt::ItemFlags flags(const ItemType &item)
892 {
893 return item.flags();
894 }
895 \endcode
896
897 A specialization of this type will take precedence over any predefined
898 behavior. Do not specialize this template for types you do not own. Types
899 for which ItemAccess is specialized with a \c{readRole} implementation are
900 implicitly interpreted as \l{RowCategory}{multi-role items}.
901*/
902
903/*!
904 \fn template <typename Range, QRangeModelDetails::if_table_range<Range>> QRangeModel::QRangeModel(Range &&range, QObject *parent)
905 \fn template <typename Range, QRangeModelDetails::if_tree_range<Range>> QRangeModel::QRangeModel(Range &&range, QObject *parent)
906 \fn template <typename Range, typename Protocol, QRangeModelDetails::if_tree_range<Range, Protocol>> QRangeModel::QRangeModel(Range &&range, Protocol &&protocol, QObject *parent)
907
908 Constructs a QRangeModel instance that operates on the data in \a range.
909 The \a range has to be a sequential range for which the compiler finds
910 \c{begin} and \c{end} overloads through
911 \l{https://en.cppreference.com/w/cpp/language/adl.html}{argument dependent
912 lookup}, or for which \c{std::begin} and \c{std::end} are implemented. If
913 \a protocol is provided, then the model will represent the range as a tree
914 using the protocol implementation. The model instance becomes a child of \a
915 parent.
916
917 The \a range can be a pointer or reference wrapper, in which case mutating
918 model APIs (such as \l{setData()} or \l{insertRow()}) will modify the data
919 in the referenced range instance. If \a range is a value (or moved into the
920 model), then connect to the signals emitted by the model to respond to
921 changes to the data.
922
923 QRangeModel will not access the \a range while being constructed. This
924 makes it legal to pass a pointer or reference to a range object that is not
925 fully constructed yet to this constructor, for example when \l{Subclassing
926 QRangeModel}{subclassing QRangeModel}.
927
928 If the \a range was moved into the model, then the range and all data in it
929 will be destroyed upon destruction of the model.
930
931 \note While the model does not take ownership of the range object otherwise,
932 you must not modify the \a range directly once the model has been constructed
933 and and passed on to a view. Such modifications will not emit signals
934 necessary to keep model users (other models or views) synchronized with the
935 model, resulting in inconsistent results, undefined behavior, and crashes.
936 Use QRangeModelAdapter to safely interact with the underlying range while
937 keeping the model updated.
938
939 \sa QRangeModelAdapter
940*/
941
942/*!
943 Destroys the QRangeModel.
944
945 The range that the model was constructed from is not accessed, and only
946 destroyed if the model was constructed from a moved-in range.
947*/
948QRangeModel::~QRangeModel() = default;
949
950/*!
951 \reimp
952
953 Returns the index of the model item at \a row and \a column in \a parent.
954
955 Passing a valid parent produces an invalid index for models that operate on
956 list and table ranges.
957
958 \sa parent()
959*/
960QModelIndex QRangeModel::index(int row, int column, const QModelIndex &parent) const
961{
962 Q_D(const QRangeModel);
963 return d->impl->call<QRangeModelImplBase::Index>(row, column, parent);
964}
965
966/*!
967 \reimp
968
969 Returns the parent of the item at the \a child index.
970
971 This function always produces an invalid index for models that operate on
972 list and table ranges. For models operation on a tree, this function
973 returns the index for the row item returned by the parent() implementation
974 of the tree traversal protocol.
975
976 \sa index(), hasChildren()
977*/
978QModelIndex QRangeModel::parent(const QModelIndex &child) const
979{
980 Q_D(const QRangeModel);
981 return d->impl->call<QRangeModelImplBase::Parent>(child);
982}
983
984/*!
985 \reimp
986
987 Returns the sibling at \a row and \a column for the item at \a index, or an
988 invalid QModelIndex if there is no sibling at that location.
989
990 This implementation is significantly faster than going through the parent()
991 of the \a index.
992
993 \sa index(), QModelIndex::row(), QModelIndex::column()
994*/
995QModelIndex QRangeModel::sibling(int row, int column, const QModelIndex &index) const
996{
997 Q_D(const QRangeModel);
998 return d->impl->call<QRangeModelImplBase::Sibling>(row, column, index);
999}
1000
1001/*!
1002 \reimp
1003
1004 Returns the number of rows under the given \a parent. This is the number of
1005 items in the root range for an invalid \a parent index.
1006
1007 If the \a parent index is valid, then this function always returns 0 for
1008 models that operate on list and table ranges. For trees, this returns the
1009 size of the range returned by the childRows() implementation of the tree
1010 traversal protocol.
1011
1012 \sa columnCount(), insertRows(), hasChildren()
1013*/
1014int QRangeModel::rowCount(const QModelIndex &parent) const
1015{
1016 Q_D(const QRangeModel);
1017 return d->impl->call<QRangeModelImplBase::RowCount>(parent);
1018}
1019
1020/*!
1021 \reimp
1022
1023 Returns the number of columns of the model. This function returns the same
1024 value for all \a parent indexes.
1025
1026 For models operating on a statically sized row type, this returned value is
1027 always the same throughout the lifetime of the model. For models operating
1028 on dynamically sized row type, the model returns the number of items in the
1029 first row, or 0 if the model has no rows.
1030
1031 \sa rowCount, insertColumns()
1032*/
1033int QRangeModel::columnCount(const QModelIndex &parent) const
1034{
1035 Q_D(const QRangeModel);
1036 return d->impl->call<QRangeModelImplBase::ColumnCount>(parent);
1037}
1038
1039/*!
1040 \reimp
1041
1042 Returns the item flags for the given \a index.
1043
1044 The implementation returns a combination of flags that enables the item
1045 (\c ItemIsEnabled) and allows it to be selected (\c ItemIsSelectable). For
1046 models operating on a range with mutable data, it also sets the flag
1047 that allows the item to be editable (\c ItemIsEditable).
1048
1049 To customize the flags for your own data types, provide a specialization
1050 of RowOptions and/or ItemAccess for your row or item types.
1051
1052 \sa Qt::ItemFlags, RowOptions, ItemAccess
1053*/
1054Qt::ItemFlags QRangeModel::flags(const QModelIndex &index) const
1055{
1056 Q_D(const QRangeModel);
1057 return d->impl->call<QRangeModelImplBase::Flags>(index);
1058}
1059
1060/*!
1061 \reimp
1062
1063 Returns the data for the given \a role and \a section in the header with
1064 the specified \a orientation.
1065
1066 For horizontal headers, the section number corresponds to the column
1067 number. Similarly, for vertical headers, the section number corresponds to
1068 the row number.
1069
1070 For the horizontal header and the Qt::DisplayRole \a role, models that
1071 operate on a range that uses an array as the row type return \a section. If
1072 the row type is a tuple, then the implementation returns the name of the
1073 type at \a section. For rows that are a gadget or QObject type, this
1074 function returns the name of the property at the index of \a section.
1075
1076 For the vertical header, this function always returns the result of the
1077 default implementation in QAbstractItemModel.
1078
1079 \sa Qt::ItemDataRole, setHeaderData(), QHeaderView
1080*/
1081QVariant QRangeModel::headerData(int section, Qt::Orientation orientation, int role) const
1082{
1083 Q_D(const QRangeModel);
1084 return d->impl->call<QRangeModelImplBase::HeaderData>(section, orientation, role);
1085}
1086
1087/*!
1088 \reimp
1089*/
1090bool QRangeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &data,
1091 int role)
1092{
1093 return QAbstractItemModel::setHeaderData(section, orientation, data, role);
1094}
1095
1096/*!
1097 \reimp
1098
1099 Returns the data stored under the given \a role for the value in the
1100 range referred to by the \a index.
1101
1102 If the item type for that index is an associative container that maps from
1103 either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then the role
1104 data is looked up in that container and returned.
1105
1106 If the item is a gadget or QObject, then the implementation returns the
1107 value of the item's property matching the \a role entry in the roleNames()
1108 mapping.
1109
1110 Otherwise, the implementation returns a QVariant constructed from the item
1111 via \c{QVariant::fromValue()} for \c{Qt::DisplayRole} or \c{Qt::EditRole}.
1112 For other roles, the implementation returns an \b invalid
1113 (default-constructed) QVariant.
1114
1115 \sa Qt::ItemDataRole, setData(), headerData()
1116*/
1117QVariant QRangeModel::data(const QModelIndex &index, int role) const
1118{
1119 Q_D(const QRangeModel);
1120 return d->impl->call<QRangeModelImplBase::Data>(index, role);
1121}
1122
1123/*!
1124 \reimp
1125
1126 Sets the \a role data for the item at \a index to \a data.
1127
1128 If the item type for that \a index is an associative container that maps
1129 from either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then
1130 \a data is stored in that container for the key specified by \a role.
1131
1132 If the item is a gadget or QObject, then \a data is written to the item's
1133 property matching the \a role entry in the the roleNames() mapping. The
1134 function returns \c{true} if a property was found and if \a data stored a
1135 value that could be converted to the required type, otherwise returns
1136 \c{false}.
1137
1138 Otherwise, this implementation assigns the value in \a data to the item at
1139 the \a index in the range for \c{Qt::DisplayRole} and \c{Qt::EditRole},
1140 and returns \c{true}. For other roles, the implementation returns
1141 \c{false}.
1142
1143//! [read-only-setData]
1144 For models operating on a read-only range, or on a read-only column in
1145 a row type that implements \l{the C++ tuple protocol}, this implementation
1146 returns \c{false} immediately.
1147//! [read-only-setData]
1148*/
1149bool QRangeModel::setData(const QModelIndex &index, const QVariant &data, int role)
1150{
1151 Q_D(QRangeModel);
1152 return d->impl->call<QRangeModelImplBase::SetData>(index, data, role);
1153}
1154
1155/*!
1156 \reimp
1157
1158 Returns a map with values for all predefined roles in the model for the
1159 item at the given \a index.
1160
1161 If the item type for that \a index is an associative container that maps
1162 from either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then the
1163 data from that container is returned.
1164
1165 If the item type is a gadget or QObject subclass, then the values of those
1166 properties that match a \l{roleNames()}{role name} are returned.
1167
1168 If the item is not an associative container, gadget, or QObject subclass,
1169 then this calls the base class implementation.
1170
1171 \sa setItemData(), Qt::ItemDataRole, data()
1172*/
1173QMap<int, QVariant> QRangeModel::itemData(const QModelIndex &index) const
1174{
1175 Q_D(const QRangeModel);
1176 return d->impl->call<QRangeModelImplBase::ItemData>(index);
1177}
1178
1179/*!
1180 \reimp
1181
1182 If the item type for that \a index is an associative container that maps
1183 from either \c{int} or Qt::ItemDataRole to a QVariant, then the entries in
1184 \a data are stored in that container. If the associative container maps from
1185 QString to QVariant, then only those values in \a data are stored for which
1186 there is a mapping in the \l{roleNames()}{role names} table.
1187
1188 If the item type is a gadget or QObject subclass, then those properties that
1189 match a \l{roleNames()}{role name} are set to the corresponding value in
1190 \a data.
1191
1192 Roles for which there is no entry in \a data are not modified.
1193
1194 For item types that can be copied, this implementation is transactional,
1195 and returns true if all the entries from \a data could be stored. If any
1196 entry could not be updated, then the original container is not modified at
1197 all, and the function returns false.
1198
1199 If the item is not an associative container, gadget, or QObject subclass,
1200 then this calls the base class implementation, which calls setData() for
1201 each entry in \a data.
1202
1203 \sa itemData(), setData(), Qt::ItemDataRole
1204*/
1205bool QRangeModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1206{
1207 Q_D(QRangeModel);
1208 return d->impl->call<QRangeModelImplBase::SetItemData>(index, data);
1209}
1210
1211/*!
1212 \reimp
1213
1214 Replaces the value stored in the range at \a index with a default-
1215 constructed value.
1216
1217 \include qrangemodel.cpp read-only-setData
1218*/
1219bool QRangeModel::clearItemData(const QModelIndex &index)
1220{
1221 Q_D(QRangeModel);
1222 return d->impl->call<QRangeModelImplBase::ClearItemData>(index);
1223}
1224
1225/*
1226//! [column-change-requirement]
1227 \note A dynamically sized row type needs to provide a \c{\1} member function.
1228
1229 For models operating on a read-only range, or on a range with a
1230 statically sized row type (such as a tuple, array, or struct), this
1231 implementation does nothing and returns \c{false} immediately. This is
1232 always the case for tree models.
1233//! [column-change-requirement]
1234*/
1235
1236/*!
1237 \reimp
1238
1239 Inserts \a count empty columns before the item at \a column in all rows
1240 of the range at \a parent. Returns \c{true} if successful; otherwise
1241 returns \c{false}.
1242
1243 \include qrangemodel.cpp {column-change-requirement} {insert(const_iterator, size_t, value_type)}
1244*/
1245bool QRangeModel::insertColumns(int column, int count, const QModelIndex &parent)
1246{
1247 Q_D(QRangeModel);
1248 return d->impl->call<QRangeModelImplBase::InsertColumns>(column, count, parent);
1249}
1250
1251/*!
1252 \reimp
1253
1254 Removes \a count columns from the item at \a column on in all rows of the
1255 range at \a parent. Returns \c{true} if successful, otherwise returns
1256 \c{false}.
1257
1258 \include qrangemodel.cpp {column-change-requirement} {erase(const_iterator, size_t)}
1259*/
1260bool QRangeModel::removeColumns(int column, int count, const QModelIndex &parent)
1261{
1262 Q_D(QRangeModel);
1263 return d->impl->call<QRangeModelImplBase::RemoveColumns>(column, count, parent);
1264}
1265
1266/*!
1267 \reimp
1268
1269 Moves \a count columns starting with the given \a sourceColumn under parent
1270 \a sourceParent to column \a destinationColumn under parent \a destinationParent.
1271
1272 Returns \c{true} if the columns were successfully moved; otherwise returns
1273 \c{false}.
1274*/
1275bool QRangeModel::moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1276 const QModelIndex &destinationParent, int destinationColumn)
1277{
1278 Q_D(QRangeModel);
1279 return d->impl->call<QRangeModelImplBase::MoveColumns>(
1280 sourceParent, sourceColumn, count,
1281 destinationParent, destinationColumn);
1282}
1283
1284/*
1285//! [row-change-requirement]
1286 \note The range needs to be dynamically sized and provide a \c{\1}
1287 member function.
1288
1289 For models operating on a read-only or statically-sized range (such as
1290 an array), this implementation does nothing and returns \c{false}
1291 immediately.
1292//! [row-change-requirement]
1293*/
1294
1295/*!
1296 \reimp
1297
1298 Inserts \a count empty rows before the given \a row into the range at
1299 \a parent. Returns \c{true} if successful; otherwise returns \c{false}.
1300
1301 \include qrangemodel.cpp {row-change-requirement} {insert(const_iterator, size_t, value_type)}
1302
1303 \note For ranges with a dynamically sized column type, the column needs
1304 to provide a \c{resize(size_t)} member function.
1305*/
1306bool QRangeModel::insertRows(int row, int count, const QModelIndex &parent)
1307{
1308 Q_D(QRangeModel);
1309 return d->impl->call<QRangeModelImplBase::InsertRows>(row, count, parent);
1310}
1311
1312/*!
1313 \reimp
1314
1315 Removes \a count rows from the range at \a parent, starting with the
1316 given \a row. Returns \c{true} if successful, otherwise returns \c{false}.
1317
1318 \include qrangemodel.cpp {row-change-requirement} {erase(const_iterator, size_t)}
1319*/
1320bool QRangeModel::removeRows(int row, int count, const QModelIndex &parent)
1321{
1322 Q_D(QRangeModel);
1323 return d->impl->call<QRangeModelImplBase::RemoveRows>(row, count, parent);
1324}
1325
1326/*!
1327 \reimp
1328
1329 Moves \a count rows starting with the given \a sourceRow under parent
1330 \a sourceParent to row \a destinationRow under parent \a destinationParent.
1331
1332 Returns \c{true} if the rows were successfully moved; otherwise returns
1333 \c{false}.
1334*/
1335bool QRangeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
1336 const QModelIndex &destinationParent, int destinationRow)
1337{
1338 Q_D(QRangeModel);
1339 return d->impl->call<QRangeModelImplBase::MoveRows>(
1340 sourceParent, sourceRow, count,
1341 destinationParent, destinationRow);
1342}
1343
1344/*!
1345 \reimp
1346*/
1347bool QRangeModel::canFetchMore(const QModelIndex &parent) const
1348{
1349 return QAbstractItemModel::canFetchMore(parent);
1350}
1351
1352/*!
1353 \reimp
1354*/
1355void QRangeModel::fetchMore(const QModelIndex &parent)
1356{
1357 QAbstractItemModel::fetchMore(parent);
1358}
1359
1360/*!
1361 \reimp
1362*/
1363bool QRangeModel::hasChildren(const QModelIndex &parent) const
1364{
1365 return QAbstractItemModel::hasChildren(parent);
1366}
1367
1368/*!
1369 \reimp
1370*/
1371QModelIndex QRangeModel::buddy(const QModelIndex &index) const
1372{
1373 return QAbstractItemModel::buddy(index);
1374}
1375
1376/*!
1377 \reimp
1378*/
1379bool QRangeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
1380 int row, int column, const QModelIndex &parent) const
1381{
1382 return QAbstractItemModel::canDropMimeData(data, action, row, column, parent);
1383}
1384
1385/*!
1386 \reimp
1387*/
1388bool QRangeModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
1389 int row, int column, const QModelIndex &parent)
1390{
1391 return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
1392}
1393
1394/*!
1395 \reimp
1396*/
1397QMimeData *QRangeModel::mimeData(const QModelIndexList &indexes) const
1398{
1399 return QAbstractItemModel::mimeData(indexes);
1400}
1401
1402/*!
1403 \reimp
1404*/
1405QStringList QRangeModel::mimeTypes() const
1406{
1407 return QAbstractItemModel::mimeTypes();
1408}
1409
1410/*!
1411 \reimp
1412
1413 Returns a list of indexes for the items in the column of \a start where
1414 the data stored under \a role matches \a value, using the match criteria
1415 defined by \a flags. Use \a hits = -1 to find all matching items.
1416
1417 \note This implementation reads data directly from the underlying C++
1418 range and does not dispatch through overrides of data().
1419*/
1420QModelIndexList QRangeModel::match(const QModelIndex &start, int role, const QVariant &value,
1421 int hits, Qt::MatchFlags flags) const
1422{
1423 Q_D(const QRangeModel);
1424 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0))
1425 return QAbstractItemModel::match(start, role, value, hits, flags);
1426 return d->impl->call<QRangeModelImplBase::Match>(start, role, value, hits, flags);
1427}
1428
1429/*!
1430 \reimp
1431*/
1432void QRangeModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1433{
1434 Q_D(const QRangeModel);
1435 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 11, 0))
1436 return QAbstractItemModel::multiData(index, roleDataSpan);
1437 d->impl->call<QRangeModelImplBase::MultiData>(index, roleDataSpan);
1438}
1439
1440
1441/*!
1442 \property QRangeModel::roleNames
1443 \brief the role names for the model.
1444
1445 If all columns in the range are of the same type, and if that type provides
1446 a meta object (i.e., it is a gadget, or a QObject subclass), then this
1447 property holds the names of the properties of that type, mapped to values of
1448 Qt::ItemDataRole values from Qt::UserRole and up. In addition, a role
1449 "modelData" provides access to the gadget or QObject instance.
1450
1451 Override this default behavior by setting this property explicitly to a non-
1452 empty mapping. Setting this property to an empty mapping, or using
1453 resetRoleNames(), restores the default behavior.
1454
1455 \sa QAbstractItemModel::roleNames()
1456*/
1457
1458QHash<int, QByteArray> QRangeModelImplBase::roleNamesForMetaObject(const QAbstractItemModel &model,
1459 const QMetaObject &metaObject)
1460{
1461 const auto defaults = model.QAbstractItemModel::roleNames();
1462 QHash<int, QByteArray> result = {{Qt::RangeModelDataRole, "modelData"}};
1463 int offset = metaObject.propertyOffset();
1464 for (int i = offset; i < metaObject.propertyCount(); ++i) {
1465 const auto name = metaObject.property(i).name();
1466 const int defaultRole = defaults.key(name, -1);
1467 if (defaultRole != -1) {
1468 ++offset;
1469 result[defaultRole] = name;
1470 } else {
1471 result[Qt::UserRole + i - offset] = name;
1472 }
1473 }
1474 return result;
1475}
1476
1477QHash<int, QByteArray> QRangeModelImplBase::roleNamesForSimpleType()
1478{
1479 // just a plain value
1480 return QHash<int, QByteArray>{
1481 {Qt::DisplayRole, "display"},
1482 {Qt::EditRole, "edit"},
1483 {Qt::RangeModelDataRole, "modelData"},
1484 };
1485}
1486
1487/*!
1488 \reimp
1489
1490 \note Overriding this function in a QRangeModel subclass is possible,
1491 but might break the behavior of the property.
1492*/
1493QHash<int, QByteArray> QRangeModel::roleNames() const
1494{
1495 Q_D(const QRangeModel);
1496 if (d->m_roleNames.isEmpty())
1497 d->m_roleNames = d->impl->call<QRangeModelImplBase::RoleNames>();
1498
1499 return d->m_roleNames;
1500}
1501
1502void QRangeModel::setRoleNames(const QHash<int, QByteArray> &names)
1503{
1504 Q_D(QRangeModel);
1505 if (d->m_roleNames == names)
1506 return;
1507 beginResetModel();
1508 d->impl->call<QRangeModelImplBase::InvalidateCaches>();
1509 if (d->m_autoConnectPolicy != AutoConnectPolicy::None)
1510 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
1511
1512 d->m_roleNames = names;
1513 endResetModel();
1514 Q_EMIT roleNamesChanged();
1515}
1516
1517void QRangeModel::resetRoleNames()
1518{
1519 setRoleNames({});
1520}
1521
1522/*!
1523 \enum QRangeModel::AutoConnectPolicy
1524 \since 6.11
1525
1526 This enum defines if and when QRangeModel auto-connects changed-signals for
1527 properties to the \l{QAbstractItemModel::}{dataChanged()} signal of the
1528 model. Only properties that match one of the \l{roleNames()}{role names}
1529 are connected.
1530
1531 \value None No connections are made automatically.
1532 \value Full The signals for all relevant properties are connected
1533 automatically, for all QObject items. This includes QObject
1534 items that are added to newly inserted rows and columns.
1535 \value OnRead Signals for relevant properties are connected the first time
1536 the model reads the property.
1537
1538 The memory overhead of making automatic connections can be substantial. A
1539 Full auto-connection does not require any book-keeping in addition to the
1540 connection itself, but each connection takes memory, and connecting all
1541 properties of all objects can be very costly, especially if only a few
1542 properties of a subset of objects will ever change.
1543
1544 The OnRead connection policy will not connect to objects or properties that
1545 are never read from (for instance, never rendered in a view), but remembering
1546 which connections have been made requires some book-keeping overhead, and
1547 unpredictable memory growth over time. For instance, scrolling down a long
1548 list of items can easily result in thousands of new connections being made.
1549
1550 \sa autoConnectPolicy, roleNames()
1551*/
1552
1553/*!
1554 \property QRangeModel::autoConnectPolicy
1555 \since 6.11
1556 \brief if and when the model auto-connects to property changed notifications.
1557
1558 If QRangeModel operates on a data structure that holds the same type of
1559 QObject subclass as its row or item type, then it can automatically connect
1560 the properties of the QObjects to the dataChanged() signal. For QObject
1561 rows, this is done for each column, mapping to the Qt::DisplayRole
1562 property. For items, this is done for those properties that match one of
1563 the \l{roleNames()}{role names}.
1564
1565 By default, the value of this property is \l{QRangeModel::AutoConnectPolicy::}
1566 {None}, so no such connections are made. Changing the value of this property
1567 always breaks all existing connections.
1568
1569 \note Connections are not broken or created if QObjects in the data
1570 structure that QRangeModel operates on are swapped out.
1571
1572 \sa roleNames()
1573*/
1574
1575QRangeModel::AutoConnectPolicy QRangeModel::autoConnectPolicy() const
1576{
1577 Q_D(const QRangeModel);
1578 return d->m_autoConnectPolicy;
1579}
1580
1581void QRangeModel::setAutoConnectPolicy(QRangeModel::AutoConnectPolicy policy)
1582{
1583 Q_D(QRangeModel);
1584 if (d->m_autoConnectPolicy == policy)
1585 return;
1586
1587 d->m_autoConnectPolicy = policy;
1588 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
1589 Q_EMIT autoConnectPolicyChanged(policy);
1590}
1591
1592/*!
1593 \reimp
1594
1595 Sorts the the underlying range in the given \a order, based on the data for
1596 the \l{sortRole} (Qt::DisplayRole by default) of the items in \a column.
1597
1598 \note This implementation uses a member function \c{sort(Compare comp)} of
1599 the C++ range if available (such as in \c{std::list}), or otherwise
1600 \c{std::stable_sort()} if the range provides random-access iterators. If
1601 neither is available then the implementation does nothing and returns
1602 immediately.
1603
1604 \note Accessing the item does not dispatch the reading of data through
1605 overrides of data().
1606
1607 \sa sortRole, QSortFilterProxyModel
1608*/
1609void QRangeModel::sort(int column, Qt::SortOrder order)
1610{
1611 Q_D(QRangeModel);
1612 if (d->m_interfaceVersion < QT_VERSION_CHECK(6, 12, 0))
1613 return QAbstractItemModel::sort(column, order);
1614 QT_TRY {
1615 d->impl->call<QRangeModelImplBase::Sort>(column, order);
1616 } QT_CATCH(const std::bad_alloc &) {
1617 qCritical("QRangeModel::sort ran out of memory, sort likely incomplete.");
1618 }
1619}
1620
1621/*!
1622 \property QRangeModel::sortRole
1623 \since 6.12
1624 \brief the data role used when sorting items.
1625
1626 The default value is Qt::DisplayRole.
1627
1628 \sa sort(), sortCollator, QSortFilterProxyModel
1629*/
1630int QRangeModel::sortRole() const
1631{
1632 Q_D(const QRangeModel);
1633 return d->m_sortRole;
1634}
1635
1636void QRangeModel::setSortRole(int role)
1637{
1638 Q_D(QRangeModel);
1639 if (d->m_sortRole == role)
1640 return;
1641 d->m_sortRole = role;
1642 Q_EMIT sortRoleChanged(d->m_sortRole);
1643}
1644
1645void QRangeModel::resetSortRole()
1646{
1647 setSortRole(Qt::DisplayRole);
1648}
1649
1650/*!
1651 \property QRangeModel::sortCollator
1652 \since 6.12
1653 \brief the collator that will be used when sorting the model
1654
1655 The default value of this property is a QCollator for the C-locale.
1656 Sorting will not be locale aware, and case sensitive. Setting a collator
1657 will make the sorting locale-aware.
1658
1659 \sa sort(), sortRole, QSortFilterProxyModel
1660*/
1661QCollator QRangeModel::sortCollator() const
1662{
1663 Q_D(const QRangeModel);
1664 return d->m_sortCollator.value_or(QCollator(QLocale::C));
1665}
1666
1667void QRangeModel::setSortCollator(const QCollator &collator)
1668{
1669 Q_D(QRangeModel);
1670 if (sortCollator() == collator)
1671 return;
1672 d->m_sortCollator = collator;
1673 Q_EMIT sortCollatorChanged(*d->m_sortCollator);
1674}
1675
1676void QRangeModel::resetSortCollator()
1677{
1678 Q_D(QRangeModel);
1679 if (!d->m_sortCollator)
1680 return;
1681 d->m_sortCollator = std::nullopt;
1682 Q_EMIT sortCollatorChanged(sortCollator());
1683}
1684
1685/*!
1686 \reimp
1687*/
1688QSize QRangeModel::span(const QModelIndex &index) const
1689{
1690 return QAbstractItemModel::span(index);
1691}
1692
1693/*!
1694 \reimp
1695*/
1696Qt::DropActions QRangeModel::supportedDragActions() const
1697{
1698 return QAbstractItemModel::supportedDragActions();
1699}
1700
1701/*!
1702 \reimp
1703*/
1704Qt::DropActions QRangeModel::supportedDropActions() const
1705{
1706 return QAbstractItemModel::supportedDropActions();
1707}
1708
1709/*!
1710 \reimp
1711*/
1712void QRangeModel::resetInternalData()
1713{
1714 QAbstractItemModel::resetInternalData();
1715}
1716
1717/*!
1718 \reimp
1719*/
1720bool QRangeModel::event(QEvent *event)
1721{
1722 return QAbstractItemModel::event(event);
1723}
1724
1725/*!
1726 \reimp
1727*/
1728bool QRangeModel::eventFilter(QObject *object, QEvent *event)
1729{
1730 return QAbstractItemModel::eventFilter(object, event);
1731}
1732
1733QT_END_NAMESPACE
1734
1735#include "moc_qrangemodel.cpp"
static bool connectPropertiesHelper(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, const QHash< int, QMetaProperty > &properties)
~ConstPropertyChangedHandler()=default
ConstPropertyChangedHandler(ConstPropertyChangedHandler &&other) noexcept=default
ConstPropertyChangedHandler(const QModelIndex &index, int role)
PropertyChangedHandler & operator=(PropertyChangedHandler &&)=delete
PropertyChangedHandler(PropertyChangedHandler &&other) noexcept
PropertyChangedHandler(const PropertyChangedHandler &)=delete
PropertyChangedHandler & operator=(const PropertyChangedHandler &)=delete
~PropertyChangedHandler()=default
PropertyChangedHandler(const QPersistentModelIndex &index, int role)
PropertyChangedHandler & operator=(QMetaObject::Connection &&connection)