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 "qqmllist.h"
41#include "qqmllist_p.h"
42#include "qqmlengine_p.h"
43#include "qqmlproperty_p.h"
44
45QT_BEGIN_NAMESPACE
46
47QQmlListReferencePrivate::QQmlListReferencePrivate()
48: propertyType(-1), refCount(1)
49{
50}
51
52QQmlListReference QQmlListReferencePrivate::init(const QQmlListProperty<QObject> &prop, int propType, QQmlEngine *engine)
53{
54 QQmlListReference rv;
55
56 if (!prop.object) return rv;
57
58 QQmlEnginePrivate *p = engine?QQmlEnginePrivate::get(engine):nullptr;
59
60 int listType = p?p->listType(propType):QQmlMetaType::listType(propType);
61 if (listType == -1) return rv;
62
63 rv.d = new QQmlListReferencePrivate;
64 rv.d->object = prop.object;
65 rv.d->elementType = QQmlPropertyPrivate::rawMetaObjectForType(p, listType);
66 rv.d->property = prop;
67 rv.d->propertyType = propType;
68
69 return rv;
70}
71
72void QQmlListReferencePrivate::addref()
73{
74 Q_ASSERT(refCount > 0);
75 ++refCount;
76}
77
78void QQmlListReferencePrivate::release()
79{
80 Q_ASSERT(refCount > 0);
81 --refCount;
82 if (!refCount)
83 delete this;
84}
85
86/*!
87\class QQmlListReference
88\since 5.0
89\inmodule QtQml
90\brief The QQmlListReference class allows the manipulation of QQmlListProperty properties.
91
92QQmlListReference allows C++ programs to read from, and assign values to a QML list property in a
93simple and type-safe way. A QQmlListReference can be created by passing an object and property
94name or through a QQmlProperty instance. These two are equivalent:
95
96\code
97QQmlListReference ref1(object, "children");
98
99QQmlProperty ref2(object, "children");
100QQmlListReference ref2 = qvariant_cast<QQmlListReference>(ref2.read());
101\endcode
102
103Not all QML list properties support all operations. A set of methods, canAppend(), canAt(), canClear() and
104canCount() allow programs to query whether an operation is supported on a given property.
105
106QML list properties are type-safe. Only QObject's that derive from the correct base class can be assigned to
107the list. The listElementType() method can be used to query the QMetaObject of the QObject type supported.
108Attempting to add objects of the incorrect type to a list property will fail.
109
110Like with normal lists, when accessing a list element by index, it is the callers responsibility to ensure
111that it does not request an out of range element using the count() method before calling at().
112*/
113
114/*!
115Constructs an invalid instance.
116*/
117QQmlListReference::QQmlListReference()
118: d(nullptr)
119{
120}
121
122/*!
123Constructs a QQmlListReference for \a object's \a property. If \a property is not a list
124property, an invalid QQmlListReference is created. If \a object is destroyed after
125the reference is constructed, it will automatically become invalid. That is, it is safe to hold
126QQmlListReference instances even after \a object is deleted.
127
128Passing \a engine is required to access some QML created list properties. If in doubt, and an engine
129is available, pass it.
130*/
131QQmlListReference::QQmlListReference(QObject *object, const char *property, QQmlEngine *engine)
132: d(nullptr)
133{
134 if (!object || !property) return;
135
136 QQmlPropertyData local;
137 QQmlPropertyData *data =
138 QQmlPropertyCache::property(engine, object, QLatin1String(property), nullptr, local);
139
140 if (!data || !data->isQList()) return;
141
142 QQmlEnginePrivate *p = engine?QQmlEnginePrivate::get(engine):nullptr;
143
144 int listType = p?p->listType(data->propType()):QQmlMetaType::listType(data->propType());
145 if (listType == -1) return;
146
147 d = new QQmlListReferencePrivate;
148 d->object = object;
149 d->elementType = p ? p->rawMetaObjectForType(listType) : QQmlMetaType::qmlType(listType).baseMetaObject();
150 d->propertyType = data->propType();
151
152 void *args[] = { &d->property, nullptr };
153 QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex(), args);
154}
155
156/*! \internal */
157QQmlListReference::QQmlListReference(const QQmlListReference &o)
158: d(o.d)
159{
160 if (d) d->addref();
161}
162
163/*! \internal */
164QQmlListReference &QQmlListReference::operator=(const QQmlListReference &o)
165{
166 if (o.d) o.d->addref();
167 if (d) d->release();
168 d = o.d;
169 return *this;
170}
171
172/*! \internal */
173QQmlListReference::~QQmlListReference()
174{
175 if (d) d->release();
176}
177
178/*!
179Returns true if the instance refers to a valid list property, otherwise false.
180*/
181bool QQmlListReference::isValid() const
182{
183 return d && d->object;
184}
185
186/*!
187Returns the list property's object. Returns \nullptr if the reference is invalid.
188*/
189QObject *QQmlListReference::object() const
190{
191 if (isValid()) return d->object;
192 else return nullptr;
193}
194
195/*!
196Returns the QMetaObject for the elements stored in the list property,
197or \nullptr if the reference is invalid.
198
199The QMetaObject can be used ahead of time to determine whether a given instance can be added
200to a list.
201*/
202const QMetaObject *QQmlListReference::listElementType() const
203{
204 if (isValid()) return d->elementType.metaObject();
205 else return nullptr;
206}
207
208/*!
209Returns true if the list property can be appended to, otherwise false. Returns false if the
210reference is invalid.
211
212\sa append()
213*/
214bool QQmlListReference::canAppend() const
215{
216 return (isValid() && d->property.append);
217}
218
219/*!
220Returns true if the list property can queried by index, otherwise false. Returns false if the
221reference is invalid.
222
223\sa at()
224*/
225bool QQmlListReference::canAt() const
226{
227 return (isValid() && d->property.at);
228}
229
230/*!
231Returns true if the list property can be cleared, otherwise false. Returns false if the
232reference is invalid.
233
234\sa clear()
235*/
236bool QQmlListReference::canClear() const
237{
238 return (isValid() && d->property.clear);
239}
240
241/*!
242Returns true if the list property can be queried for its element count, otherwise false.
243Returns false if the reference is invalid.
244
245\sa count()
246*/
247bool QQmlListReference::canCount() const
248{
249 return (isValid() && d->property.count);
250}
251
252/*!
253 Return true if at(), count(), append() and clear() are implemented, so you can manipulate
254 the list.
255
256\sa isReadable(), at(), count(), append(), clear()
257*/
258bool QQmlListReference::isManipulable() const
259{
260 return (isValid()
261 && d->property.append
262 && d->property.count
263 && d->property.at
264 && d->property.clear);
265}
266
267
268/*!
269 Return true if at() and count() are implemented, so you can access the elements.
270
271\sa isManipulable(), at(), count()
272*/
273bool QQmlListReference::isReadable() const
274{
275 return (isValid() && d->property.count && d->property.at);
276}
277
278/*!
279Appends \a object to the list. Returns true if the operation succeeded, otherwise false.
280
281\sa canAppend()
282*/
283bool QQmlListReference::append(QObject *object) const
284{
285 if (!canAppend()) return false;
286
287 if (object && !QQmlMetaObject::canConvert(object, d->elementType))
288 return false;
289
290 d->property.append(&d->property, object);
291
292 return true;
293}
294
295/*!
296Returns the list element at \a index, or 0 if the operation failed.
297
298\sa canAt()
299*/
300QObject *QQmlListReference::at(int index) const
301{
302 if (!canAt()) return nullptr;
303
304 return d->property.at(&d->property, index);
305}
306
307/*!
308Clears the list. Returns true if the operation succeeded, otherwise false.
309
310\sa canClear()
311*/
312bool QQmlListReference::clear() const
313{
314 if (!canClear()) return false;
315
316 d->property.clear(&d->property);
317
318 return true;
319}
320
321/*!
322Returns the number of objects in the list, or 0 if the operation failed.
323*/
324int QQmlListReference::count() const
325{
326 if (!canCount()) return 0;
327
328 return d->property.count(&d->property);
329}
330
331/*!
332\class QQmlListProperty
333\since 5.0
334\inmodule QtQml
335\brief The QQmlListProperty class allows applications to expose list-like
336properties of QObject-derived classes to QML.
337
338QML has many list properties, where more than one object value can be assigned.
339The use of a list property from QML looks like this:
340
341\code
342FruitBasket {
343 fruit: [
344 Apple {},
345 Orange{},
346 Banana{}
347 ]
348}
349\endcode
350
351The QQmlListProperty encapsulates a group of function pointers that represent the
352set of actions QML can perform on the list - adding items, retrieving items and
353clearing the list. In the future, additional operations may be supported. All
354list properties must implement the append operation, but the rest are optional.
355
356To provide a list property, a C++ class must implement the operation callbacks,
357and then return an appropriate QQmlListProperty value from the property getter.
358List properties should have no setter. In the example above, the Q_PROPERTY()
359declarative will look like this:
360
361\code
362Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit)
363\endcode
364
365QML list properties are type-safe - in this case \c {Fruit} is a QObject type that
366\c {Apple}, \c {Orange} and \c {Banana} all derive from.
367
368\sa {Extending QML - Object and List Property Types Example}
369*/
370
371/*!
372\fn template<typename T> QQmlListProperty<T>::QQmlListProperty()
373\internal
374*/
375
376/*!
377\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> &list)
378
379Convenience constructor for making a QQmlListProperty value from an existing
380QList \a list. The \a list reference must remain valid for as long as \a object
381exists. \a object must be provided.
382
383Generally this constructor should not be used in production code, as a
384writable QList violates QML's memory management rules. However, this constructor
385can be very useful while prototyping.
386*/
387
388/*!
389\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, void *data,
390 CountFunction count, AtFunction at)
391
392Construct a readonly QQmlListProperty from a set of operation functions
393\a count and \a at. An opaque \a data handle may be passed which can be
394accessed from within the operation functions. The list property
395remains valid while \a object exists.
396*/
397
398/*!
399\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, void *data, AppendFunction append,
400 CountFunction count, AtFunction at,
401 ClearFunction clear)
402
403Construct a QQmlListProperty from a set of operation functions \a append,
404\a count, \a at, and \a clear. An opaque \a data handle may be passed which
405can be accessed from within the operation functions. The list property
406remains valid while \a object exists.
407
408Null pointers can be passed for any function. If any null pointers are passed in, the list
409will be neither designable nor alterable by the debugger. It is recommended to provide valid
410pointers for all functions.
411*/
412
413/*!
414\typedef QQmlListProperty::AppendFunction
415
416Synonym for \c {void (*)(QQmlListProperty<T> *property, T *value)}.
417
418Append the \a value to the list \a property.
419*/
420
421/*!
422\typedef QQmlListProperty::CountFunction
423
424Synonym for \c {int (*)(QQmlListProperty<T> *property)}.
425
426Return the number of elements in the list \a property.
427*/
428
429/*!
430\fn template<typename T> bool QQmlListProperty<T>::operator==(const QQmlListProperty &other) const
431
432Returns true if this QQmlListProperty is equal to \a other, otherwise false.
433*/
434
435/*!
436\typedef QQmlListProperty::AtFunction
437
438Synonym for \c {T *(*)(QQmlListProperty<T> *property, int index)}.
439
440Return the element at position \a index in the list \a property.
441*/
442
443/*!
444\typedef QQmlListProperty::ClearFunction
445
446Synonym for \c {void (*)(QQmlListProperty<T> *property)}.
447
448Clear the list \a property.
449*/
450
451QT_END_NAMESPACE
452