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

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