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 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | QQmlListReferencePrivate::QQmlListReferencePrivate() |
48 | : propertyType(-1), refCount(1) |
49 | { |
50 | } |
51 | |
52 | QQmlListReference 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 | |
72 | void QQmlListReferencePrivate::addref() |
73 | { |
74 | Q_ASSERT(refCount > 0); |
75 | ++refCount; |
76 | } |
77 | |
78 | void 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 | |
92 | QQmlListReference allows C++ programs to read from, and assign values to a QML list property in a |
93 | simple and type-safe way. A QQmlListReference can be created by passing an object and property |
94 | name or through a QQmlProperty instance. These two are equivalent: |
95 | |
96 | \code |
97 | QQmlListReference ref1(object, "children"); |
98 | |
99 | QQmlProperty ref2(object, "children"); |
100 | QQmlListReference ref2 = qvariant_cast<QQmlListReference>(ref2.read()); |
101 | \endcode |
102 | |
103 | Not all QML list properties support all operations. A set of methods, canAppend(), canAt(), canClear() and |
104 | canCount() allow programs to query whether an operation is supported on a given property. |
105 | |
106 | QML list properties are type-safe. Only QObject's that derive from the correct base class can be assigned to |
107 | the list. The listElementType() method can be used to query the QMetaObject of the QObject type supported. |
108 | Attempting to add objects of the incorrect type to a list property will fail. |
109 | |
110 | Like with normal lists, when accessing a list element by index, it is the callers responsibility to ensure |
111 | that it does not request an out of range element using the count() method before calling at(). |
112 | */ |
113 | |
114 | /*! |
115 | Constructs an invalid instance. |
116 | */ |
117 | QQmlListReference::QQmlListReference() |
118 | : d(nullptr) |
119 | { |
120 | } |
121 | |
122 | /*! |
123 | Constructs a QQmlListReference for \a object's \a property. If \a property is not a list |
124 | property, an invalid QQmlListReference is created. If \a object is destroyed after |
125 | the reference is constructed, it will automatically become invalid. That is, it is safe to hold |
126 | QQmlListReference instances even after \a object is deleted. |
127 | |
128 | Passing \a engine is required to access some QML created list properties. If in doubt, and an engine |
129 | is available, pass it. |
130 | */ |
131 | QQmlListReference::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 */ |
157 | QQmlListReference::QQmlListReference(const QQmlListReference &o) |
158 | : d(o.d) |
159 | { |
160 | if (d) d->addref(); |
161 | } |
162 | |
163 | /*! \internal */ |
164 | QQmlListReference &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 */ |
173 | QQmlListReference::~QQmlListReference() |
174 | { |
175 | if (d) d->release(); |
176 | } |
177 | |
178 | /*! |
179 | Returns true if the instance refers to a valid list property, otherwise false. |
180 | */ |
181 | bool QQmlListReference::isValid() const |
182 | { |
183 | return d && d->object; |
184 | } |
185 | |
186 | /*! |
187 | Returns the list property's object. Returns \nullptr if the reference is invalid. |
188 | */ |
189 | QObject *QQmlListReference::object() const |
190 | { |
191 | if (isValid()) return d->object; |
192 | else return nullptr; |
193 | } |
194 | |
195 | /*! |
196 | Returns the QMetaObject for the elements stored in the list property, |
197 | or \nullptr if the reference is invalid. |
198 | |
199 | The QMetaObject can be used ahead of time to determine whether a given instance can be added |
200 | to a list. |
201 | */ |
202 | const QMetaObject *QQmlListReference::listElementType() const |
203 | { |
204 | if (isValid()) return d->elementType.metaObject(); |
205 | else return nullptr; |
206 | } |
207 | |
208 | /*! |
209 | Returns true if the list property can be appended to, otherwise false. Returns false if the |
210 | reference is invalid. |
211 | |
212 | \sa append() |
213 | */ |
214 | bool QQmlListReference::canAppend() const |
215 | { |
216 | return (isValid() && d->property.append); |
217 | } |
218 | |
219 | /*! |
220 | Returns true if the list property can queried by index, otherwise false. Returns false if the |
221 | reference is invalid. |
222 | |
223 | \sa at() |
224 | */ |
225 | bool QQmlListReference::canAt() const |
226 | { |
227 | return (isValid() && d->property.at); |
228 | } |
229 | |
230 | /*! |
231 | Returns true if the list property can be cleared, otherwise false. Returns false if the |
232 | reference is invalid. |
233 | |
234 | \sa clear() |
235 | */ |
236 | bool QQmlListReference::canClear() const |
237 | { |
238 | return (isValid() && d->property.clear); |
239 | } |
240 | |
241 | /*! |
242 | Returns true if the list property can be queried for its element count, otherwise false. |
243 | Returns false if the reference is invalid. |
244 | |
245 | \sa count() |
246 | */ |
247 | bool 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 | */ |
258 | bool 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 | */ |
273 | bool QQmlListReference::isReadable() const |
274 | { |
275 | return (isValid() && d->property.count && d->property.at); |
276 | } |
277 | |
278 | /*! |
279 | Appends \a object to the list. Returns true if the operation succeeded, otherwise false. |
280 | |
281 | \sa canAppend() |
282 | */ |
283 | bool 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 | /*! |
296 | Returns the list element at \a index, or 0 if the operation failed. |
297 | |
298 | \sa canAt() |
299 | */ |
300 | QObject *QQmlListReference::at(int index) const |
301 | { |
302 | if (!canAt()) return nullptr; |
303 | |
304 | return d->property.at(&d->property, index); |
305 | } |
306 | |
307 | /*! |
308 | Clears the list. Returns true if the operation succeeded, otherwise false. |
309 | |
310 | \sa canClear() |
311 | */ |
312 | bool QQmlListReference::clear() const |
313 | { |
314 | if (!canClear()) return false; |
315 | |
316 | d->property.clear(&d->property); |
317 | |
318 | return true; |
319 | } |
320 | |
321 | /*! |
322 | Returns the number of objects in the list, or 0 if the operation failed. |
323 | */ |
324 | int 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 |
336 | properties of QObject-derived classes to QML. |
337 | |
338 | QML has many list properties, where more than one object value can be assigned. |
339 | The use of a list property from QML looks like this: |
340 | |
341 | \code |
342 | FruitBasket { |
343 | fruit: [ |
344 | Apple {}, |
345 | Orange{}, |
346 | Banana{} |
347 | ] |
348 | } |
349 | \endcode |
350 | |
351 | The QQmlListProperty encapsulates a group of function pointers that represent the |
352 | set of actions QML can perform on the list - adding items, retrieving items and |
353 | clearing the list. In the future, additional operations may be supported. All |
354 | list properties must implement the append operation, but the rest are optional. |
355 | |
356 | To provide a list property, a C++ class must implement the operation callbacks, |
357 | and then return an appropriate QQmlListProperty value from the property getter. |
358 | List properties should have no setter. In the example above, the Q_PROPERTY() |
359 | declarative will look like this: |
360 | |
361 | \code |
362 | Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit) |
363 | \endcode |
364 | |
365 | QML 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 | |
379 | Convenience constructor for making a QQmlListProperty value from an existing |
380 | QList \a list. The \a list reference must remain valid for as long as \a object |
381 | exists. \a object must be provided. |
382 | |
383 | Generally this constructor should not be used in production code, as a |
384 | writable QList violates QML's memory management rules. However, this constructor |
385 | can 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 | |
392 | Construct 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 |
394 | accessed from within the operation functions. The list property |
395 | remains 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 | |
403 | Construct 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 |
405 | can be accessed from within the operation functions. The list property |
406 | remains valid while \a object exists. |
407 | |
408 | Null pointers can be passed for any function. If any null pointers are passed in, the list |
409 | will be neither designable nor alterable by the debugger. It is recommended to provide valid |
410 | pointers for all functions. |
411 | */ |
412 | |
413 | /*! |
414 | \typedef QQmlListProperty::AppendFunction |
415 | |
416 | Synonym for \c {void (*)(QQmlListProperty<T> *property, T *value)}. |
417 | |
418 | Append the \a value to the list \a property. |
419 | */ |
420 | |
421 | /*! |
422 | \typedef QQmlListProperty::CountFunction |
423 | |
424 | Synonym for \c {int (*)(QQmlListProperty<T> *property)}. |
425 | |
426 | Return 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 | |
432 | Returns true if this QQmlListProperty is equal to \a other, otherwise false. |
433 | */ |
434 | |
435 | /*! |
436 | \typedef QQmlListProperty::AtFunction |
437 | |
438 | Synonym for \c {T *(*)(QQmlListProperty<T> *property, int index)}. |
439 | |
440 | Return the element at position \a index in the list \a property. |
441 | */ |
442 | |
443 | /*! |
444 | \typedef QQmlListProperty::ClearFunction |
445 | |
446 | Synonym for \c {void (*)(QQmlListProperty<T> *property)}. |
447 | |
448 | Clear the list \a property. |
449 | */ |
450 | |
451 | QT_END_NAMESPACE |
452 | |