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, &currentObject);
342                 }
343 
344                 if (!currentObject) return; // No value
345 
346             }
347 
348         }
349 
350         terminal = path.last();
351     }
352 
353     if (terminal.count() >= 3 &&
354         terminal.at(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