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 "qqmlproperty.h"
41#include "qqmlproperty_p.h"
42
43#include "qqml.h"
44#include "qqmlbinding_p.h"
45#include "qqmlboundsignal_p.h"
46#include "qqmlcontext.h"
47#include "qqmlcontext_p.h"
48#include "qqmlboundsignal_p.h"
49#include "qqmlengine.h"
50#include "qqmlengine_p.h"
51#include "qqmldata_p.h"
52#include "qqmlstringconverters_p.h"
53#include "qqmllist_p.h"
54#include "qqmlvmemetaobject_p.h"
55#include "qqmlexpression_p.h"
56#include "qqmlvaluetypeproxybinding_p.h"
57#include <private/qjsvalue_p.h>
58#include <private/qv4functionobject_p.h>
59
60#include <QStringList>
61#include <QVector>
62#include <private/qmetaobject_p.h>
63#include <private/qqmlvaluetypewrapper_p.h>
64#include <QtCore/qdebug.h>
65#include <cmath>
66#include <QtQml/QQmlPropertyMap>
67
68Q_DECLARE_METATYPE(QList<int>)
69Q_DECLARE_METATYPE(QList<qreal>)
70Q_DECLARE_METATYPE(QList<bool>)
71Q_DECLARE_METATYPE(QList<QString>)
72Q_DECLARE_METATYPE(QList<QUrl>)
73
74QT_BEGIN_NAMESPACE
75
76/*!
77\class QQmlProperty
78\since 5.0
79\inmodule QtQml
80\brief The QQmlProperty class abstracts accessing properties on objects created from QML.
81
82As QML uses Qt's meta-type system all of the existing QMetaObject classes can be used to introspect
83and interact with objects created by QML. However, some of the new features provided by QML - such
84as type safety and attached properties - are most easily used through the QQmlProperty class
85that simplifies some of their natural complexity.
86
87Unlike QMetaProperty which represents a property on a class type, QQmlProperty encapsulates
88a property on a specific object instance. To read a property's value, programmers create a
89QQmlProperty instance and call the read() method. Likewise to write a property value the
90write() method is used.
91
92For example, for the following QML code:
93
94\qml
95// MyItem.qml
96import QtQuick 2.0
97
98Text { text: "A bit of text" }
99\endqml
100
101The \l Text object's properties could be accessed using QQmlProperty, like this:
102
103\code
104#include <QQmlProperty>
105#include <QGraphicsObject>
106
107...
108
109QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
110QQmlProperty property(view.rootObject(), "font.pixelSize");
111qWarning() << "Current pixel size:" << property.read().toInt();
112property.write(24);
113qWarning() << "Pixel size should now be 24:" << property.read().toInt();
114\endcode
115*/
116
117/*!
118 Create an invalid QQmlProperty.
119*/
120QQmlProperty::QQmlProperty()
121: d(nullptr)
122{
123}
124
125/*! \internal */
126QQmlProperty::~QQmlProperty()
127{
128 if (d)
129 d->release();
130 d = nullptr;
131}
132
133/*!
134 Creates a QQmlProperty for the default property of \a obj. If there is no
135 default property, an invalid QQmlProperty will be created.
136 */
137QQmlProperty::QQmlProperty(QObject *obj)
138: d(new QQmlPropertyPrivate)
139{
140 d->initDefault(obj);
141}
142
143/*!
144 Creates a QQmlProperty for the default property of \a obj
145 using the \l{QQmlContext} {context} \a ctxt. If there is
146 no default property, an invalid QQmlProperty will be
147 created.
148 */
149QQmlProperty::QQmlProperty(QObject *obj, QQmlContext *ctxt)
150: d(new QQmlPropertyPrivate)
151{
152 d->context = ctxt?QQmlContextData::get(context: ctxt):nullptr;
153 d->engine = ctxt?ctxt->engine():nullptr;
154 d->initDefault(obj);
155}
156
157/*!
158 Creates a QQmlProperty for the default property of \a obj
159 using the environment for instantiating QML components that is
160 provided by \a engine. If there is no default property, an
161 invalid QQmlProperty will be created.
162 */
163QQmlProperty::QQmlProperty(QObject *obj, QQmlEngine *engine)
164 : d(new QQmlPropertyPrivate)
165{
166 d->context = nullptr;
167 d->engine = engine;
168 d->initDefault(obj);
169}
170
171/*!
172 Initialize from the default property of \a obj
173*/
174void QQmlPropertyPrivate::initDefault(QObject *obj)
175{
176 if (!obj)
177 return;
178
179 QMetaProperty p = QQmlMetaType::defaultProperty(obj);
180 core.load(p);
181 if (core.isValid())
182 object = obj;
183}
184
185/*!
186 Creates a QQmlProperty for the property \a name of \a obj.
187 */
188QQmlProperty::QQmlProperty(QObject *obj, const QString &name)
189: d(new QQmlPropertyPrivate)
190{
191 d->initProperty(obj, name);
192 if (!isValid()) d->object = nullptr;
193}
194
195/*!
196 Creates a QQmlProperty for the property \a name of \a obj
197 using the \l{QQmlContext} {context} \a ctxt.
198
199 Creating a QQmlProperty without a context will render some
200 properties - like attached properties - inaccessible.
201*/
202QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt)
203: d(new QQmlPropertyPrivate)
204{
205 d->context = ctxt?QQmlContextData::get(context: ctxt):nullptr;
206 d->engine = ctxt?ctxt->engine():nullptr;
207 d->initProperty(obj, name);
208 if (!isValid()) { d->object = nullptr; d->context = nullptr; d->engine = nullptr; }
209}
210
211/*!
212 Creates a QQmlProperty for the property \a name of \a obj
213 using the environment for instantiating QML components that is
214 provided by \a engine.
215 */
216QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine)
217: d(new QQmlPropertyPrivate)
218{
219 d->context = nullptr;
220 d->engine = engine;
221 d->initProperty(obj, name);
222 if (!isValid()) { d->object = nullptr; d->context = nullptr; d->engine = nullptr; }
223}
224
225QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propertyName, QQmlContextData *context)
226{
227 QQmlProperty result;
228 auto d = new QQmlPropertyPrivate;
229 result.d = d;
230 d->context = context;
231 d->engine = context ? context->engine : nullptr;
232 d->initProperty(obj: target, name: propertyName);
233 if (!result.isValid()) {
234 d->object = nullptr;
235 d->context = nullptr;
236 d->engine = nullptr;
237 }
238 return result;
239}
240
241QQmlPropertyPrivate::QQmlPropertyPrivate()
242: context(nullptr), engine(nullptr), object(nullptr), isNameCached(false)
243{
244}
245
246QQmlContextData *QQmlPropertyPrivate::effectiveContext() const
247{
248 if (context) return context;
249 else if (engine) return QQmlContextData::get(context: engine->rootContext());
250 else return nullptr;
251}
252
253void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
254{
255 if (!obj) return;
256
257 QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context?context->imports:nullptr;
258
259 QObject *currentObject = obj;
260 QVector<QStringRef> path;
261 QStringRef terminal(&name);
262
263 if (name.contains(c: QLatin1Char('.'))) {
264 path = name.splitRef(sep: QLatin1Char('.'));
265 if (path.isEmpty()) return;
266
267 // Everything up to the last property must be an "object type" property
268 for (int ii = 0; ii < path.count() - 1; ++ii) {
269 const QStringRef &pathName = path.at(i: ii);
270
271 // Types must begin with an uppercase letter (see checkRegistration()
272 // in qqmlmetatype.cpp for the enforcement of this).
273 if (typeNameCache && !pathName.isEmpty() && pathName.at(i: 0).isUpper()) {
274 QQmlTypeNameCache::Result r = typeNameCache->query(pathName);
275 if (r.isValid()) {
276 if (r.type.isValid()) {
277 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(e: engine);
278 QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(engine: enginePrivate);
279 if (!func) return; // Not an attachable type
280
281 currentObject = qmlAttachedPropertiesObject(currentObject, func);
282 if (!currentObject) return; // Something is broken with the attachable type
283 } else if (r.importNamespace) {
284 if ((ii + 1) == path.count()) return; // No type following the namespace
285
286 ++ii; r = typeNameCache->query(path.at(i: ii), importNamespace: r.importNamespace);
287 if (!r.type.isValid()) return; // Invalid type in namespace
288
289 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(e: engine);
290 QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(engine: enginePrivate);
291 if (!func) return; // Not an attachable type
292
293 currentObject = qmlAttachedPropertiesObject(currentObject, func);
294 if (!currentObject) return; // Something is broken with the attachable type
295
296 } else if (r.scriptIndex != -1) {
297 return; // Not a type
298 } else {
299 Q_ASSERT(!"Unreachable");
300 }
301 continue;
302 }
303
304 }
305
306 QQmlPropertyData local;
307 QQmlPropertyData *property =
308 QQmlPropertyCache::property(engine, currentObject, pathName, context, local);
309
310 if (!property) return; // Not a property
311 if (property->isFunction())
312 return; // Not an object property
313
314 if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(idx: property->propType())) {
315 // We're now at a value type property
316 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: property->propType());
317 if (!valueTypeMetaObject) return; // Not a value type
318
319 int idx = valueTypeMetaObject->indexOfProperty(name: path.last().toUtf8().constData());
320 if (idx == -1) return; // Value type property does not exist
321
322 QMetaProperty vtProp = valueTypeMetaObject->property(index: idx);
323
324 Q_ASSERT(vtProp.userType() <= 0x0000FFFF);
325 Q_ASSERT(idx <= 0x0000FFFF);
326
327 object = currentObject;
328 core = *property;
329 valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp));
330 valueTypeData.setPropType(vtProp.userType());
331 valueTypeData.setCoreIndex(idx);
332
333 return;
334 } else {
335 if (!property->isQObject()) {
336 if (auto asPropertyMap = qobject_cast<QQmlPropertyMap*>(object: currentObject))
337 currentObject = asPropertyMap->value(key: path.at(i: ii).toString()).value<QObject*>();
338 else
339 return; // Not an object property, and not a property map
340 } else {
341 property->readProperty(target: currentObject, property: &currentObject);
342 }
343
344 if (!currentObject) return; // No value
345
346 }
347
348 }
349
350 terminal = path.last();
351 }
352
353 if (terminal.count() >= 3 &&
354 terminal.at(i: 0) == QLatin1Char('o') &&
355 terminal.at(i: 1) == QLatin1Char('n') &&
356 (terminal.at(i: 2).isUpper() || terminal.at(i: 2) == '_')) {
357
358 QString signalName = terminal.mid(pos: 2).toString();
359 int firstNon_;
360 int length = signalName.length();
361 for (firstNon_ = 0; firstNon_ < length; ++firstNon_)
362 if (signalName.at(i: firstNon_) != '_')
363 break;
364 signalName[firstNon_] = signalName.at(i: firstNon_).toLower();
365
366 // XXX - this code treats methods as signals
367
368 QQmlData *ddata = QQmlData::get(object: currentObject, create: false);
369 if (ddata && ddata->propertyCache) {
370
371 // Try method
372 QQmlPropertyData *d = ddata->propertyCache->property(key: signalName, object: currentObject, context);
373 while (d && !d->isFunction())
374 d = ddata->propertyCache->overrideData(data: d);
375
376 if (d) {
377 object = currentObject;
378 core = *d;
379 return;
380 }
381
382 // Try property
383 if (signalName.endsWith(s: QLatin1String("Changed"))) {
384 const QStringRef propName = signalName.midRef(position: 0, n: signalName.length() - 7);
385 QQmlPropertyData *d = ddata->propertyCache->property(key: propName, object: currentObject, context);
386 while (d && d->isFunction())
387 d = ddata->propertyCache->overrideData(data: d);
388
389 if (d && d->notifyIndex() != -1) {
390 object = currentObject;
391 core = *ddata->propertyCache->signal(index: d->notifyIndex());
392 return;
393 }
394 }
395
396 } else {
397 QMetaMethod method = findSignalByName(mo: currentObject->metaObject(),
398 signalName.toLatin1());
399 if (method.isValid()) {
400 object = currentObject;
401 core.load(method);
402 return;
403 }
404 }
405 }
406
407 // Property
408 QQmlPropertyData local;
409 QQmlPropertyData *property =
410 QQmlPropertyCache::property(engine, currentObject, terminal, context, local);
411 if (property && !property->isFunction()) {
412 object = currentObject;
413 core = *property;
414 nameCache = terminal.toString();
415 isNameCached = true;
416 }
417}
418
419/*! \internal
420 Returns the index of this property's signal, in the signal index range
421 (see QObjectPrivate::signalIndex()). This is different from
422 QMetaMethod::methodIndex().
423*/
424int QQmlPropertyPrivate::signalIndex() const
425{
426 Q_ASSERT(type() == QQmlProperty::SignalProperty);
427 QMetaMethod m = object->metaObject()->method(index: core.coreIndex());
428 return QMetaObjectPrivate::signalIndex(m);
429}
430
431/*!
432 Create a copy of \a other.
433*/
434QQmlProperty::QQmlProperty(const QQmlProperty &other)
435{
436 d = other.d;
437 if (d)
438 d->addref();
439}
440
441/*!
442 \enum QQmlProperty::PropertyTypeCategory
443
444 This enum specifies a category of QML property.
445
446 \value InvalidCategory The property is invalid, or is a signal property.
447 \value List The property is a QQmlListProperty list property
448 \value Object The property is a QObject derived type pointer
449 \value Normal The property is a normal value property.
450 */
451
452/*!
453 \enum QQmlProperty::Type
454
455 This enum specifies a type of QML property.
456
457 \value Invalid The property is invalid.
458 \value Property The property is a regular Qt property.
459 \value SignalProperty The property is a signal property.
460*/
461
462/*!
463 Returns the property category.
464*/
465QQmlProperty::PropertyTypeCategory QQmlProperty::propertyTypeCategory() const
466{
467 return d ? d->propertyTypeCategory() : InvalidCategory;
468}
469
470QQmlProperty::PropertyTypeCategory
471QQmlPropertyPrivate::propertyTypeCategory() const
472{
473 uint type = this->type();
474
475 if (isValueType()) {
476 return QQmlProperty::Normal;
477 } else if (type & QQmlProperty::Property) {
478 int type = propertyType();
479 if (type == QMetaType::UnknownType)
480 return QQmlProperty::InvalidCategory;
481 else if (QQmlValueTypeFactory::isValueType(idx: (uint)type))
482 return QQmlProperty::Normal;
483 else if (core.isQObject())
484 return QQmlProperty::Object;
485 else if (core.isQList())
486 return QQmlProperty::List;
487 else
488 return QQmlProperty::Normal;
489 }
490
491 return QQmlProperty::InvalidCategory;
492}
493
494/*!
495 Returns the type name of the property, or 0 if the property has no type
496 name.
497*/
498const char *QQmlProperty::propertyTypeName() const
499{
500 if (!d)
501 return nullptr;
502 if (d->isValueType()) {
503 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: d->core.propType());
504 Q_ASSERT(valueTypeMetaObject);
505 return valueTypeMetaObject->property(index: d->valueTypeData.coreIndex()).typeName();
506 } else if (d->object && type() & Property && d->core.isValid()) {
507 return d->object->metaObject()->property(index: d->core.coreIndex()).typeName();
508 } else {
509 return nullptr;
510 }
511}
512
513/*!
514 Returns true if \a other and this QQmlProperty represent the same
515 property.
516*/
517bool QQmlProperty::operator==(const QQmlProperty &other) const
518{
519 if (!d || !other.d)
520 return false;
521 // category is intentially omitted here as it is generated
522 // from the other members
523 return d->object == other.d->object &&
524 d->core.coreIndex() == other.d->core.coreIndex() &&
525 d->valueTypeData.coreIndex() == other.d->valueTypeData.coreIndex();
526}
527
528/*!
529 Returns the QVariant type of the property, or QVariant::Invalid if the
530 property has no QVariant type.
531*/
532int QQmlProperty::propertyType() const
533{
534 return d ? d->propertyType() : int(QMetaType::UnknownType);
535}
536
537bool QQmlPropertyPrivate::isValueType() const
538{
539 return valueTypeData.isValid();
540}
541
542int QQmlPropertyPrivate::propertyType() const
543{
544 uint type = this->type();
545 if (isValueType()) {
546 return valueTypeData.propType();
547 } else if (type & QQmlProperty::Property) {
548 return core.propType();
549 } else {
550 return QMetaType::UnknownType;
551 }
552}
553
554QQmlProperty::Type QQmlPropertyPrivate::type() const
555{
556 if (core.isFunction())
557 return QQmlProperty::SignalProperty;
558 else if (core.isValid())
559 return QQmlProperty::Property;
560 else
561 return QQmlProperty::Invalid;
562}
563
564/*!
565 Returns the type of the property.
566*/
567QQmlProperty::Type QQmlProperty::type() const
568{
569 return d ? d->type() : Invalid;
570}
571
572/*!
573 Returns true if this QQmlProperty represents a regular Qt property.
574*/
575bool QQmlProperty::isProperty() const
576{
577 return type() & Property;
578}
579
580/*!
581 Returns true if this QQmlProperty represents a QML signal property.
582*/
583bool QQmlProperty::isSignalProperty() const
584{
585 return type() & SignalProperty;
586}
587
588/*!
589 Returns the QQmlProperty's QObject.
590*/
591QObject *QQmlProperty::object() const
592{
593 return d ? d->object : nullptr;
594}
595
596/*!
597 Assign \a other to this QQmlProperty.
598*/
599QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other)
600{
601 QQmlProperty copied(other);
602 qSwap(value1&: d, value2&: copied.d);
603 return *this;
604}
605
606/*!
607 Returns true if the property is writable, otherwise false.
608*/
609bool QQmlProperty::isWritable() const
610{
611 if (!d)
612 return false;
613 if (!d->object)
614 return false;
615 if (d->core.isQList()) //list
616 return true;
617 else if (d->core.isFunction()) //signal handler
618 return false;
619 else if (d->core.isValid()) //normal property
620 return d->core.isWritable();
621 else
622 return false;
623}
624
625/*!
626 Returns true if the property is designable, otherwise false.
627*/
628bool QQmlProperty::isDesignable() const
629{
630 if (!d)
631 return false;
632 if (type() & Property && d->core.isValid() && d->object)
633 return d->object->metaObject()->property(index: d->core.coreIndex()).isDesignable();
634 else
635 return false;
636}
637
638/*!
639 Returns true if the property is resettable, otherwise false.
640*/
641bool QQmlProperty::isResettable() const
642{
643 if (!d)
644 return false;
645 if (type() & Property && d->core.isValid() && d->object)
646 return d->core.isResettable();
647 else
648 return false;
649}
650
651/*!
652 Returns true if the QQmlProperty refers to a valid property, otherwise
653 false.
654*/
655bool QQmlProperty::isValid() const
656{
657 if (!d)
658 return false;
659 return type() != Invalid;
660}
661
662/*!
663 Return the name of this QML property.
664*/
665QString QQmlProperty::name() const
666{
667 if (!d)
668 return QString();
669 if (!d->isNameCached) {
670 // ###
671 if (!d->object) {
672 } else if (d->isValueType()) {
673 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: d->core.propType());
674 Q_ASSERT(valueTypeMetaObject);
675
676 const char *vtName = valueTypeMetaObject->property(index: d->valueTypeData.coreIndex()).name();
677 d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(str: vtName);
678 } else if (type() & SignalProperty) {
679 QString name = QLatin1String("on") + d->core.name(d->object);
680 name[2] = name.at(i: 2).toUpper();
681 d->nameCache = name;
682 } else {
683 d->nameCache = d->core.name(d->object);
684 }
685 d->isNameCached = true;
686 }
687
688 return d->nameCache;
689}
690
691/*!
692 Returns the \l{QMetaProperty} {Qt property} associated with
693 this QML property.
694 */
695QMetaProperty QQmlProperty::property() const
696{
697 if (!d)
698 return QMetaProperty();
699 if (type() & Property && d->core.isValid() && d->object)
700 return d->object->metaObject()->property(index: d->core.coreIndex());
701 else
702 return QMetaProperty();
703}
704
705/*!
706 Return the QMetaMethod for this property if it is a SignalProperty,
707 otherwise returns an invalid QMetaMethod.
708*/
709QMetaMethod QQmlProperty::method() const
710{
711 if (!d)
712 return QMetaMethod();
713 if (type() & SignalProperty && d->object)
714 return d->object->metaObject()->method(index: d->core.coreIndex());
715 else
716 return QMetaMethod();
717}
718
719/*!
720 Returns the binding associated with this property, or 0 if no binding
721 exists.
722*/
723QQmlAbstractBinding *
724QQmlPropertyPrivate::binding(const QQmlProperty &that)
725{
726 if (!that.d || !that.isProperty() || !that.d->object)
727 return nullptr;
728
729 QQmlPropertyIndex thatIndex(that.d->core.coreIndex(), that.d->valueTypeData.coreIndex());
730 return binding(that.d->object, index: thatIndex);
731}
732
733/*!
734 Set the binding associated with this property to \a newBinding. Returns
735 the existing binding (if any), otherwise 0.
736
737 \a newBinding will be enabled, and the returned binding (if any) will be
738 disabled.
739
740 Ownership of \a newBinding transfers to QML. Ownership of the return value
741 is assumed by the caller.
742
743 \a flags is passed through to the binding and is used for the initial update (when
744 the binding sets the initial value, it will use these flags for the write).
745*/
746void
747QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QQmlAbstractBinding *newBinding)
748{
749 if (!newBinding) {
750 removeBinding(that);
751 return;
752 }
753
754 if (!that.d || !that.isProperty() || !that.d->object) {
755 if (!newBinding->ref)
756 delete newBinding;
757 return;
758 }
759 setBinding(binding: newBinding);
760}
761
762static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None)
763{
764 int coreIndex = index.coreIndex();
765 int valueTypeIndex = index.valueTypeIndex();
766
767 QQmlData *data = QQmlData::get(object, create: false);
768
769 if (!data || !data->hasBindingBit(coreIndex))
770 return;
771
772 QQmlAbstractBinding::Ptr oldBinding;
773 oldBinding = data->bindings;
774
775 while (oldBinding && (oldBinding->targetPropertyIndex().coreIndex() != coreIndex ||
776 oldBinding->targetPropertyIndex().hasValueTypeIndex()))
777 oldBinding = oldBinding->nextBinding();
778
779 if (!oldBinding)
780 return;
781
782 if (valueTypeIndex != -1 && oldBinding->isValueTypeProxy())
783 oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(targetPropertyIndex: index);
784
785 if (!oldBinding)
786 return;
787
788 if (!(flags & QQmlPropertyPrivate::DontEnable))
789 oldBinding->setEnabled(e: false, f: {});
790 oldBinding->removeFromObject();
791}
792
793void QQmlPropertyPrivate::removeBinding(QQmlAbstractBinding *b)
794{
795 removeBinding(o: b->targetObject(), index: b->targetPropertyIndex());
796}
797
798void QQmlPropertyPrivate::removeBinding(QObject *o, QQmlPropertyIndex index)
799{
800 Q_ASSERT(o);
801
802 QObject *target;
803 QQmlPropertyIndex targetIndex;
804 findAliasTarget(o, index, &target, &targetIndex);
805 removeOldBinding(object: target, index: targetIndex);
806}
807
808void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that)
809{
810 if (!that.d || !that.isProperty() || !that.d->object)
811 return;
812
813 removeBinding(o: that.d->object, index: that.d->encodedIndex());
814}
815
816QQmlAbstractBinding *
817QQmlPropertyPrivate::binding(QObject *object, QQmlPropertyIndex index)
818{
819 findAliasTarget(object, index, &object, &index);
820
821 QQmlData *data = QQmlData::get(object);
822 if (!data)
823 return nullptr;
824
825 const int coreIndex = index.coreIndex();
826 const int valueTypeIndex = index.valueTypeIndex();
827
828 if (coreIndex < 0 || !data->hasBindingBit(coreIndex))
829 return nullptr;
830
831 QQmlAbstractBinding *binding = data->bindings;
832 while (binding && (binding->targetPropertyIndex().coreIndex() != coreIndex ||
833 binding->targetPropertyIndex().hasValueTypeIndex()))
834 binding = binding->nextBinding();
835
836 if (binding && valueTypeIndex != -1) {
837 if (binding->isValueTypeProxy()) {
838 binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(targetPropertyIndex: index);
839 }
840 }
841
842 return binding;
843}
844
845void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bindingIndex,
846 QObject **targetObject,
847 QQmlPropertyIndex *targetBindingIndex)
848{
849 QQmlData *data = QQmlData::get(object, create: false);
850 if (data) {
851 int coreIndex = bindingIndex.coreIndex();
852 int valueTypeIndex = bindingIndex.valueTypeIndex();
853
854 QQmlPropertyData *propertyData =
855 data->propertyCache?data->propertyCache->property(index: coreIndex):nullptr;
856 if (propertyData && propertyData->isAlias()) {
857 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(o: object, coreIndex);
858
859 QObject *aObject = nullptr; int aCoreIndex = -1; int aValueTypeIndex = -1;
860 if (vme->aliasTarget(index: coreIndex, target: &aObject, coreIndex: &aCoreIndex, valueTypeIndex: &aValueTypeIndex)) {
861 // This will either be a value type sub-reference or an alias to a value-type sub-reference not both
862 Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
863
864 QQmlPropertyIndex aBindingIndex(aCoreIndex);
865 if (aValueTypeIndex != -1) {
866 aBindingIndex = QQmlPropertyIndex(aCoreIndex, aValueTypeIndex);
867 } else if (valueTypeIndex != -1) {
868 aBindingIndex = QQmlPropertyIndex(aCoreIndex, valueTypeIndex);
869 }
870
871 findAliasTarget(object: aObject, bindingIndex: aBindingIndex, targetObject, targetBindingIndex);
872 return;
873 }
874 }
875 }
876
877 *targetObject = object;
878 *targetBindingIndex = bindingIndex;
879}
880
881
882void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags)
883{
884 Q_ASSERT(binding);
885 Q_ASSERT(binding->targetObject());
886
887 QObject *object = binding->targetObject();
888 const QQmlPropertyIndex index = binding->targetPropertyIndex();
889
890#ifndef QT_NO_DEBUG
891 int coreIndex = index.coreIndex();
892 QQmlData *data = QQmlData::get(object, create: true);
893 if (data->propertyCache) {
894 QQmlPropertyData *propertyData = data->propertyCache->property(index: coreIndex);
895 Q_ASSERT(propertyData);
896 }
897#endif
898
899 removeOldBinding(object, index, flags);
900
901 binding->addToObject();
902 if (!(flags & DontEnable))
903 binding->setEnabled(e: true, f: writeFlags);
904
905}
906
907/*!
908 Returns the expression associated with this signal property, or 0 if no
909 signal expression exists.
910*/
911QQmlBoundSignalExpression *
912QQmlPropertyPrivate::signalExpression(const QQmlProperty &that)
913{
914 if (!(that.type() & QQmlProperty::SignalProperty))
915 return nullptr;
916
917 if (!that.d->object)
918 return nullptr;
919 QQmlData *data = QQmlData::get(object: that.d->object);
920 if (!data)
921 return nullptr;
922
923 QQmlBoundSignal *signalHandler = data->signalHandlers;
924
925 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(p: that)->signalIndex())
926 signalHandler = signalHandler->m_nextSignal;
927
928 if (signalHandler)
929 return signalHandler->expression();
930
931 return nullptr;
932}
933
934/*!
935 Set the signal expression associated with this signal property to \a expr.
936 A reference to \a expr will be added by QML.
937*/
938void QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr)
939{
940 if (expr)
941 expr->addref();
942 QQmlPropertyPrivate::takeSignalExpression(that, expr);
943}
944
945/*!
946 Set the signal expression associated with this signal property to \a expr.
947 Ownership of \a expr transfers to QML.
948*/
949void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that,
950 QQmlBoundSignalExpression *expr)
951{
952 if (!(that.type() & QQmlProperty::SignalProperty)) {
953 if (expr)
954 expr->release();
955 return;
956 }
957
958 if (!that.d->object)
959 return;
960 QQmlData *data = QQmlData::get(object: that.d->object, create: nullptr != expr);
961 if (!data)
962 return;
963
964 QQmlBoundSignal *signalHandler = data->signalHandlers;
965
966 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(p: that)->signalIndex())
967 signalHandler = signalHandler->m_nextSignal;
968
969 if (signalHandler) {
970 signalHandler->takeExpression(expr);
971 return;
972 }
973
974 if (expr) {
975 int signalIndex = QQmlPropertyPrivate::get(p: that)->signalIndex();
976 QQmlBoundSignal *signal = new QQmlBoundSignal(that.d->object, signalIndex, that.d->object,
977 expr->context()->engine);
978 signal->takeExpression(expr);
979 }
980}
981
982/*!
983 Returns the property value.
984*/
985QVariant QQmlProperty::read() const
986{
987 if (!d)
988 return QVariant();
989 if (!d->object)
990 return QVariant();
991
992 if (type() & SignalProperty) {
993
994 return QVariant();
995
996 } else if (type() & Property) {
997
998 return d->readValueProperty();
999
1000 }
1001 return QVariant();
1002}
1003
1004/*!
1005Return the \a name property value of \a object. This method is equivalent to:
1006\code
1007 QQmlProperty p(object, name);
1008 p.read();
1009\endcode
1010*/
1011QVariant QQmlProperty::read(const QObject *object, const QString &name)
1012{
1013 QQmlProperty p(const_cast<QObject *>(object), name);
1014 return p.read();
1015}
1016
1017/*!
1018 Return the \a name property value of \a object using the
1019 \l{QQmlContext} {context} \a ctxt. This method is
1020 equivalent to:
1021
1022 \code
1023 QQmlProperty p(object, name, context);
1024 p.read();
1025 \endcode
1026*/
1027QVariant QQmlProperty::read(const QObject *object, const QString &name, QQmlContext *ctxt)
1028{
1029 QQmlProperty p(const_cast<QObject *>(object), name, ctxt);
1030 return p.read();
1031}
1032
1033/*!
1034
1035 Return the \a name property value of \a object using the environment
1036 for instantiating QML components that is provided by \a engine. .
1037 This method is equivalent to:
1038
1039 \code
1040 QQmlProperty p(object, name, engine);
1041 p.read();
1042 \endcode
1043*/
1044QVariant QQmlProperty::read(const QObject *object, const QString &name, QQmlEngine *engine)
1045{
1046 QQmlProperty p(const_cast<QObject *>(object), name, engine);
1047 return p.read();
1048}
1049
1050QVariant QQmlPropertyPrivate::readValueProperty()
1051{
1052 auto doRead = [&](QQmlGadgetPtrWrapper *wrapper) {
1053 wrapper->read(obj: object, idx: core.coreIndex());
1054 return wrapper->property(index: valueTypeData.coreIndex()).read(obj: wrapper);
1055 };
1056
1057 if (isValueType()) {
1058 if (QQmlGadgetPtrWrapper *wrapper = QQmlGadgetPtrWrapper::instance(engine, index: core.propType()))
1059 return doRead(wrapper);
1060 if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(idx: core.propType())) {
1061 QQmlGadgetPtrWrapper wrapper(valueType, nullptr);
1062 return doRead(&wrapper);
1063 }
1064 return QVariant();
1065 } else if (core.isQList()) {
1066
1067 QQmlListProperty<QObject> prop;
1068 core.readProperty(target: object, property: &prop);
1069 return QVariant::fromValue(value: QQmlListReferencePrivate::init(prop, core.propType(), engine));
1070
1071 } else if (core.isQObject()) {
1072
1073 QObject *rv = nullptr;
1074 core.readProperty(target: object, property: &rv);
1075 return QVariant::fromValue(value: rv);
1076
1077 } else {
1078
1079 if (!core.propType()) // Unregistered type
1080 return object->metaObject()->property(index: core.coreIndex()).read(obj: object);
1081
1082 QVariant value;
1083 int status = -1;
1084 void *args[] = { nullptr, &value, &status };
1085 if (core.propType() == QMetaType::QVariant) {
1086 args[0] = &value;
1087 } else {
1088 value = QVariant(core.propType(), (void*)nullptr);
1089 args[0] = value.data();
1090 }
1091 core.readPropertyWithArgs(target: object, args);
1092 if (core.propType() != QMetaType::QVariant && args[0] != value.data())
1093 return QVariant((QVariant::Type)core.propType(), args[0]);
1094
1095 return value;
1096 }
1097}
1098
1099// helper function to allow assignment / binding to QList<QUrl> properties.
1100QVariant QQmlPropertyPrivate::resolvedUrlSequence(const QVariant &value, QQmlContextData *context)
1101{
1102 QList<QUrl> urls;
1103 if (value.userType() == qMetaTypeId<QUrl>()) {
1104 urls.append(t: value.toUrl());
1105 } else if (value.userType() == qMetaTypeId<QString>()) {
1106 urls.append(t: QUrl(value.toString()));
1107 } else if (value.userType() == qMetaTypeId<QByteArray>()) {
1108 urls.append(t: QUrl(QString::fromUtf8(str: value.toByteArray())));
1109 } else if (value.userType() == qMetaTypeId<QList<QUrl> >()) {
1110 urls = value.value<QList<QUrl> >();
1111 } else if (value.userType() == qMetaTypeId<QStringList>()) {
1112 QStringList urlStrings = value.value<QStringList>();
1113 const int urlStringsSize = urlStrings.size();
1114 urls.reserve(alloc: urlStringsSize);
1115 for (int i = 0; i < urlStringsSize; ++i)
1116 urls.append(t: QUrl(urlStrings.at(i)));
1117 } else if (value.userType() == qMetaTypeId<QList<QString> >()) {
1118 QList<QString> urlStrings = value.value<QList<QString> >();
1119 const int urlStringsSize = urlStrings.size();
1120 urls.reserve(alloc: urlStringsSize);
1121 for (int i = 0; i < urlStringsSize; ++i)
1122 urls.append(t: QUrl(urlStrings.at(i)));
1123 } // note: QList<QByteArray> is not currently supported.
1124
1125 QList<QUrl> resolvedUrls;
1126 const int urlsSize = urls.size();
1127 resolvedUrls.reserve(alloc: urlsSize);
1128 for (int i = 0; i < urlsSize; ++i) {
1129 QUrl u = urls.at(i);
1130 if (context && u.isRelative() && !u.isEmpty())
1131 u = context->resolvedUrl(u);
1132 resolvedUrls.append(t: u);
1133 }
1134
1135 return QVariant::fromValue<QList<QUrl> >(value: resolvedUrls);
1136}
1137
1138//writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC!
1139bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags)
1140{
1141 if (!object || !prop.isWritable())
1142 return false;
1143
1144 QVariant v = value;
1145 if (prop.isEnumType()) {
1146 QMetaEnum menum = prop.enumerator();
1147 if (v.userType() == QMetaType::QString
1148#ifdef QT3_SUPPORT
1149 || v.userType() == QVariant::CString
1150#endif
1151 ) {
1152 bool ok;
1153 if (prop.isFlagType())
1154 v = QVariant(menum.keysToValue(keys: value.toByteArray(), ok: &ok));
1155 else
1156 v = QVariant(menum.keyToValue(key: value.toByteArray(), ok: &ok));
1157 if (!ok)
1158 return false;
1159 } else if (v.userType() != QMetaType::Int && v.userType() != QMetaType::UInt) {
1160 int enumMetaTypeId = QMetaType::type(typeName: QByteArray(menum.scope() + QByteArray("::") + menum.name()));
1161 if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData())
1162 return false;
1163 v = QVariant(*reinterpret_cast<const int *>(v.constData()));
1164 }
1165 v.convert(targetTypeId: QMetaType::Int);
1166 }
1167
1168 // the status variable is changed by qt_metacall to indicate what it did
1169 // this feature is currently only used by QtDBus and should not be depended
1170 // upon. Don't change it without looking into QDBusAbstractInterface first
1171 // -1 (unchanged): normal qt_metacall, result stored in argv[0]
1172 // changed: result stored directly in value, return the value of status
1173 int status = -1;
1174 void *argv[] = { v.data(), &v, &status, &flags };
1175 QMetaObject::metacall(object, QMetaObject::WriteProperty, idx, argv);
1176 return status;
1177}
1178
1179bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags)
1180{
1181 return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags);
1182}
1183
1184bool
1185QQmlPropertyPrivate::writeValueProperty(QObject *object,
1186 const QQmlPropertyData &core,
1187 const QQmlPropertyData &valueTypeData,
1188 const QVariant &value,
1189 QQmlContextData *context,QQmlPropertyData::WriteFlags flags)
1190{
1191 // Remove any existing bindings on this property
1192 if (!(flags & QQmlPropertyData::DontRemoveBinding) && object)
1193 removeBinding(o: object, index: encodedIndex(core, valueTypeData));
1194
1195 bool rv = false;
1196 if (valueTypeData.isValid()) {
1197 auto doWrite = [&](QQmlGadgetPtrWrapper *wrapper) {
1198 wrapper->read(obj: object, idx: core.coreIndex());
1199 rv = write(wrapper, valueTypeData, value, context, flags);
1200 wrapper->write(obj: object, idx: core.coreIndex(), flags);
1201 };
1202
1203 QQmlGadgetPtrWrapper *wrapper = context
1204 ? QQmlGadgetPtrWrapper::instance(engine: context->engine, index: core.propType())
1205 : nullptr;
1206 if (wrapper) {
1207 doWrite(wrapper);
1208 } else if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(idx: core.propType())) {
1209 QQmlGadgetPtrWrapper wrapper(valueType, nullptr);
1210 doWrite(&wrapper);
1211 }
1212
1213 } else {
1214 rv = write(object, core, value, context, flags);
1215 }
1216
1217 return rv;
1218}
1219
1220bool QQmlPropertyPrivate::write(QObject *object,
1221 const QQmlPropertyData &property,
1222 const QVariant &value, QQmlContextData *context,
1223 QQmlPropertyData::WriteFlags flags)
1224{
1225 const int propertyType = property.propType();
1226 const int variantType = value.userType();
1227
1228 if (property.isEnum()) {
1229 QMetaProperty prop = object->metaObject()->property(index: property.coreIndex());
1230 QVariant v = value;
1231 // Enum values come through the script engine as doubles
1232 if (variantType == QMetaType::Double) {
1233 double integral;
1234 double fractional = std::modf(x: value.toDouble(), iptr: &integral);
1235 if (qFuzzyIsNull(d: fractional))
1236 v.convert(targetTypeId: QMetaType::Int);
1237 }
1238 return writeEnumProperty(prop, idx: property.coreIndex(), object, value: v, flags);
1239 }
1240
1241 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(c: context);
1242 const bool isUrl = propertyType == QMetaType::QUrl; // handled separately
1243
1244 // The cases below are in approximate order of likelyhood:
1245 if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
1246 return property.writeProperty(target: object, value: const_cast<void *>(value.constData()), flags);
1247 } else if (property.isQObject()) {
1248 QVariant val = value;
1249 int varType = variantType;
1250 if (variantType == QMetaType::Nullptr) {
1251 // This reflects the fact that you can assign a nullptr to a QObject pointer
1252 // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject
1253 varType = QMetaType::QObjectStar;
1254 val = QVariant(QMetaType::QObjectStar, nullptr);
1255 }
1256 QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType);
1257 if (valMo.isNull())
1258 return false;
1259 QObject *o = *static_cast<QObject *const *>(val.constData());
1260 QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType);
1261
1262 if (o)
1263 valMo = o;
1264
1265 if (QQmlMetaObject::canConvert(from: valMo, to: propMo)) {
1266 return property.writeProperty(target: object, value: &o, flags);
1267 } else if (!o && QQmlMetaObject::canConvert(from: propMo, to: valMo)) {
1268 // In the case of a null QObject, we assign the null if there is
1269 // any change that the null variant type could be up or down cast to
1270 // the property type.
1271 return property.writeProperty(target: object, value: &o, flags);
1272 } else {
1273 return false;
1274 }
1275 } else if (value.canConvert(targetTypeId: propertyType) && !isUrl && variantType != QMetaType::QString && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
1276 // common cases:
1277 switch (propertyType) {
1278 case QMetaType::Bool: {
1279 bool b = value.toBool();
1280 return property.writeProperty(target: object, value: &b, flags);
1281 }
1282 case QMetaType::Int: {
1283 int i = value.toInt();
1284 return property.writeProperty(target: object, value: &i, flags);
1285 }
1286 case QMetaType::Double: {
1287 double d = value.toDouble();
1288 return property.writeProperty(target: object, value: &d, flags);
1289 }
1290 case QMetaType::Float: {
1291 float f = value.toFloat();
1292 return property.writeProperty(target: object, value: &f, flags);
1293 }
1294 case QMetaType::QString: {
1295 QString s = value.toString();
1296 return property.writeProperty(target: object, value: &s, flags);
1297 }
1298 default: { // "fallback":
1299 QVariant v = value;
1300 v.convert(targetTypeId: propertyType);
1301 return property.writeProperty(target: object, value: const_cast<void *>(v.constData()), flags);
1302 }
1303 }
1304 } else if (propertyType == qMetaTypeId<QVariant>()) {
1305 return property.writeProperty(target: object, value: const_cast<QVariant *>(&value), flags);
1306 } else if (isUrl) {
1307 QUrl u;
1308 if (variantType == QMetaType::QUrl) {
1309 u = value.toUrl();
1310 } else if (variantType == QMetaType::QByteArray) {
1311 QString input(QString::fromUtf8(str: value.toByteArray()));
1312 // Encoded dir-separators defeat QUrl processing - decode them first
1313 input.replace(before: QLatin1String("%2f"), after: QLatin1String("/"), cs: Qt::CaseInsensitive);
1314 u = QUrl(input);
1315 } else if (variantType == QMetaType::QString) {
1316 QString input(value.toString());
1317 // Encoded dir-separators defeat QUrl processing - decode them first
1318 input.replace(before: QLatin1String("%2f"), after: QLatin1String("/"), cs: Qt::CaseInsensitive);
1319 u = QUrl(input);
1320 } else {
1321 return false;
1322 }
1323
1324 if (context && u.isRelative() && !u.isEmpty())
1325 u = context->resolvedUrl(u);
1326 return property.writeProperty(target: object, value: &u, flags);
1327 } else if (propertyType == qMetaTypeId<QList<QUrl>>()) {
1328 QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl>>();
1329 return property.writeProperty(target: object, value: &urlSeq, flags);
1330 } else if (property.isQList()) {
1331 QQmlMetaObject listType;
1332
1333 if (enginePriv) {
1334 listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType()));
1335 } else {
1336 QQmlType type = QQmlMetaType::qmlType(typeId: QQmlMetaType::listType(property.propType()));
1337 if (!type.isValid())
1338 return false;
1339 listType = type.baseMetaObject();
1340 }
1341 if (listType.isNull())
1342 return false;
1343
1344 QQmlListProperty<void> prop;
1345 property.readProperty(target: object, property: &prop);
1346
1347 if (!prop.clear)
1348 return false;
1349
1350 prop.clear(&prop);
1351
1352 if (variantType == qMetaTypeId<QQmlListReference>()) {
1353 QQmlListReference qdlr = value.value<QQmlListReference>();
1354
1355 for (int ii = 0; ii < qdlr.count(); ++ii) {
1356 QObject *o = qdlr.at(ii);
1357 if (o && !QQmlMetaObject::canConvert(from: o, to: listType))
1358 o = nullptr;
1359 prop.append(&prop, o);
1360 }
1361 } else if (variantType == qMetaTypeId<QList<QObject *> >()) {
1362 const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(v: value);
1363
1364 for (int ii = 0; ii < list.count(); ++ii) {
1365 QObject *o = list.at(i: ii);
1366 if (o && !QQmlMetaObject::canConvert(from: o, to: listType))
1367 o = nullptr;
1368 prop.append(&prop, o);
1369 }
1370 } else {
1371 QObject *o = enginePriv?enginePriv->toQObject(value):QQmlMetaType::toQObject(value);
1372 if (o && !QQmlMetaObject::canConvert(from: o, to: listType))
1373 o = nullptr;
1374 prop.append(&prop, o);
1375 }
1376 } else {
1377 Q_ASSERT(variantType != propertyType);
1378
1379 bool ok = false;
1380 QVariant v;
1381 if (variantType == QMetaType::QString)
1382 v = QQmlStringConverters::variantFromString(value.toString(), preferredType: propertyType, ok: &ok);
1383
1384 if (!ok) {
1385 v = value;
1386 if (v.convert(targetTypeId: propertyType)) {
1387 ok = true;
1388 } else if (v.isValid() && value.isNull()) {
1389 // For historical reasons converting a null QVariant to another type will do the trick
1390 // but return false anyway. This is caught with the above condition and considered a
1391 // successful conversion.
1392 Q_ASSERT(v.userType() == propertyType);
1393 ok = true;
1394 } else if (static_cast<uint>(propertyType) >= QMetaType::User &&
1395 variantType == QMetaType::QString) {
1396 QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType);
1397 if (con) {
1398 v = con(value.toString());
1399 if (v.userType() == propertyType)
1400 ok = true;
1401 }
1402 }
1403 }
1404 if (!ok) {
1405 // the only other options are that they are assigning a single value
1406 // to a sequence type property (eg, an int to a QList<int> property).
1407 // or that we encountered an interface type
1408 // Note that we've already handled single-value assignment to QList<QUrl> properties.
1409 if (variantType == QMetaType::Int && propertyType == qMetaTypeId<QList<int> >()) {
1410 QList<int> list;
1411 list << value.toInt();
1412 v = QVariant::fromValue<QList<int> >(value: list);
1413 ok = true;
1414 } else if ((variantType == QMetaType::Double || variantType == QMetaType::Int)
1415 && (propertyType == qMetaTypeId<QList<qreal> >())) {
1416 QList<qreal> list;
1417 list << value.toReal();
1418 v = QVariant::fromValue<QList<qreal> >(value: list);
1419 ok = true;
1420 } else if (variantType == QMetaType::Bool && propertyType == qMetaTypeId<QList<bool> >()) {
1421 QList<bool> list;
1422 list << value.toBool();
1423 v = QVariant::fromValue<QList<bool> >(value: list);
1424 ok = true;
1425 } else if (variantType == QMetaType::QString && propertyType == qMetaTypeId<QList<QString> >()) {
1426 QList<QString> list;
1427 list << value.toString();
1428 v = QVariant::fromValue<QList<QString> >(value: list);
1429 ok = true;
1430 } else if (variantType == QMetaType::QString && propertyType == qMetaTypeId<QStringList>()) {
1431 QStringList list;
1432 list << value.toString();
1433 v = QVariant::fromValue<QStringList>(value: list);
1434 ok = true;
1435 }
1436 }
1437
1438 if (!ok && QQmlMetaType::isInterface(propertyType)) {
1439 auto valueAsQObject = qvariant_cast<QObject *>(v: value);
1440 if (valueAsQObject && valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyType))) {
1441 // this case can occur when object has an interface type
1442 // and the variant contains a type implementing the interface
1443 return property.writeProperty(target: object, value: const_cast<void *>(value.constData()), flags);
1444 }
1445 }
1446
1447 if (ok) {
1448 return property.writeProperty(target: object, value: const_cast<void *>(v.constData()), flags);
1449 } else {
1450 return false;
1451 }
1452 }
1453
1454 return true;
1455}
1456
1457QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engine, int userType)
1458{
1459 QMetaType metaType(userType);
1460 if ((metaType.flags() & QMetaType::PointerToQObject) && metaType.metaObject())
1461 return metaType.metaObject();
1462 if (engine)
1463 return engine->rawMetaObjectForType(userType);
1464 QQmlType type = QQmlMetaType::qmlType(typeId: userType);
1465 if (type.isValid())
1466 return QQmlMetaObject(type.baseMetaObject());
1467 return QQmlMetaObject();
1468}
1469
1470/*!
1471 Sets the property value to \a value. Returns \c true on success, or
1472 \c false if the property can't be set because the \a value is the
1473 wrong type, for example.
1474 */
1475bool QQmlProperty::write(const QVariant &value) const
1476{
1477 return QQmlPropertyPrivate::write(that: *this, value, {});
1478}
1479
1480/*!
1481 Writes \a value to the \a name property of \a object. This method
1482 is equivalent to:
1483
1484 \code
1485 QQmlProperty p(object, name);
1486 p.write(value);
1487 \endcode
1488
1489 Returns \c true on success, \c false otherwise.
1490*/
1491bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value)
1492{
1493 QQmlProperty p(object, name);
1494 return p.write(value);
1495}
1496
1497/*!
1498 Writes \a value to the \a name property of \a object using the
1499 \l{QQmlContext} {context} \a ctxt. This method is
1500 equivalent to:
1501
1502 \code
1503 QQmlProperty p(object, name, ctxt);
1504 p.write(value);
1505 \endcode
1506
1507 Returns \c true on success, \c false otherwise.
1508*/
1509bool QQmlProperty::write(QObject *object,
1510 const QString &name,
1511 const QVariant &value,
1512 QQmlContext *ctxt)
1513{
1514 QQmlProperty p(object, name, ctxt);
1515 return p.write(value);
1516}
1517
1518/*!
1519
1520 Writes \a value to the \a name property of \a object using the
1521 environment for instantiating QML components that is provided by
1522 \a engine. This method is equivalent to:
1523
1524 \code
1525 QQmlProperty p(object, name, engine);
1526 p.write(value);
1527 \endcode
1528
1529 Returns \c true on success, \c false otherwise.
1530*/
1531bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value,
1532 QQmlEngine *engine)
1533{
1534 QQmlProperty p(object, name, engine);
1535 return p.write(value);
1536}
1537
1538/*!
1539 Resets the property and returns true if the property is
1540 resettable. If the property is not resettable, nothing happens
1541 and false is returned.
1542*/
1543bool QQmlProperty::reset() const
1544{
1545 if (isResettable()) {
1546 void *args[] = { nullptr };
1547 QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex(), args);
1548 return true;
1549 } else {
1550 return false;
1551 }
1552}
1553
1554bool QQmlPropertyPrivate::write(const QQmlProperty &that,
1555 const QVariant &value, QQmlPropertyData::WriteFlags flags)
1556{
1557 if (!that.d)
1558 return false;
1559 if (that.d->object && that.type() & QQmlProperty::Property &&
1560 that.d->core.isValid() && that.isWritable())
1561 return that.d->writeValueProperty(value, flags);
1562 else
1563 return false;
1564}
1565
1566/*!
1567 Returns true if the property has a change notifier signal, otherwise false.
1568*/
1569bool QQmlProperty::hasNotifySignal() const
1570{
1571 if (type() & Property && d->object) {
1572 return d->object->metaObject()->property(index: d->core.coreIndex()).hasNotifySignal();
1573 }
1574 return false;
1575}
1576
1577/*!
1578 Returns true if the property needs a change notifier signal for bindings
1579 to remain upto date, false otherwise.
1580
1581 Some properties, such as attached properties or those whose value never
1582 changes, do not require a change notifier.
1583*/
1584bool QQmlProperty::needsNotifySignal() const
1585{
1586 return type() & Property && !property().isConstant();
1587}
1588
1589/*!
1590 Connects the property's change notifier signal to the
1591 specified \a method of the \a dest object and returns
1592 true. Returns false if this metaproperty does not
1593 represent a regular Qt property or if it has no
1594 change notifier signal, or if the \a dest object does
1595 not have the specified \a method.
1596*/
1597bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const
1598{
1599 if (!(type() & Property) || !d->object)
1600 return false;
1601
1602 QMetaProperty prop = d->object->metaObject()->property(index: d->core.coreIndex());
1603 if (prop.hasNotifySignal()) {
1604 return QQmlPropertyPrivate::connect(sender: d->object, signal_index: prop.notifySignalIndex(), receiver: dest, method_index: method, type: Qt::DirectConnection);
1605 } else {
1606 return false;
1607 }
1608}
1609
1610/*!
1611 Connects the property's change notifier signal to the
1612 specified \a slot of the \a dest object and returns
1613 true. Returns false if this metaproperty does not
1614 represent a regular Qt property or if it has no
1615 change notifier signal, or if the \a dest object does
1616 not have the specified \a slot.
1617
1618 \note \a slot should be passed using the SLOT() macro so it is
1619 correctly identified.
1620*/
1621bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const
1622{
1623 if (!(type() & Property) || !d->object)
1624 return false;
1625
1626 QMetaProperty prop = d->object->metaObject()->property(index: d->core.coreIndex());
1627 if (prop.hasNotifySignal()) {
1628 QByteArray signal('2' + prop.notifySignal().methodSignature());
1629 return QObject::connect(sender: d->object, signal: signal.constData(), receiver: dest, member: slot);
1630 } else {
1631 return false;
1632 }
1633}
1634
1635/*!
1636 Return the Qt metaobject index of the property.
1637*/
1638int QQmlProperty::index() const
1639{
1640 return d ? d->core.coreIndex() : -1;
1641}
1642
1643QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that)
1644{
1645 return that.d ? that.d->encodedIndex() : QQmlPropertyIndex();
1646}
1647
1648QQmlProperty
1649QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data,
1650 const QQmlPropertyData *valueTypeData, QQmlContextData *ctxt)
1651{
1652 QQmlProperty prop;
1653
1654 prop.d = new QQmlPropertyPrivate;
1655 prop.d->object = object;
1656 prop.d->context = ctxt;
1657 prop.d->engine = ctxt ? ctxt->engine : nullptr;
1658
1659 prop.d->core = data;
1660 if (valueTypeData)
1661 prop.d->valueTypeData = *valueTypeData;
1662
1663 return prop;
1664}
1665
1666/*!
1667 Return the signal corresponding to \a name
1668*/
1669QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const QByteArray &name)
1670{
1671 Q_ASSERT(mo);
1672 int methods = mo->methodCount();
1673 for (int ii = methods - 1; ii >= 2; --ii) { // >= 2 to block the destroyed signal
1674 QMetaMethod method = mo->method(index: ii);
1675
1676 if (method.name() == name && (method.methodType() & QMetaMethod::Signal))
1677 return method;
1678 }
1679
1680 // If no signal is found, but the signal is of the form "onBlahChanged",
1681 // return the notify signal for the property "Blah"
1682 if (name.endsWith(c: "Changed")) {
1683 QByteArray propName = name.mid(index: 0, len: name.length() - 7);
1684 int propIdx = mo->indexOfProperty(name: propName.constData());
1685 if (propIdx >= 0) {
1686 QMetaProperty prop = mo->property(index: propIdx);
1687 if (prop.hasNotifySignal())
1688 return prop.notifySignal();
1689 }
1690 }
1691
1692 return QMetaMethod();
1693}
1694
1695/*! \internal
1696 If \a indexInSignalRange is true, \a index is treated as a signal index
1697 (see QObjectPrivate::signalIndex()), otherwise it is treated as a
1698 method index (QMetaMethod::methodIndex()).
1699*/
1700static inline void flush_vme_signal(const QObject *object, int index, bool indexInSignalRange)
1701{
1702 QQmlData *data = QQmlData::get(object);
1703 if (data && data->propertyCache) {
1704 QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index)
1705 : data->propertyCache->method(index);
1706
1707 if (property && property->isVMESignal()) {
1708 QQmlVMEMetaObject *vme;
1709 if (indexInSignalRange)
1710 vme = QQmlVMEMetaObject::getForSignal(o: const_cast<QObject *>(object), coreIndex: index);
1711 else
1712 vme = QQmlVMEMetaObject::getForMethod(o: const_cast<QObject *>(object), coreIndex: index);
1713 vme->connectAliasSignal(index, indexInSignalRange);
1714 }
1715 }
1716}
1717
1718/*!
1719Connect \a sender \a signal_index to \a receiver \a method_index with the specified
1720\a type and \a types. This behaves identically to QMetaObject::connect() except that
1721it connects any lazy "proxy" signal connections set up by QML.
1722
1723It is possible that this logic should be moved to QMetaObject::connect().
1724*/
1725bool QQmlPropertyPrivate::connect(const QObject *sender, int signal_index,
1726 const QObject *receiver, int method_index,
1727 int type, int *types)
1728{
1729 static const bool indexInSignalRange = false;
1730 flush_vme_signal(object: sender, index: signal_index, indexInSignalRange);
1731 flush_vme_signal(object: receiver, index: method_index, indexInSignalRange);
1732
1733 return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
1734}
1735
1736/*! \internal
1737 \a signal_index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1738 This is different from QMetaMethod::methodIndex().
1739*/
1740void QQmlPropertyPrivate::flushSignal(const QObject *sender, int signal_index)
1741{
1742 static const bool indexInSignalRange = true;
1743 flush_vme_signal(object: sender, index: signal_index, indexInSignalRange);
1744}
1745
1746QT_END_NAMESPACE
1747

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