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(e: 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(e: 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(typeId: 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/*!
253Returns true if items in the list property can be replaced, otherwise false.
254Returns false if the reference is invalid.
255
256\sa replace()
257*/
258bool QQmlListReference::canReplace() const
259{
260 return (isValid() && d->property.replace);
261}
262
263/*!
264Returns true if the last item can be removed from the list property, otherwise false.
265Returns false if the reference is invalid.
266
267\sa removeLast()
268*/
269bool QQmlListReference::canRemoveLast() const
270{
271 return (isValid() && d->property.removeLast);
272}
273
274/*!
275 Return true if at(), count(), append(), and either clear() or removeLast()
276 are implemented, so you can manipulate the list.
277
278 Mind that replace() and removeLast() can be emulated by stashing all
279 items and rebuilding the list using clear() and append(). Therefore,
280 they are not required for the list to be manipulable. Furthermore,
281 clear() can be emulated using removeLast().
282
283\sa isReadable(), at(), count(), append(), clear(), replace(), removeLast()
284*/
285bool QQmlListReference::isManipulable() const
286{
287 return (isValid()
288 && d->property.append
289 && d->property.count
290 && d->property.at
291 && d->property.clear);
292}
293
294
295/*!
296 Return true if at() and count() are implemented, so you can access the elements.
297
298\sa isManipulable(), at(), count()
299*/
300bool QQmlListReference::isReadable() const
301{
302 return (isValid() && d->property.count && d->property.at);
303}
304
305/*!
306Appends \a object to the list. Returns true if the operation succeeded, otherwise false.
307
308\sa canAppend()
309*/
310bool QQmlListReference::append(QObject *object) const
311{
312 if (!canAppend()) return false;
313
314 if (object && !QQmlMetaObject::canConvert(from: object, to: d->elementType))
315 return false;
316
317 d->property.append(&d->property, object);
318
319 return true;
320}
321
322/*!
323Returns the list element at \a index, or 0 if the operation failed.
324
325\sa canAt()
326*/
327QObject *QQmlListReference::at(int index) const
328{
329 if (!canAt()) return nullptr;
330
331 return d->property.at(&d->property, index);
332}
333
334/*!
335Clears the list. Returns true if the operation succeeded, otherwise false.
336
337\sa canClear()
338*/
339bool QQmlListReference::clear() const
340{
341 if (!canClear()) return false;
342
343 d->property.clear(&d->property);
344
345 return true;
346}
347
348/*!
349Returns the number of objects in the list, or 0 if the operation failed.
350*/
351int QQmlListReference::count() const
352{
353 if (!canCount()) return 0;
354
355 return d->property.count(&d->property);
356}
357
358/*!
359Replaces the item at \a index in the list with \a object.
360Returns true if the operation succeeded, otherwise false.
361
362\sa canReplace()
363*/
364bool QQmlListReference::replace(int index, QObject *object) const
365{
366 if (!canReplace())
367 return false;
368
369 if (object && !QQmlMetaObject::canConvert(from: object, to: d->elementType))
370 return false;
371
372 d->property.replace(&d->property, index, object);
373 return true;
374}
375
376/*!
377Removes the last item in the list.
378Returns true if the operation succeeded, otherwise false.
379
380\sa canRemoveLast()
381*/
382bool QQmlListReference::removeLast() const
383{
384 if (!canRemoveLast())
385 return false;
386
387 d->property.removeLast(&d->property);
388 return true;
389}
390
391/*!
392\class QQmlListProperty
393\since 5.0
394\inmodule QtQml
395\brief The QQmlListProperty class allows applications to expose list-like
396properties of QObject-derived classes to QML.
397
398QML has many list properties, where more than one object value can be assigned.
399The use of a list property from QML looks like this:
400
401\code
402FruitBasket {
403 fruit: [
404 Apple {},
405 Orange{},
406 Banana{}
407 ]
408}
409\endcode
410
411The QQmlListProperty encapsulates a group of function pointers that represent the
412set of actions QML can perform on the list - adding items, retrieving items and
413clearing the list. In the future, additional operations may be supported. All
414list properties must implement the append operation, but the rest are optional.
415
416To provide a list property, a C++ class must implement the operation callbacks,
417and then return an appropriate QQmlListProperty value from the property getter.
418List properties should have no setter. In the example above, the Q_PROPERTY()
419declarative will look like this:
420
421\code
422Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit)
423\endcode
424
425QML list properties are type-safe - in this case \c {Fruit} is a QObject type that
426\c {Apple}, \c {Orange} and \c {Banana} all derive from.
427
428\sa {Extending QML - Object and List Property Types Example}
429*/
430
431/*!
432\fn template<typename T> QQmlListProperty<T>::QQmlListProperty()
433\internal
434*/
435
436/*!
437\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> &list)
438\deprecated
439
440Convenience constructor for making a QQmlListProperty value from an existing
441QList \a list. The \a list reference must remain valid for as long as \a object
442exists. \a object must be provided.
443
444This constructor synthesizes the removeLast() and replace() methods
445introduced in Qt 5.15, using count(), at(), clear(), and append(). This is slow.
446If you intend to manipulate the list beyond clearing it, you should explicitly
447provide these methods.
448*/
449
450/*!
451\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> *list)
452\since 5.15
453
454Convenience constructor for making a QQmlListProperty value from an existing
455QList \a list. The \a list reference must remain valid for as long as \a object
456exists. \a object must be provided.
457*/
458
459/*!
460\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, void *data,
461 CountFunction count, AtFunction at)
462
463Construct a readonly QQmlListProperty from a set of operation functions
464\a count and \a at. An opaque \a data handle may be passed which can be
465accessed from within the operation functions. The list property
466remains valid while \a object exists.
467*/
468
469/*!
470\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, void *data, AppendFunction append,
471 CountFunction count, AtFunction at,
472 ClearFunction clear)
473
474Construct a QQmlListProperty from a set of operation functions \a append,
475\a count, \a at, and \a clear. An opaque \a data handle may be passed which
476can be accessed from within the operation functions. The list property
477remains valid while \a object exists.
478
479Null pointers can be passed for any function. If any null pointers are passed in, the list
480will be neither designable nor alterable by the debugger. It is recommended to provide valid
481pointers for all functions.
482
483\note The resulting QQmlListProperty will synthesize the removeLast() and
484replace() methods using \a count, \a at, \a clear, and \a append if all of those
485are given. This is slow. If you intend to manipulate the list beyond clearing it,
486you should explicitly provide these methods.
487*/
488
489/*!
490\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(
491 QObject *object, void *data, AppendFunction append, CountFunction count,
492 AtFunction at, ClearFunction clear, ReplaceFunction replace,
493 RemoveLastFunction removeLast)
494
495Construct a QQmlListProperty from a set of operation functions \a append,
496\a count, \a at, \a clear, \a replace, and \removeLast. An opaque \a data handle
497may be passed which can be accessed from within the operation functions. The
498list property remains valid while \a object exists.
499
500Null pointers can be passed for any function, causing the respective function to
501be synthesized using the others, if possible. QQmlListProperty can synthesize
502\list
503 \li \a clear using \a count and \a removeLast
504 \li \a replace using \a count, \a at, \a clear, and \a append
505 \li \a replace using \a count, \a at, \a removeLast, and \a append
506 \li \a removeLast using \a count, \a at, \a clear, and \a append
507\endlist
508if those are given. This is slow, but if your list does not natively provide
509faster options for these primitives, you may want to use the synthesized ones.
510
511Furthermore, if either of \a count, \a at, \a append, and \a clear are neither
512given explicitly nor synthesized, the list will be neither designable nor
513alterable by the debugger. It is recommended to provide enough valid pointers
514to avoid this situation.
515*/
516
517/*!
518\typedef QQmlListProperty::AppendFunction
519
520Synonym for \c {void (*)(QQmlListProperty<T> *property, T *value)}.
521
522Append the \a value to the list \a property.
523*/
524
525/*!
526\typedef QQmlListProperty::CountFunction
527
528Synonym for \c {int (*)(QQmlListProperty<T> *property)}.
529
530Return the number of elements in the list \a property.
531*/
532
533/*!
534\fn template<typename T> bool QQmlListProperty<T>::operator==(const QQmlListProperty &other) const
535
536Returns true if this QQmlListProperty is equal to \a other, otherwise false.
537*/
538
539/*!
540\typedef QQmlListProperty::AtFunction
541
542Synonym for \c {T *(*)(QQmlListProperty<T> *property, int index)}.
543
544Return the element at position \a index in the list \a property.
545*/
546
547/*!
548\typedef QQmlListProperty::ClearFunction
549
550Synonym for \c {void (*)(QQmlListProperty<T> *property)}.
551
552Clear the list \a property.
553*/
554
555/*!
556\typedef QQmlListProperty::ReplaceFunction
557
558Synonym for \c {void (*)(QQmlListProperty<T> *property, int index, T *value)}.
559
560Replace the element at position \a index in the list \a property with \a value.
561*/
562
563/*!
564\typedef QQmlListProperty::RemoveLastFunction
565
566Synonym for \c {void (*)(QQmlListProperty<T> *property)}.
567
568Remove the last element from the list \a property.
569*/
570
571QT_END_NAMESPACE
572

source code of qtdeclarative/src/qml/qml/qqmllist.cpp