1// Copyright (C) 2016 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
4#include "qqmlobjectmodel_p.h"
5
6#include <QtCore/qcoreapplication.h>
7#include <QtQml/qqmlcontext.h>
8#include <QtQml/qqmlengine.h>
9#include <QtQml/qqmlinfo.h>
10
11#include <private/qqmlchangeset_p.h>
12#include <private/qqmlglobal_p.h>
13#include <private/qobject_p.h>
14#include <private/qpodvector_p.h>
15
16#include <QtCore/qhash.h>
17#include <QtCore/qlist.h>
18
19QT_BEGIN_NAMESPACE
20
21QHash<QObject*, QQmlObjectModelAttached*> QQmlObjectModelAttached::attachedProperties;
22
23
24class QQmlObjectModelPrivate : public QObjectPrivate
25{
26 Q_DECLARE_PUBLIC(QQmlObjectModel)
27public:
28 class Item {
29 public:
30 Item(QObject *i) : item(i), ref(0) {}
31
32 void addRef() { ++ref; }
33 bool deref() { return --ref == 0; }
34
35 QObject *item;
36 int ref;
37 };
38
39 QQmlObjectModelPrivate() : QObjectPrivate(), moveId(0) {}
40
41 static void children_append(QQmlListProperty<QObject> *prop, QObject *item) {
42 qsizetype index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.size();
43 static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item);
44 }
45
46 static qsizetype children_count(QQmlListProperty<QObject> *prop) {
47 return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.size();
48 }
49
50 static QObject *children_at(QQmlListProperty<QObject> *prop, qsizetype index) {
51 return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.at(i: index).item;
52 }
53
54 static void children_clear(QQmlListProperty<QObject> *prop) {
55 static_cast<QQmlObjectModelPrivate *>(prop->data)->clear();
56 }
57
58 static void children_replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *item) {
59 static_cast<QQmlObjectModelPrivate *>(prop->data)->replace(index, item);
60 }
61
62 static void children_removeLast(QQmlListProperty<QObject> *prop) {
63 auto data = static_cast<QQmlObjectModelPrivate *>(prop->data);
64 data->remove(index: data->children.size() - 1, n: 1);
65 }
66
67 void insert(int index, QObject *item) {
68 Q_Q(QQmlObjectModel);
69 children.insert(i: index, t: Item(item));
70 for (int i = index; i < children.size(); ++i) {
71 QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(obj: children.at(i).item);
72 attached->setIndex(i);
73 }
74 QQmlChangeSet changeSet;
75 changeSet.insert(index, count: 1);
76 emit q->modelUpdated(changeSet, reset: false);
77 emit q->countChanged();
78 emit q->childrenChanged();
79 }
80
81 void replace(int index, QObject *item) {
82 Q_Q(QQmlObjectModel);
83 auto *attached = QQmlObjectModelAttached::properties(obj: children.at(i: index).item);
84 attached->setIndex(-1);
85 children.replace(i: index, t: Item(item));
86 QQmlObjectModelAttached::properties(obj: children.at(i: index).item)->setIndex(index);
87 QQmlChangeSet changeSet;
88 changeSet.change(index, count: 1);
89 emit q->modelUpdated(changeSet, reset: false);
90 emit q->childrenChanged();
91 }
92
93 void move(int from, int to, int n) {
94 Q_Q(QQmlObjectModel);
95 if (from > to) {
96 // Only move forwards - flip if backwards moving
97 int tfrom = from;
98 int tto = to;
99 from = tto;
100 to = tto+n;
101 n = tfrom-tto;
102 }
103
104 QPODVector<QQmlObjectModelPrivate::Item, 4> store;
105 for (int i = 0; i < to - from; ++i)
106 store.append(v: children[from + n + i]);
107 for (int i = 0; i < n; ++i)
108 store.append(v: children[from + i]);
109
110 for (int i = 0; i < store.count(); ++i) {
111 children[from + i] = store[i];
112 QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(obj: children.at(i: from + i).item);
113 attached->setIndex(from + i);
114 }
115
116 QQmlChangeSet changeSet;
117 changeSet.move(from, to, count: n, moveId: ++moveId);
118 emit q->modelUpdated(changeSet, reset: false);
119 emit q->childrenChanged();
120 }
121
122 void remove(int index, int n) {
123 Q_Q(QQmlObjectModel);
124 for (int i = index; i < index + n; ++i) {
125 QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(obj: children.at(i).item);
126 attached->setIndex(-1);
127 }
128 children.erase(begin: children.begin() + index, end: children.begin() + index + n);
129 for (int i = index; i < children.size(); ++i) {
130 QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(obj: children.at(i).item);
131 attached->setIndex(i);
132 }
133 QQmlChangeSet changeSet;
134 changeSet.remove(index, count: n);
135 emit q->modelUpdated(changeSet, reset: false);
136 emit q->countChanged();
137 emit q->childrenChanged();
138 }
139
140 void clear() {
141 Q_Q(QQmlObjectModel);
142 const auto copy = children;
143 for (const Item &child : copy)
144 emit q->destroyingItem(object: child.item);
145 remove(index: 0, n: children.size());
146 }
147
148 int indexOf(QObject *item) const {
149 for (int i = 0; i < children.size(); ++i)
150 if (children.at(i).item == item)
151 return i;
152 return -1;
153 }
154
155 uint moveId;
156 QList<Item> children;
157};
158
159Q_DECLARE_TYPEINFO(QQmlObjectModelPrivate::Item, Q_PRIMITIVE_TYPE);
160
161
162/*!
163 \qmltype ObjectModel
164 \instantiates QQmlObjectModel
165 \inqmlmodule QtQml.Models
166 \ingroup qtquick-models
167 \brief Defines a set of items to be used as a model.
168
169 An ObjectModel contains the visual items to be used in a view.
170 When an ObjectModel is used in a view, the view does not require
171 a delegate since the ObjectModel already contains the visual
172 delegate (items).
173
174 An item can determine its index within the
175 model via the \l{ObjectModel::index}{index} attached property.
176
177 The example below places three colored rectangles in a ListView.
178 \code
179 import QtQuick 2.0
180 import QtQml.Models 2.1
181
182 Rectangle {
183 ObjectModel {
184 id: itemModel
185 Rectangle { height: 30; width: 80; color: "red" }
186 Rectangle { height: 30; width: 80; color: "green" }
187 Rectangle { height: 30; width: 80; color: "blue" }
188 }
189
190 ListView {
191 anchors.fill: parent
192 model: itemModel
193 }
194 }
195 \endcode
196
197 \image objectmodel.png
198
199 \sa {Qt Quick Examples - Views}
200*/
201
202QQmlObjectModel::QQmlObjectModel(QObject *parent)
203 : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent)
204{
205}
206
207/*!
208 \qmlattachedproperty int QtQml.Models::ObjectModel::index
209 This attached property holds the index of this delegate's item within the model.
210
211 It is attached to each instance of the delegate.
212*/
213
214QQmlListProperty<QObject> QQmlObjectModel::children()
215{
216 Q_D(QQmlObjectModel);
217 return QQmlListProperty<QObject>(this, d,
218 QQmlObjectModelPrivate::children_append,
219 QQmlObjectModelPrivate::children_count,
220 QQmlObjectModelPrivate::children_at,
221 QQmlObjectModelPrivate::children_clear,
222 QQmlObjectModelPrivate::children_replace,
223 QQmlObjectModelPrivate::children_removeLast);
224}
225
226/*!
227 \qmlproperty int QtQml.Models::ObjectModel::count
228
229 The number of items in the model. This property is readonly.
230*/
231int QQmlObjectModel::count() const
232{
233 Q_D(const QQmlObjectModel);
234 return d->children.size();
235}
236
237bool QQmlObjectModel::isValid() const
238{
239 return true;
240}
241
242QObject *QQmlObjectModel::object(int index, QQmlIncubator::IncubationMode)
243{
244 Q_D(QQmlObjectModel);
245 QQmlObjectModelPrivate::Item &item = d->children[index];
246 item.addRef();
247 if (item.ref == 1) {
248 emit initItem(index, object: item.item);
249 emit createdItem(index, object: item.item);
250 }
251 return item.item;
252}
253
254QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item, ReusableFlag)
255{
256 Q_D(QQmlObjectModel);
257 int idx = d->indexOf(item);
258 if (idx >= 0) {
259 if (!d->children[idx].deref())
260 return QQmlInstanceModel::Referenced;
261 }
262 return {};
263}
264
265QVariant QQmlObjectModel::variantValue(int index, const QString &role)
266{
267 Q_D(QQmlObjectModel);
268 if (index < 0 || index >= d->children.size())
269 return QString();
270 return d->children.at(i: index).item->property(name: role.toUtf8().constData());
271}
272
273QQmlIncubator::Status QQmlObjectModel::incubationStatus(int)
274{
275 return QQmlIncubator::Ready;
276}
277
278int QQmlObjectModel::indexOf(QObject *item, QObject *) const
279{
280 Q_D(const QQmlObjectModel);
281 return d->indexOf(item);
282}
283
284QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj)
285{
286 return QQmlObjectModelAttached::properties(obj);
287}
288
289/*!
290 \qmlmethod object QtQml.Models::ObjectModel::get(int index)
291 \since 5.6
292
293 Returns the item at \a index in the model. This allows the item
294 to be accessed or modified from JavaScript:
295
296 \code
297 Component.onCompleted: {
298 objectModel.append(objectComponent.createObject())
299 console.log(objectModel.get(0).objectName);
300 objectModel.get(0).objectName = "first";
301 }
302 \endcode
303
304 The \a index must be an element in the list.
305
306 \sa append()
307*/
308QObject *QQmlObjectModel::get(int index) const
309{
310 Q_D(const QQmlObjectModel);
311 if (index < 0 || index >= d->children.size())
312 return nullptr;
313 return d->children.at(i: index).item;
314}
315
316/*!
317 \qmlmethod QtQml.Models::ObjectModel::append(object item)
318 \since 5.6
319
320 Appends a new \a item to the end of the model.
321
322 \code
323 objectModel.append(objectComponent.createObject())
324 \endcode
325
326 \sa insert(), remove()
327*/
328void QQmlObjectModel::append(QObject *object)
329{
330 Q_D(QQmlObjectModel);
331 d->insert(index: count(), item: object);
332}
333
334/*!
335 \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item)
336 \since 5.6
337
338 Inserts a new \a item to the model at position \a index.
339
340 \code
341 objectModel.insert(2, objectComponent.createObject())
342 \endcode
343
344 The \a index must be to an existing item in the list, or one past
345 the end of the list (equivalent to append).
346
347 \sa append(), remove()
348*/
349void QQmlObjectModel::insert(int index, QObject *object)
350{
351 Q_D(QQmlObjectModel);
352 if (index < 0 || index > count()) {
353 qmlWarning(me: this) << tr(s: "insert: index %1 out of range").arg(a: index);
354 return;
355 }
356 d->insert(index, item: object);
357}
358
359/*!
360 \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1)
361 \since 5.6
362
363 Moves \a n items \a from one position \a to another.
364
365 The from and to ranges must exist; for example, to move the first 3 items
366 to the end of the model:
367
368 \code
369 objectModel.move(0, objectModel.count - 3, 3)
370 \endcode
371
372 \sa append()
373*/
374void QQmlObjectModel::move(int from, int to, int n)
375{
376 Q_D(QQmlObjectModel);
377 if (n <= 0 || from == to)
378 return;
379 if (from < 0 || to < 0 || from + n > count() || to + n > count()) {
380 qmlWarning(me: this) << tr(s: "move: out of range");
381 return;
382 }
383 d->move(from, to, n);
384}
385
386/*!
387 \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1)
388 \since 5.6
389
390 Removes \a n items at \a index from the model.
391
392 \sa clear()
393*/
394void QQmlObjectModel::remove(int index, int n)
395{
396 Q_D(QQmlObjectModel);
397 if (index < 0 || n <= 0 || index + n > count()) {
398 qmlWarning(me: this) << tr(s: "remove: indices [%1 - %2] out of range [0 - %3]").arg(a: index).arg(a: index+n).arg(a: count());
399 return;
400 }
401 d->remove(index, n);
402}
403
404/*!
405 \qmlmethod QtQml.Models::ObjectModel::clear()
406 \since 5.6
407
408 Clears all items from the model.
409
410 \sa append(), remove()
411*/
412void QQmlObjectModel::clear()
413{
414 Q_D(QQmlObjectModel);
415 d->clear();
416}
417
418bool QQmlInstanceModel::setRequiredProperty(int index, const QString &name, const QVariant &value)
419{
420 Q_UNUSED(index);
421 Q_UNUSED(name);
422 Q_UNUSED(value);
423 // The view should not call this function, unless
424 // it's actually handled in a subclass.
425 Q_UNREACHABLE_RETURN(false);
426}
427
428QT_END_NAMESPACE
429
430#include "moc_qqmlobjectmodel_p.cpp"
431

source code of qtdeclarative/src/qmlmodels/qqmlobjectmodel.cpp