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(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 | |
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(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 */ |
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 | Returns true if items in the list property can be replaced, otherwise false. |
254 | Returns false if the reference is invalid. |
255 | |
256 | \sa replace() |
257 | */ |
258 | bool QQmlListReference::canReplace() const |
259 | { |
260 | return (isValid() && d->property.replace); |
261 | } |
262 | |
263 | /*! |
264 | Returns true if the last item can be removed from the list property, otherwise false. |
265 | Returns false if the reference is invalid. |
266 | |
267 | \sa removeLast() |
268 | */ |
269 | bool 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 | */ |
285 | bool 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 | */ |
300 | bool QQmlListReference::isReadable() const |
301 | { |
302 | return (isValid() && d->property.count && d->property.at); |
303 | } |
304 | |
305 | /*! |
306 | Appends \a object to the list. Returns true if the operation succeeded, otherwise false. |
307 | |
308 | \sa canAppend() |
309 | */ |
310 | bool 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 | /*! |
323 | Returns the list element at \a index, or 0 if the operation failed. |
324 | |
325 | \sa canAt() |
326 | */ |
327 | QObject *QQmlListReference::at(int index) const |
328 | { |
329 | if (!canAt()) return nullptr; |
330 | |
331 | return d->property.at(&d->property, index); |
332 | } |
333 | |
334 | /*! |
335 | Clears the list. Returns true if the operation succeeded, otherwise false. |
336 | |
337 | \sa canClear() |
338 | */ |
339 | bool QQmlListReference::clear() const |
340 | { |
341 | if (!canClear()) return false; |
342 | |
343 | d->property.clear(&d->property); |
344 | |
345 | return true; |
346 | } |
347 | |
348 | /*! |
349 | Returns the number of objects in the list, or 0 if the operation failed. |
350 | */ |
351 | int QQmlListReference::count() const |
352 | { |
353 | if (!canCount()) return 0; |
354 | |
355 | return d->property.count(&d->property); |
356 | } |
357 | |
358 | /*! |
359 | Replaces the item at \a index in the list with \a object. |
360 | Returns true if the operation succeeded, otherwise false. |
361 | |
362 | \sa canReplace() |
363 | */ |
364 | bool 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 | /*! |
377 | Removes the last item in the list. |
378 | Returns true if the operation succeeded, otherwise false. |
379 | |
380 | \sa canRemoveLast() |
381 | */ |
382 | bool 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 |
396 | properties of QObject-derived classes to QML. |
397 | |
398 | QML has many list properties, where more than one object value can be assigned. |
399 | The use of a list property from QML looks like this: |
400 | |
401 | \code |
402 | FruitBasket { |
403 | fruit: [ |
404 | Apple {}, |
405 | Orange{}, |
406 | Banana{} |
407 | ] |
408 | } |
409 | \endcode |
410 | |
411 | The QQmlListProperty encapsulates a group of function pointers that represent the |
412 | set of actions QML can perform on the list - adding items, retrieving items and |
413 | clearing the list. In the future, additional operations may be supported. All |
414 | list properties must implement the append operation, but the rest are optional. |
415 | |
416 | To provide a list property, a C++ class must implement the operation callbacks, |
417 | and then return an appropriate QQmlListProperty value from the property getter. |
418 | List properties should have no setter. In the example above, the Q_PROPERTY() |
419 | declarative will look like this: |
420 | |
421 | \code |
422 | Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit) |
423 | \endcode |
424 | |
425 | QML 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 | |
440 | Convenience constructor for making a QQmlListProperty value from an existing |
441 | QList \a list. The \a list reference must remain valid for as long as \a object |
442 | exists. \a object must be provided. |
443 | |
444 | This constructor synthesizes the removeLast() and replace() methods |
445 | introduced in Qt 5.15, using count(), at(), clear(), and append(). This is slow. |
446 | If you intend to manipulate the list beyond clearing it, you should explicitly |
447 | provide these methods. |
448 | */ |
449 | |
450 | /*! |
451 | \fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> *list) |
452 | \since 5.15 |
453 | |
454 | Convenience constructor for making a QQmlListProperty value from an existing |
455 | QList \a list. The \a list reference must remain valid for as long as \a object |
456 | exists. \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 | |
463 | Construct 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 |
465 | accessed from within the operation functions. The list property |
466 | remains 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 | |
474 | Construct 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 |
476 | can be accessed from within the operation functions. The list property |
477 | remains valid while \a object exists. |
478 | |
479 | Null pointers can be passed for any function. If any null pointers are passed in, the list |
480 | will be neither designable nor alterable by the debugger. It is recommended to provide valid |
481 | pointers for all functions. |
482 | |
483 | \note The resulting QQmlListProperty will synthesize the removeLast() and |
484 | replace() methods using \a count, \a at, \a clear, and \a append if all of those |
485 | are given. This is slow. If you intend to manipulate the list beyond clearing it, |
486 | you 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 | |
495 | Construct 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 |
497 | may be passed which can be accessed from within the operation functions. The |
498 | list property remains valid while \a object exists. |
499 | |
500 | Null pointers can be passed for any function, causing the respective function to |
501 | be 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 |
508 | if those are given. This is slow, but if your list does not natively provide |
509 | faster options for these primitives, you may want to use the synthesized ones. |
510 | |
511 | Furthermore, if either of \a count, \a at, \a append, and \a clear are neither |
512 | given explicitly nor synthesized, the list will be neither designable nor |
513 | alterable by the debugger. It is recommended to provide enough valid pointers |
514 | to avoid this situation. |
515 | */ |
516 | |
517 | /*! |
518 | \typedef QQmlListProperty::AppendFunction |
519 | |
520 | Synonym for \c {void (*)(QQmlListProperty<T> *property, T *value)}. |
521 | |
522 | Append the \a value to the list \a property. |
523 | */ |
524 | |
525 | /*! |
526 | \typedef QQmlListProperty::CountFunction |
527 | |
528 | Synonym for \c {int (*)(QQmlListProperty<T> *property)}. |
529 | |
530 | Return 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 | |
536 | Returns true if this QQmlListProperty is equal to \a other, otherwise false. |
537 | */ |
538 | |
539 | /*! |
540 | \typedef QQmlListProperty::AtFunction |
541 | |
542 | Synonym for \c {T *(*)(QQmlListProperty<T> *property, int index)}. |
543 | |
544 | Return the element at position \a index in the list \a property. |
545 | */ |
546 | |
547 | /*! |
548 | \typedef QQmlListProperty::ClearFunction |
549 | |
550 | Synonym for \c {void (*)(QQmlListProperty<T> *property)}. |
551 | |
552 | Clear the list \a property. |
553 | */ |
554 | |
555 | /*! |
556 | \typedef QQmlListProperty::ReplaceFunction |
557 | |
558 | Synonym for \c {void (*)(QQmlListProperty<T> *property, int index, T *value)}. |
559 | |
560 | Replace the element at position \a index in the list \a property with \a value. |
561 | */ |
562 | |
563 | /*! |
564 | \typedef QQmlListProperty::RemoveLastFunction |
565 | |
566 | Synonym for \c {void (*)(QQmlListProperty<T> *property)}. |
567 | |
568 | Remove the last element from the list \a property. |
569 | */ |
570 | |
571 | QT_END_NAMESPACE |
572 | |