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