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
68 Q_DECLARE_METATYPE(QList<int>)
Q_DECLARE_METATYPE(QList<qreal>)69 Q_DECLARE_METATYPE(QList<qreal>)
70 Q_DECLARE_METATYPE(QList<bool>)
71 Q_DECLARE_METATYPE(QList<QString>)
72 Q_DECLARE_METATYPE(QList<QUrl>)
73
74 QT_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
82 As QML uses Qt's meta-type system all of the existing QMetaObject classes can be used to introspect
83 and interact with objects created by QML. However, some of the new features provided by QML - such
84 as type safety and attached properties - are most easily used through the QQmlProperty class
85 that simplifies some of their natural complexity.
86
87 Unlike QMetaProperty which represents a property on a class type, QQmlProperty encapsulates
88 a property on a specific object instance. To read a property's value, programmers create a
89 QQmlProperty instance and call the read() method. Likewise to write a property value the
90 write() method is used.
91
92 For example, for the following QML code:
93
94 \qml
95 // MyItem.qml
96 import QtQuick 2.0
97
98 Text { text: "A bit of text" }
99 \endqml
100
101 The \l Text object's properties could be accessed using QQmlProperty, like this:
102
103 \code
104 #include <QQmlProperty>
105 #include <QGraphicsObject>
106
107 ...
108
109 QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
110 QQmlProperty property(view.rootObject(), "font.pixelSize");
111 qWarning() << "Current pixel size:" << property.read().toInt();
112 property.write(24);
113 qWarning() << "Pixel size should now be 24:" << property.read().toInt();
114 \endcode
115 */
116
117 /*!
118 Create an invalid QQmlProperty.
119 */
120 QQmlProperty::QQmlProperty()
121 : d(nullptr)
122 {
123 }
124
125 /*! \internal */
~QQmlProperty()126 QQmlProperty::~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 */
QQmlProperty(QObject * obj)137 QQmlProperty::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 */
QQmlProperty(QObject * obj,QQmlContext * ctxt)149 QQmlProperty::QQmlProperty(QObject *obj, QQmlContext *ctxt)
150 : d(new QQmlPropertyPrivate)
151 {
152 d->context = ctxt?QQmlContextData::get(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 */
QQmlProperty(QObject * obj,QQmlEngine * engine)163 QQmlProperty::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 */
initDefault(QObject * obj)174 void 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 */
QQmlProperty(QObject * obj,const QString & name)188 QQmlProperty::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 */
QQmlProperty(QObject * obj,const QString & name,QQmlContext * ctxt)202 QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt)
203 : d(new QQmlPropertyPrivate)
204 {
205 d->context = ctxt?QQmlContextData::get(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 */
QQmlProperty(QObject * obj,const QString & name,QQmlEngine * engine)216 QQmlProperty::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
create(QObject * target,const QString & propertyName,QQmlContextData * context)225 QQmlProperty 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(target, propertyName);
233 if (!result.isValid()) {
234 d->object = nullptr;
235 d->context = nullptr;
236 d->engine = nullptr;
237 }
238 return result;
239 }
240
QQmlPropertyPrivate()241 QQmlPropertyPrivate::QQmlPropertyPrivate()
242 : context(nullptr), engine(nullptr), object(nullptr), isNameCached(false)
243 {
244 }
245
effectiveContext() const246 QQmlContextData *QQmlPropertyPrivate::effectiveContext() const
247 {
248 if (context) return context;
249 else if (engine) return QQmlContextData::get(engine->rootContext());
250 else return nullptr;
251 }
252
initProperty(QObject * obj,const QString & name)253 void 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(QLatin1Char('.'))) {
264 path = name.splitRef(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(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(0).isUpper()) {
274 QQmlTypeNameCache::Result r = typeNameCache->query(pathName);
275 if (r.isValid()) {
276 if (r.type.isValid()) {
277 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
278 QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(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(ii), r.importNamespace);
287 if (!r.type.isValid()) return; // Invalid type in namespace
288
289 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
290 QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(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(property->propType())) {
315 // We're now at a value type property
316 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType());
317 if (!valueTypeMetaObject) return; // Not a value type
318
319 int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData());
320 if (idx == -1) return; // Value type property does not exist
321
322 QMetaProperty vtProp = valueTypeMetaObject->property(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*>(currentObject))
337 currentObject = asPropertyMap->value(path.at(ii).toString()).value<QObject*>();
338 else
339 return; // Not an object property, and not a property map
340 } else {
341 property->readProperty(currentObject, ¤tObject);
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(0) == QLatin1Char('o') &&
355 terminal.at(1) == QLatin1Char('n') &&
356 (terminal.at(2).isUpper() || terminal.at(2) == '_')) {
357
358 QString signalName = terminal.mid(2).toString();
359 int firstNon_;
360 int length = signalName.length();
361 for (firstNon_ = 0; firstNon_ < length; ++firstNon_)
362 if (signalName.at(firstNon_) != '_')
363 break;
364 signalName[firstNon_] = signalName.at(firstNon_).toLower();
365
366 // XXX - this code treats methods as signals
367
368 QQmlData *ddata = QQmlData::get(currentObject, false);
369 if (ddata && ddata->propertyCache) {
370
371 // Try method
372 QQmlPropertyData *d = ddata->propertyCache->property(signalName, currentObject, context);
373 while (d && !d->isFunction())
374 d = ddata->propertyCache->overrideData(d);
375
376 if (d) {
377 object = currentObject;
378 core = *d;
379 return;
380 }
381
382 // Try property
383 if (signalName.endsWith(QLatin1String("Changed"))) {
384 const QStringRef propName = signalName.midRef(0, signalName.length() - 7);
385 QQmlPropertyData *d = ddata->propertyCache->property(propName, currentObject, context);
386 while (d && d->isFunction())
387 d = ddata->propertyCache->overrideData(d);
388
389 if (d && d->notifyIndex() != -1) {
390 object = currentObject;
391 core = *ddata->propertyCache->signal(d->notifyIndex());
392 return;
393 }
394 }
395
396 } else {
397 QMetaMethod method = findSignalByName(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 */
signalIndex() const424 int QQmlPropertyPrivate::signalIndex() const
425 {
426 Q_ASSERT(type() == QQmlProperty::SignalProperty);
427 QMetaMethod m = object->metaObject()->method(core.coreIndex());
428 return QMetaObjectPrivate::signalIndex(m);
429 }
430
431 /*!
432 Create a copy of \a other.
433 */
QQmlProperty(const QQmlProperty & other)434 QQmlProperty::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 */
propertyTypeCategory() const465 QQmlProperty::PropertyTypeCategory QQmlProperty::propertyTypeCategory() const
466 {
467 return d ? d->propertyTypeCategory() : InvalidCategory;
468 }
469
470 QQmlProperty::PropertyTypeCategory
propertyTypeCategory() const471 QQmlPropertyPrivate::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((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 */
propertyTypeName() const498 const char *QQmlProperty::propertyTypeName() const
499 {
500 if (!d)
501 return nullptr;
502 if (d->isValueType()) {
503 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType());
504 Q_ASSERT(valueTypeMetaObject);
505 return valueTypeMetaObject->property(d->valueTypeData.coreIndex()).typeName();
506 } else if (d->object && type() & Property && d->core.isValid()) {
507 return d->object->metaObject()->property(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 */
operator ==(const QQmlProperty & other) const517 bool 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 */
propertyType() const532 int QQmlProperty::propertyType() const
533 {
534 return d ? d->propertyType() : int(QMetaType::UnknownType);
535 }
536
isValueType() const537 bool QQmlPropertyPrivate::isValueType() const
538 {
539 return valueTypeData.isValid();
540 }
541
propertyType() const542 int 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
type() const554 QQmlProperty::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 */
type() const567 QQmlProperty::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 */
isProperty() const575 bool QQmlProperty::isProperty() const
576 {
577 return type() & Property;
578 }
579
580 /*!
581 Returns true if this QQmlProperty represents a QML signal property.
582 */
isSignalProperty() const583 bool QQmlProperty::isSignalProperty() const
584 {
585 return type() & SignalProperty;
586 }
587
588 /*!
589 Returns the QQmlProperty's QObject.
590 */
object() const591 QObject *QQmlProperty::object() const
592 {
593 return d ? d->object : nullptr;
594 }
595
596 /*!
597 Assign \a other to this QQmlProperty.
598 */
operator =(const QQmlProperty & other)599 QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other)
600 {
601 if (d)
602 d->release();
603 d = other.d;
604 if (d)
605 d->addref();
606
607 return *this;
608 }
609
610 /*!
611 Returns true if the property is writable, otherwise false.
612 */
isWritable() const613 bool QQmlProperty::isWritable() const
614 {
615 if (!d)
616 return false;
617 if (!d->object)
618 return false;
619 if (d->core.isQList()) //list
620 return true;
621 else if (d->core.isFunction()) //signal handler
622 return false;
623 else if (d->core.isValid()) //normal property
624 return d->core.isWritable();
625 else
626 return false;
627 }
628
629 /*!
630 Returns true if the property is designable, otherwise false.
631 */
isDesignable() const632 bool QQmlProperty::isDesignable() const
633 {
634 if (!d)
635 return false;
636 if (type() & Property && d->core.isValid() && d->object)
637 return d->object->metaObject()->property(d->core.coreIndex()).isDesignable();
638 else
639 return false;
640 }
641
642 /*!
643 Returns true if the property is resettable, otherwise false.
644 */
isResettable() const645 bool QQmlProperty::isResettable() const
646 {
647 if (!d)
648 return false;
649 if (type() & Property && d->core.isValid() && d->object)
650 return d->core.isResettable();
651 else
652 return false;
653 }
654
655 /*!
656 Returns true if the QQmlProperty refers to a valid property, otherwise
657 false.
658 */
isValid() const659 bool QQmlProperty::isValid() const
660 {
661 if (!d)
662 return false;
663 return type() != Invalid;
664 }
665
666 /*!
667 Return the name of this QML property.
668 */
name() const669 QString QQmlProperty::name() const
670 {
671 if (!d)
672 return QString();
673 if (!d->isNameCached) {
674 // ###
675 if (!d->object) {
676 } else if (d->isValueType()) {
677 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType());
678 Q_ASSERT(valueTypeMetaObject);
679
680 const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex()).name();
681 d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName);
682 } else if (type() & SignalProperty) {
683 QString name = QLatin1String("on") + d->core.name(d->object);
684 name[2] = name.at(2).toUpper();
685 d->nameCache = name;
686 } else {
687 d->nameCache = d->core.name(d->object);
688 }
689 d->isNameCached = true;
690 }
691
692 return d->nameCache;
693 }
694
695 /*!
696 Returns the \l{QMetaProperty} {Qt property} associated with
697 this QML property.
698 */
property() const699 QMetaProperty QQmlProperty::property() const
700 {
701 if (!d)
702 return QMetaProperty();
703 if (type() & Property && d->core.isValid() && d->object)
704 return d->object->metaObject()->property(d->core.coreIndex());
705 else
706 return QMetaProperty();
707 }
708
709 /*!
710 Return the QMetaMethod for this property if it is a SignalProperty,
711 otherwise returns an invalid QMetaMethod.
712 */
method() const713 QMetaMethod QQmlProperty::method() const
714 {
715 if (!d)
716 return QMetaMethod();
717 if (type() & SignalProperty && d->object)
718 return d->object->metaObject()->method(d->core.coreIndex());
719 else
720 return QMetaMethod();
721 }
722
723 /*!
724 Returns the binding associated with this property, or 0 if no binding
725 exists.
726 */
727 QQmlAbstractBinding *
binding(const QQmlProperty & that)728 QQmlPropertyPrivate::binding(const QQmlProperty &that)
729 {
730 if (!that.d || !that.isProperty() || !that.d->object)
731 return nullptr;
732
733 QQmlPropertyIndex thatIndex(that.d->core.coreIndex(), that.d->valueTypeData.coreIndex());
734 return binding(that.d->object, thatIndex);
735 }
736
737 /*!
738 Set the binding associated with this property to \a newBinding. Returns
739 the existing binding (if any), otherwise 0.
740
741 \a newBinding will be enabled, and the returned binding (if any) will be
742 disabled.
743
744 Ownership of \a newBinding transfers to QML. Ownership of the return value
745 is assumed by the caller.
746
747 \a flags is passed through to the binding and is used for the initial update (when
748 the binding sets the initial value, it will use these flags for the write).
749 */
750 void
setBinding(const QQmlProperty & that,QQmlAbstractBinding * newBinding)751 QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QQmlAbstractBinding *newBinding)
752 {
753 if (!newBinding) {
754 removeBinding(that);
755 return;
756 }
757
758 if (!that.d || !that.isProperty() || !that.d->object) {
759 if (!newBinding->ref)
760 delete newBinding;
761 return;
762 }
763 setBinding(newBinding);
764 }
765
removeOldBinding(QObject * object,QQmlPropertyIndex index,QQmlPropertyPrivate::BindingFlags flags=QQmlPropertyPrivate::None)766 static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None)
767 {
768 int coreIndex = index.coreIndex();
769 int valueTypeIndex = index.valueTypeIndex();
770
771 QQmlData *data = QQmlData::get(object, false);
772
773 if (!data || !data->hasBindingBit(coreIndex))
774 return;
775
776 QQmlAbstractBinding::Ptr oldBinding;
777 oldBinding = data->bindings;
778
779 while (oldBinding && (oldBinding->targetPropertyIndex().coreIndex() != coreIndex ||
780 oldBinding->targetPropertyIndex().hasValueTypeIndex()))
781 oldBinding = oldBinding->nextBinding();
782
783 if (!oldBinding)
784 return;
785
786 if (valueTypeIndex != -1 && oldBinding->isValueTypeProxy())
787 oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index);
788
789 if (!oldBinding)
790 return;
791
792 if (!(flags & QQmlPropertyPrivate::DontEnable))
793 oldBinding->setEnabled(false, {});
794 oldBinding->removeFromObject();
795 }
796
removeBinding(QQmlAbstractBinding * b)797 void QQmlPropertyPrivate::removeBinding(QQmlAbstractBinding *b)
798 {
799 removeBinding(b->targetObject(), b->targetPropertyIndex());
800 }
801
removeBinding(QObject * o,QQmlPropertyIndex index)802 void QQmlPropertyPrivate::removeBinding(QObject *o, QQmlPropertyIndex index)
803 {
804 Q_ASSERT(o);
805
806 QObject *target;
807 QQmlPropertyIndex targetIndex;
808 findAliasTarget(o, index, &target, &targetIndex);
809 removeOldBinding(target, targetIndex);
810 }
811
removeBinding(const QQmlProperty & that)812 void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that)
813 {
814 if (!that.d || !that.isProperty() || !that.d->object)
815 return;
816
817 removeBinding(that.d->object, that.d->encodedIndex());
818 }
819
820 QQmlAbstractBinding *
binding(QObject * object,QQmlPropertyIndex index)821 QQmlPropertyPrivate::binding(QObject *object, QQmlPropertyIndex index)
822 {
823 findAliasTarget(object, index, &object, &index);
824
825 QQmlData *data = QQmlData::get(object);
826 if (!data)
827 return nullptr;
828
829 const int coreIndex = index.coreIndex();
830 const int valueTypeIndex = index.valueTypeIndex();
831
832 if (coreIndex < 0 || !data->hasBindingBit(coreIndex))
833 return nullptr;
834
835 QQmlAbstractBinding *binding = data->bindings;
836 while (binding && (binding->targetPropertyIndex().coreIndex() != coreIndex ||
837 binding->targetPropertyIndex().hasValueTypeIndex()))
838 binding = binding->nextBinding();
839
840 if (binding && valueTypeIndex != -1) {
841 if (binding->isValueTypeProxy()) {
842 binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
843 }
844 }
845
846 return binding;
847 }
848
findAliasTarget(QObject * object,QQmlPropertyIndex bindingIndex,QObject ** targetObject,QQmlPropertyIndex * targetBindingIndex)849 void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bindingIndex,
850 QObject **targetObject,
851 QQmlPropertyIndex *targetBindingIndex)
852 {
853 QQmlData *data = QQmlData::get(object, false);
854 if (data) {
855 int coreIndex = bindingIndex.coreIndex();
856 int valueTypeIndex = bindingIndex.valueTypeIndex();
857
858 QQmlPropertyData *propertyData =
859 data->propertyCache?data->propertyCache->property(coreIndex):nullptr;
860 if (propertyData && propertyData->isAlias()) {
861 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
862
863 QObject *aObject = nullptr; int aCoreIndex = -1; int aValueTypeIndex = -1;
864 if (vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) {
865 // This will either be a value type sub-reference or an alias to a value-type sub-reference not both
866 Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
867
868 QQmlPropertyIndex aBindingIndex(aCoreIndex);
869 if (aValueTypeIndex != -1) {
870 aBindingIndex = QQmlPropertyIndex(aCoreIndex, aValueTypeIndex);
871 } else if (valueTypeIndex != -1) {
872 aBindingIndex = QQmlPropertyIndex(aCoreIndex, valueTypeIndex);
873 }
874
875 findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex);
876 return;
877 }
878 }
879 }
880
881 *targetObject = object;
882 *targetBindingIndex = bindingIndex;
883 }
884
885
setBinding(QQmlAbstractBinding * binding,BindingFlags flags,QQmlPropertyData::WriteFlags writeFlags)886 void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags)
887 {
888 Q_ASSERT(binding);
889 Q_ASSERT(binding->targetObject());
890
891 QObject *object = binding->targetObject();
892 const QQmlPropertyIndex index = binding->targetPropertyIndex();
893
894 #ifndef QT_NO_DEBUG
895 int coreIndex = index.coreIndex();
896 QQmlData *data = QQmlData::get(object, true);
897 if (data->propertyCache) {
898 QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
899 Q_ASSERT(propertyData);
900 }
901 #endif
902
903 removeOldBinding(object, index, flags);
904
905 binding->addToObject();
906 if (!(flags & DontEnable))
907 binding->setEnabled(true, writeFlags);
908
909 }
910
911 /*!
912 Returns the expression associated with this signal property, or 0 if no
913 signal expression exists.
914 */
915 QQmlBoundSignalExpression *
signalExpression(const QQmlProperty & that)916 QQmlPropertyPrivate::signalExpression(const QQmlProperty &that)
917 {
918 if (!(that.type() & QQmlProperty::SignalProperty))
919 return nullptr;
920
921 QQmlData *data = QQmlData::get(that.d->object);
922 if (!data)
923 return nullptr;
924
925 QQmlBoundSignal *signalHandler = data->signalHandlers;
926
927 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex())
928 signalHandler = signalHandler->m_nextSignal;
929
930 if (signalHandler)
931 return signalHandler->expression();
932
933 return nullptr;
934 }
935
936 /*!
937 Set the signal expression associated with this signal property to \a expr.
938 A reference to \a expr will be added by QML.
939 */
setSignalExpression(const QQmlProperty & that,QQmlBoundSignalExpression * expr)940 void QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr)
941 {
942 if (expr)
943 expr->addref();
944 QQmlPropertyPrivate::takeSignalExpression(that, expr);
945 }
946
947 /*!
948 Set the signal expression associated with this signal property to \a expr.
949 Ownership of \a expr transfers to QML.
950 */
takeSignalExpression(const QQmlProperty & that,QQmlBoundSignalExpression * expr)951 void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that,
952 QQmlBoundSignalExpression *expr)
953 {
954 if (!(that.type() & QQmlProperty::SignalProperty)) {
955 if (expr)
956 expr->release();
957 return;
958 }
959
960 QQmlData *data = QQmlData::get(that.d->object, nullptr != expr);
961 if (!data)
962 return;
963
964 QQmlBoundSignal *signalHandler = data->signalHandlers;
965
966 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(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(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 */
read() const985 QVariant 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 /*!
1005 Return 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 */
read(const QObject * object,const QString & name)1011 QVariant 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 */
read(const QObject * object,const QString & name,QQmlContext * ctxt)1027 QVariant 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 */
read(const QObject * object,const QString & name,QQmlEngine * engine)1044 QVariant 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
readValueProperty()1050 QVariant QQmlPropertyPrivate::readValueProperty()
1051 {
1052 auto doRead = [&](QQmlGadgetPtrWrapper *wrapper) {
1053 wrapper->read(object, core.coreIndex());
1054 return wrapper->property(valueTypeData.coreIndex()).read(wrapper);
1055 };
1056
1057 if (isValueType()) {
1058 if (QQmlGadgetPtrWrapper *wrapper = QQmlGadgetPtrWrapper::instance(engine, core.propType()))
1059 return doRead(wrapper);
1060 if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(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(object, &prop);
1069 return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType(), engine));
1070
1071 } else if (core.isQObject()) {
1072
1073 QObject *rv = nullptr;
1074 core.readProperty(object, &rv);
1075 return QVariant::fromValue(rv);
1076
1077 } else {
1078
1079 if (!core.propType()) // Unregistered type
1080 return object->metaObject()->property(core.coreIndex()).read(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(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.
resolvedUrlSequence(const QVariant & value,QQmlContextData * context)1100 QVariant QQmlPropertyPrivate::resolvedUrlSequence(const QVariant &value, QQmlContextData *context)
1101 {
1102 QList<QUrl> urls;
1103 if (value.userType() == qMetaTypeId<QUrl>()) {
1104 urls.append(value.toUrl());
1105 } else if (value.userType() == qMetaTypeId<QString>()) {
1106 urls.append(QUrl(value.toString()));
1107 } else if (value.userType() == qMetaTypeId<QByteArray>()) {
1108 urls.append(QUrl(QString::fromUtf8(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(urlStringsSize);
1115 for (int i = 0; i < urlStringsSize; ++i)
1116 urls.append(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(urlStringsSize);
1121 for (int i = 0; i < urlStringsSize; ++i)
1122 urls.append(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(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(u);
1133 }
1134
1135 return QVariant::fromValue<QList<QUrl> >(resolvedUrls);
1136 }
1137
1138 //writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC!
writeEnumProperty(const QMetaProperty & prop,int idx,QObject * object,const QVariant & value,int flags)1139 bool 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(value.toByteArray(), &ok));
1155 else
1156 v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
1157 if (!ok)
1158 return false;
1159 } else if (v.userType() != QMetaType::Int && v.userType() != QMetaType::UInt) {
1160 int enumMetaTypeId = QMetaType::type(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(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
writeValueProperty(const QVariant & value,QQmlPropertyData::WriteFlags flags)1179 bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags)
1180 {
1181 return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags);
1182 }
1183
1184 bool
writeValueProperty(QObject * object,const QQmlPropertyData & core,const QQmlPropertyData & valueTypeData,const QVariant & value,QQmlContextData * context,QQmlPropertyData::WriteFlags flags)1185 QQmlPropertyPrivate::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(object, encodedIndex(core, valueTypeData));
1194
1195 bool rv = false;
1196 if (valueTypeData.isValid()) {
1197 auto doWrite = [&](QQmlGadgetPtrWrapper *wrapper) {
1198 wrapper->read(object, core.coreIndex());
1199 rv = write(wrapper, valueTypeData, value, context, flags);
1200 wrapper->write(object, core.coreIndex(), flags);
1201 };
1202
1203 QQmlGadgetPtrWrapper *wrapper = context
1204 ? QQmlGadgetPtrWrapper::instance(context->engine, core.propType())
1205 : nullptr;
1206 if (wrapper) {
1207 doWrite(wrapper);
1208 } else if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(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
write(QObject * object,const QQmlPropertyData & property,const QVariant & value,QQmlContextData * context,QQmlPropertyData::WriteFlags flags)1220 bool 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(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(value.toDouble(), &integral);
1235 if (qFuzzyIsNull(fractional))
1236 v.convert(QMetaType::Int);
1237 }
1238 return writeEnumProperty(prop, property.coreIndex(), object, v, flags);
1239 }
1240
1241 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(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(object, 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(valMo, propMo)) {
1266 return property.writeProperty(object, &o, flags);
1267 } else if (!o && QQmlMetaObject::canConvert(propMo, 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(object, &o, flags);
1272 } else {
1273 return false;
1274 }
1275 } else if (value.canConvert(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(object, &b, flags);
1281 }
1282 case QMetaType::Int: {
1283 int i = value.toInt();
1284 return property.writeProperty(object, &i, flags);
1285 }
1286 case QMetaType::Double: {
1287 double d = value.toDouble();
1288 return property.writeProperty(object, &d, flags);
1289 }
1290 case QMetaType::Float: {
1291 float f = value.toFloat();
1292 return property.writeProperty(object, &f, flags);
1293 }
1294 case QMetaType::QString: {
1295 QString s = value.toString();
1296 return property.writeProperty(object, &s, flags);
1297 }
1298 default: { // "fallback":
1299 QVariant v = value;
1300 v.convert(propertyType);
1301 return property.writeProperty(object, const_cast<void *>(v.constData()), flags);
1302 }
1303 }
1304 } else if (propertyType == qMetaTypeId<QVariant>()) {
1305 return property.writeProperty(object, 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(value.toByteArray()));
1312 // Encoded dir-separators defeat QUrl processing - decode them first
1313 input.replace(QLatin1String("%2f"), QLatin1String("/"), 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(QLatin1String("%2f"), QLatin1String("/"), 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(object, &u, flags);
1327 } else if (propertyType == qMetaTypeId<QList<QUrl>>()) {
1328 QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl>>();
1329 return property.writeProperty(object, &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(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(object, &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(o, 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 *> >(value);
1363
1364 for (int ii = 0; ii < list.count(); ++ii) {
1365 QObject *o = list.at(ii);
1366 if (o && !QQmlMetaObject::canConvert(o, 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(o, 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(), propertyType, &ok);
1383
1384 if (!ok) {
1385 v = value;
1386 if (v.convert(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> >(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> >(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> >(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> >(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>(list);
1434 ok = true;
1435 }
1436 }
1437
1438 if (!ok && QQmlMetaType::isInterface(propertyType)) {
1439 auto valueAsQObject = qvariant_cast<QObject *>(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(object, const_cast<void *>(value.constData()), flags);
1444 }
1445 }
1446
1447 if (ok) {
1448 return property.writeProperty(object, const_cast<void *>(v.constData()), flags);
1449 } else {
1450 return false;
1451 }
1452 }
1453
1454 return true;
1455 }
1456
rawMetaObjectForType(QQmlEnginePrivate * engine,int userType)1457 QQmlMetaObject 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(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 */
write(const QVariant & value) const1475 bool QQmlProperty::write(const QVariant &value) const
1476 {
1477 return QQmlPropertyPrivate::write(*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 */
write(QObject * object,const QString & name,const QVariant & value)1491 bool 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 */
write(QObject * object,const QString & name,const QVariant & value,QQmlContext * ctxt)1509 bool 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 */
write(QObject * object,const QString & name,const QVariant & value,QQmlEngine * engine)1531 bool 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 */
reset() const1543 bool 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
write(const QQmlProperty & that,const QVariant & value,QQmlPropertyData::WriteFlags flags)1554 bool 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 */
hasNotifySignal() const1569 bool QQmlProperty::hasNotifySignal() const
1570 {
1571 if (type() & Property && d->object) {
1572 return d->object->metaObject()->property(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 */
needsNotifySignal() const1584 bool 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 */
connectNotifySignal(QObject * dest,int method) const1597 bool 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(d->core.coreIndex());
1603 if (prop.hasNotifySignal()) {
1604 return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, 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 */
connectNotifySignal(QObject * dest,const char * slot) const1621 bool 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(d->core.coreIndex());
1627 if (prop.hasNotifySignal()) {
1628 QByteArray signal('2' + prop.notifySignal().methodSignature());
1629 return QObject::connect(d->object, signal.constData(), dest, slot);
1630 } else {
1631 return false;
1632 }
1633 }
1634
1635 /*!
1636 Return the Qt metaobject index of the property.
1637 */
index() const1638 int QQmlProperty::index() const
1639 {
1640 return d ? d->core.coreIndex() : -1;
1641 }
1642
propertyIndex(const QQmlProperty & that)1643 QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that)
1644 {
1645 return that.d ? that.d->encodedIndex() : QQmlPropertyIndex();
1646 }
1647
1648 QQmlProperty
restore(QObject * object,const QQmlPropertyData & data,const QQmlPropertyData * valueTypeData,QQmlContextData * ctxt)1649 QQmlPropertyPrivate::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 */
findSignalByName(const QMetaObject * mo,const QByteArray & name)1669 QMetaMethod 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(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("Changed")) {
1683 QByteArray propName = name.mid(0, name.length() - 7);
1684 int propIdx = mo->indexOfProperty(propName.constData());
1685 if (propIdx >= 0) {
1686 QMetaProperty prop = mo->property(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 */
flush_vme_signal(const QObject * object,int index,bool indexInSignalRange)1700 static 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(const_cast<QObject *>(object), index);
1711 else
1712 vme = QQmlVMEMetaObject::getForMethod(const_cast<QObject *>(object), index);
1713 vme->connectAliasSignal(index, indexInSignalRange);
1714 }
1715 }
1716 }
1717
1718 /*!
1719 Connect \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
1721 it connects any lazy "proxy" signal connections set up by QML.
1722
1723 It is possible that this logic should be moved to QMetaObject::connect().
1724 */
connect(const QObject * sender,int signal_index,const QObject * receiver,int method_index,int type,int * types)1725 bool 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(sender, signal_index, indexInSignalRange);
1731 flush_vme_signal(receiver, 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 */
flushSignal(const QObject * sender,int signal_index)1740 void QQmlPropertyPrivate::flushSignal(const QObject *sender, int signal_index)
1741 {
1742 static const bool indexInSignalRange = true;
1743 flush_vme_signal(sender, signal_index, indexInSignalRange);
1744 }
1745
1746 QT_END_NAMESPACE
1747