Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlproperty.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlproperty.h"
43 #include "qqmlproperty_p.h"
44
45 #include "qqml.h"
46 #include "qqmlbinding_p.h"
47 #include "qqmlcontext.h"
48 #include "qqmlcontext_p.h"
49 #include "qqmlboundsignal_p.h"
50 #include "qqmlengine.h"
51 #include "qqmlengine_p.h"
52 #include "qqmldata_p.h"
53 #include "qqmlstringconverters_p.h"
54 #include "qqmllist_p.h"
55 #include "qqmlcompiler_p.h"
56 #include "qqmlvmemetaobject_p.h"
57 #include "qqmlexpression_p.h"
58 #include "qqmlvaluetypeproxybinding_p.h"
59 #include <private/qv8bindings_p.h>
60
61 #include <QStringList>
62 #include <private/qmetaobject_p.h>
63 #include <QtCore/qdebug.h>
64
65 #include <math.h>
66
67 Q_DECLARE_METATYPE(QJSValue)
68 Q_DECLARE_METATYPE(QList<int>)
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 The QtQuick 1 version of this class was named QDeclarativeProperty.
117 */
118
119 /*!
120     Create an invalid QQmlProperty.
121 */
122 QQmlProperty::QQmlProperty()
123 : d(0)
124 {
125 }
126
127 /*!  \internal */
128 QQmlProperty::~QQmlProperty()
129 {
130     if (d)
131         d->release();
132     d = 0;
133 }
134
135 /*!
136     Creates a QQmlProperty for the default property of \a obj. If there is no
137     default property, an invalid QQmlProperty will be created.
138  */
139 QQmlProperty::QQmlProperty(QObject *obj)
140 : d(new QQmlPropertyPrivate)
141 {
142     d->initDefault(obj);
143 }
144
145 /*!
146     Creates a QQmlProperty for the default property of \a obj
147     using the \l{QQmlContext} {context} \a ctxt. If there is
148     no default property, an invalid QQmlProperty will be
149     created.
150  */
151 QQmlProperty::QQmlProperty(QObject *obj, QQmlContext *ctxt)
152 : d(new QQmlPropertyPrivate)
153 {
154     d->context = ctxt?QQmlContextData::get(ctxt):0;
155     d->engine = ctxt?ctxt->engine():0;
156     d->initDefault(obj);
157 }
158
159 /*!
160     Creates a QQmlProperty for the default property of \a obj
161     using the environment for instantiating QML components that is
162     provided by \a engine.  If there is no default property, an
163     invalid QQmlProperty will be created.
164  */
165 QQmlProperty::QQmlProperty(QObject *obj, QQmlEngine *engine)
166   : d(new QQmlPropertyPrivate)
167 {
168     d->context = 0;
169     d->engine = engine;
170     d->initDefault(obj);
171 }
172
173 /*!
174     Initialize from the default property of \a obj
175 */
176 void QQmlPropertyPrivate::initDefault(QObject *obj)
177 {
178     if (!obj)
179         return;
180
181     QMetaProperty p = QQmlMetaType::defaultProperty(obj);
182     core.load(p);
183     if (core.isValid())
184         object = obj;
185 }
186
187 /*!
188     Creates a QQmlProperty for the property \a name of \a obj.
189  */
190 QQmlProperty::QQmlProperty(QObject *obj, const QString &name)
191 : d(new QQmlPropertyPrivate)
192 {
193     d->initProperty(obj, name);
194     if (!isValid()) d->object = 0;
195 }
196
197 /*!
198     Creates a QQmlProperty for the property \a name of \a obj
199     using the \l{QQmlContext} {context} \a ctxt.
200
201     Creating a QQmlProperty without a context will render some
202     properties - like attached properties - inaccessible.
203 */
204 QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt)
205 : d(new QQmlPropertyPrivate)
206 {
207     d->context = ctxt?QQmlContextData::get(ctxt):0;
208     d->engine = ctxt?ctxt->engine():0;
209     d->initProperty(obj, name);
210     if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; }
211 }
212
213 /*!
214     Creates a QQmlProperty for the property \a name of \a obj
215     using the environment for instantiating QML components that is
216     provided by \a engine.
217  */
218 QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine)
219 : d(new QQmlPropertyPrivate)
220 {
221     d->context = 0;
222     d->engine = engine;
223     d->initProperty(obj, name);
224     if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; }
225 }
226
227 QQmlPropertyPrivate::QQmlPropertyPrivate()
228 : context(0), engine(0), object(0), isNameCached(false)
229 {
230 }
231
232 QQmlContextData *QQmlPropertyPrivate::effectiveContext() const
233 {
234     if (context) return context;
235     else if (engine) return QQmlContextData::get(engine->rootContext());
236     else return 0;
237 }
238
239 void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
240 {
241     if (!obj) return;
242
243     QQmlTypeNameCache *typeNameCache = context?context->imports:0;
244
245     QStringList path = name.split(QLatin1Char('.'));
246     if (path.isEmpty()) return;
247
248     QObject *currentObject = obj;
249
250     // Everything up to the last property must be an "object type" property
251     for (int ii = 0; ii < path.count() - 1; ++ii) {
252         const QString &pathName = path.at(ii);
253
254         if (typeNameCache) {
255             QQmlTypeNameCache::Result r = typeNameCache->query(pathName);
256             if (r.isValid()) {
257                 if (r.type) {
258                     QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction();
259                     if (!func) return; // Not an attachable type
260
261                     currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(), currentObject);
262                     if (!currentObject) return; // Something is broken with the attachable type
263                 } else if (r.importNamespace) {
264                     if ((ii + 1) == path.count()) return; // No type following the namespace
265
266                     ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace);
267                     if (!r.type) return; // Invalid type in namespace
268
269                     QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction();
270                     if (!func) return; // Not an attachable type
271
272                     currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(), currentObject);
273                     if (!currentObject) return; // Something is broken with the attachable type
274
275                 } else if (r.scriptIndex != -1) {
276                     return; // Not a type
277                 } else {
278                     Q_ASSERT(!"Unreachable");
279                 }
280                 continue;
281             }
282
283         }
284
285         QQmlPropertyData local;
286         QQmlPropertyData *property =
287             QQmlPropertyCache::property(engine, obj, pathName, context, local);
288
289         if (!property) return; // Not a property
290         if (property->isFunction())
291             return; // Not an object property
292
293         if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType)) {
294             // We're now at a value type property
295             QObject *typeObject = QQmlValueTypeFactory::valueType(property->propType);
296             if (!typeObject) return; // Not a value type
297
298             int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData());
299             if (idx == -1) return; // Value type property does not exist
300
301             QMetaProperty vtProp = typeObject->metaObject()->property(idx);
302
303             Q_ASSERT(QQmlPropertyData::flagsForProperty(vtProp) <= QQmlPropertyData::ValueTypeFlagMask);
304             Q_ASSERT(vtProp.userType() <= 0x0000FFFF);
305             Q_ASSERT(idx <= 0x0000FFFF);
306
307             object = currentObject;
308             core = *property;
309             core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual);
310             core.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp);
311             core.valueTypePropType = vtProp.userType();
312             core.valueTypeCoreIndex = idx;
313
314             return;
315         } else {
316             if (!property->isQObject())
317                 return; // Not an object property
318
319             void *args[] = { &currentObject, 0 };
320             QMetaObject::metacall(currentObject, QMetaObject::ReadProperty, property->coreIndex, args);
321             if (!currentObject) return; // No value
322
323         }
324
325     }
326
327     const QString &terminal = path.last();
328
329     if (terminal.count() >= 3 &&
330         terminal.at(0) == QLatin1Char('o') &&
331         terminal.at(1) == QLatin1Char('n') &&
332         terminal.at(2).isUpper()) {
333
334         QString signalName = terminal.mid(2);
335         signalName[0] = signalName.at(0).toLower();
336
337         // XXX - this code treats methods as signals
338
339         QQmlData *ddata = QQmlData::get(currentObject, false);
340         if (ddata && ddata->propertyCache) {
341
342             // Try method
343             QQmlPropertyData *d = ddata->propertyCache->property(signalName, currentObject, context);
344             while (d && !d->isFunction())
345                 d = ddata->propertyCache->overrideData(d);
346
347             if (d) {
348                 object = currentObject;
349                 core = *d;
350                 return;
351             }
352
353             // Try property
354             if (signalName.endsWith(QLatin1String("Changed"))) {
355                 QString propName = signalName.mid(0, signalName.length() - 7);
356                 QQmlPropertyData *d = ddata->propertyCache->property(propName, currentObject, context);
357                 while (d && d->isFunction())
358                     d = ddata->propertyCache->overrideData(d);
359
360                 if (d && d->notifyIndex != -1) {
361                     object = currentObject;
362                     core = *ddata->propertyCache->signal(d->notifyIndex);
363                     return;
364                 }
365             }
366
367         } else {
368             QMetaMethod method = findSignalByName(currentObject->metaObject(),
369                                                   signalName.toLatin1());
370             if (method.isValid()) {
371                 object = currentObject;
372                 core.load(method);
373                 return;
374             }
375         }
376     }
377
378     // Property
379     QQmlPropertyData local;
380     QQmlPropertyData *property =
381         QQmlPropertyCache::property(engine, currentObject, terminal, context, local);
382     if (property && !property->isFunction()) {
383         object = currentObject;
384         core = *property;
385         nameCache = terminal;
386         isNameCached = true;
387     }
388 }
389
390 /*! \internal
391     Returns the index of this property's signal, in the signal index range
392     (see QObjectPrivate::signalIndex()). This is different from
393     QMetaMethod::methodIndex().
394 */
395 int QQmlPropertyPrivate::signalIndex() const
396 {
397     Q_ASSERT(type() == QQmlProperty::SignalProperty);
398     QMetaMethod m = object->metaObject()->method(core.coreIndex);
399     return QMetaObjectPrivate::signalIndex(m);
400 }
401
402 /*!
403     Create a copy of \a other.
404 */
405 QQmlProperty::QQmlProperty(const QQmlProperty &other)
406 {
407     d = other.d;
408     if (d)
409         d->addref();
410 }
411
412 /*!
413   \enum QQmlProperty::PropertyTypeCategory
414
415   This enum specifies a category of QML property.
416
417   \value InvalidCategory The property is invalid, or is a signal property.
418   \value List The property is a QQmlListProperty list property
419   \value Object The property is a QObject derived type pointer
420   \value Normal The property is a normal value property.
421  */
422
423 /*!
424   \enum QQmlProperty::Type
425
426   This enum specifies a type of QML property.
427
428   \value Invalid The property is invalid.
429   \value Property The property is a regular Qt property.
430   \value SignalProperty The property is a signal property.
431 */
432
433 /*!
434     Returns the property category.
435 */
436 QQmlProperty::PropertyTypeCategory QQmlProperty::propertyTypeCategory() const
437 {
438     return d ? d->propertyTypeCategory() : InvalidCategory;
439 }
440
441 QQmlProperty::PropertyTypeCategory
442 QQmlPropertyPrivate::propertyTypeCategory() const
443 {
444     uint type = this->type();
445
446     if (isValueType()) {
447         return QQmlProperty::Normal;
448     } else if (type & QQmlProperty::Property) {
449         int type = propertyType();
450         if (type == QVariant::Invalid)
451             return QQmlProperty::InvalidCategory;
452         else if (QQmlValueTypeFactory::isValueType((uint)type))
453             return QQmlProperty::Normal;
454         else if (core.isQObject())
455             return QQmlProperty::Object;
456         else if (core.isQList())
457             return QQmlProperty::List;
458         else
459             return QQmlProperty::Normal;
460     }
461
462     return QQmlProperty::InvalidCategory;
463 }
464
465 /*!
466     Returns the type name of the property, or 0 if the property has no type
467     name.
468 */
469 const char *QQmlProperty::propertyTypeName() const
470 {
471     if (!d)
472         return 0;
473     if (d->isValueType()) {
474         QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->core.propType);
475         Q_ASSERT(valueType);
476         return valueType->metaObject()->property(d->core.valueTypeCoreIndex).typeName();
477     } else if (d->object && type() & Property && d->core.isValid()) {
478         return d->object->metaObject()->property(d->core.coreIndex).typeName();
479     } else {
480         return 0;
481     }
482 }
483
484 /*!
485     Returns true if \a other and this QQmlProperty represent the same
486     property.
487 */
488 bool QQmlProperty::operator==(const QQmlProperty &other) const
489 {
490     if (!d || !other.d)
491         return false;
492     // category is intentially omitted here as it is generated
493     // from the other members
494     return d->object == other.d->object &&
495            d->core.coreIndex == other.d->core.coreIndex &&
496            d->core.isValueTypeVirtual() == other.d->core.isValueTypeVirtual() &&
497            (!d->core.isValueTypeVirtual() ||
498             (d->core.valueTypeCoreIndex == other.d->core.valueTypeCoreIndex &&
499              d->core.valueTypePropType == other.d->core.valueTypePropType));
500 }
501
502 /*!
503     Returns the QVariant type of the property, or QVariant::Invalid if the
504     property has no QVariant type.
505 */
506 int QQmlProperty::propertyType() const
507 {
508     return d ? d->propertyType() : int(QVariant::Invalid);
509 }
510
511 bool QQmlPropertyPrivate::isValueType() const
512 {
513     return core.isValueTypeVirtual();
514 }
515
516 int QQmlPropertyPrivate::propertyType() const
517 {
518     uint type = this->type();
519     if (isValueType()) {
520         return core.valueTypePropType;
521     } else if (type & QQmlProperty::Property) {
522         return core.propType;
523     } else {
524         return QVariant::Invalid;
525     }
526 }
527
528 QQmlProperty::Type QQmlPropertyPrivate::type() const
529 {
530     if (core.isFunction())
531         return QQmlProperty::SignalProperty;
532     else if (core.isValid())
533         return QQmlProperty::Property;
534     else
535         return QQmlProperty::Invalid;
536 }
537
538 /*!
539     Returns the type of the property.
540 */
541 QQmlProperty::Type QQmlProperty::type() const
542 {
543     return d ? d->type() : Invalid;
544 }
545
546 /*!
547     Returns true if this QQmlProperty represents a regular Qt property.
548 */
549 bool QQmlProperty::isProperty() const
550 {
551     return type() & Property;
552 }
553
554 /*!
555     Returns true if this QQmlProperty represents a QML signal property.
556 */
557 bool QQmlProperty::isSignalProperty() const
558 {
559     return type() & SignalProperty;
560 }
561
562 /*!
563     Returns the QQmlProperty's QObject.
564 */
565 QObject *QQmlProperty::object() const
566 {
567     return d ? d->object : 0;
568 }
569
570 /*!
571     Assign \a other to this QQmlProperty.
572 */
573 QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other)
574 {
575     if (d)
576         d->release();
577     d = other.d;
578     if (d)
579         d->addref();
580
581     return *this;
582 }
583
584 /*!
585     Returns true if the property is writable, otherwise false.
586 */
587 bool QQmlProperty::isWritable() const
588 {
589     if (!d)
590         return false;
591     if (!d->object)
592         return false;
593     if (d->core.isQList())           //list
594         return true;
595     else if (d->core.isFunction())   //signal handler
596         return false;
597     else if (d->core.isValid())      //normal property
598         return d->core.isWritable();
599     else
600         return false;
601 }
602
603 /*!
604     Returns true if the property is designable, otherwise false.
605 */
606 bool QQmlProperty::isDesignable() const
607 {
608     if (!d)
609         return false;
610     if (type() & Property && d->core.isValid() && d->object)
611         return d->object->metaObject()->property(d->core.coreIndex).isDesignable();
612     else
613         return false;
614 }
615
616 /*!
617     Returns true if the property is resettable, otherwise false.
618 */
619 bool QQmlProperty::isResettable() const
620 {
621     if (!d)
622         return false;
623     if (type() & Property && d->core.isValid() && d->object)
624         return d->core.isResettable();
625     else
626         return false;
627 }
628
629 /*!
630     Returns true if the QQmlProperty refers to a valid property, otherwise
631     false.
632 */
633 bool QQmlProperty::isValid() const
634 {
635     if (!d)
636         return false;
637     return type() != Invalid;
638 }
639
640 /*!
641     Return the name of this QML property.
642 */
643 QString QQmlProperty::name() const
644 {
645     if (!d)
646         return QString();
647     if (!d->isNameCached) {
648         // ###
649         if (!d->object) {
650         } else if (d->isValueType()) {
651             QString rv = d->core.name(d->object) + QLatin1Char('.');
652
653             QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->core.propType);
654             Q_ASSERT(valueType);
655
656             const char *vtName = valueType->metaObject()->property(d->core.valueTypeCoreIndex).name();
657             rv += QString::fromUtf8(vtName);
658
659             d->nameCache = rv;
660         } else if (type() & SignalProperty) {
661             QString name = QLatin1String("on") + d->core.name(d->object);
662             name[2] = name.at(2).toUpper();
663             d->nameCache = name;
664         } else {
665             d->nameCache = d->core.name(d->object);
666         }
667         d->isNameCached = true;
668     }
669
670     return d->nameCache;
671 }
672
673 /*!
674   Returns the \l{QMetaProperty} {Qt property} associated with
675   this QML property.
676  */
677 QMetaProperty QQmlProperty::property() const
678 {
679     if (!d)
680         return QMetaProperty();
681     if (type() & Property && d->core.isValid() && d->object)
682         return d->object->metaObject()->property(d->core.coreIndex);
683     else
684         return QMetaProperty();
685 }
686
687 /*!
688     Return the QMetaMethod for this property if it is a SignalProperty,
689     otherwise returns an invalid QMetaMethod.
690 */
691 QMetaMethod QQmlProperty::method() const
692 {
693     if (!d)
694         return QMetaMethod();
695     if (type() & SignalProperty && d->object)
696         return d->object->metaObject()->method(d->core.coreIndex);
697     else
698         return QMetaMethod();
699 }
700
701 /*!
702     Returns the binding associated with this property, or 0 if no binding
703     exists.
704 */
705 QQmlAbstractBinding *
706 QQmlPropertyPrivate::binding(const QQmlProperty &that)
707 {
708     if (!that.d || !that.isProperty() || !that.d->object)
709         return 0;
710
711     return binding(that.d->object, that.d->core.coreIndex,
712                    that.d->core.getValueTypeCoreIndex());
713 }
714
715 /*!
716     Set the binding associated with this property to \a newBinding.  Returns
717     the existing binding (if any), otherwise 0.
718
719     \a newBinding will be enabled, and the returned binding (if any) will be
720     disabled.
721
722     Ownership of \a newBinding transfers to QML.  Ownership of the return value
723     is assumed by the caller.
724
725     \a flags is passed through to the binding and is used for the initial update (when
726     the binding sets the initial value, it will use these flags for the write).
727 */
728 QQmlAbstractBinding *
729 QQmlPropertyPrivate::setBinding(const QQmlProperty &that,
730                                         QQmlAbstractBinding *newBinding,
731                                         WriteFlags flags)
732 {
733     if (!that.d || !that.isProperty() || !that.d->object) {
734         if (newBinding)
735             newBinding->destroy();
736         return 0;
737     }
738
739     if (newBinding) {
740         // In the case that the new binding is provided, we must target the property it
741         // is associated with.  If we don't do this, retargetBinding() can fail.
742         QObject *object = newBinding->object();
743         int pi = newBinding->propertyIndex();
744
745         int core = pi & 0x0000FFFF;
746         int vt = (pi & 0xFFFF0000)?(pi >> 16):-1;
747
748         return setBinding(object, core, vt, newBinding, flags);
749     } else {
750         return setBinding(that.d->object, that.d->core.coreIndex,
751                           that.d->core.getValueTypeCoreIndex(),
752                           newBinding, flags);
753     }
754 }
755
756 QQmlAbstractBinding *
757 QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex)
758 {
759     QQmlData *data = QQmlData::get(object);
760     if (!data)
761         return 0;
762
763     QQmlPropertyData *propertyData =
764         data->propertyCache?data->propertyCache->property(coreIndex):0;
765     if (propertyData && propertyData->isAlias()) {
766         QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
767
768         QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1;
769         if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex) || aCoreIndex == -1)
770             return 0;
771
772         // This will either be a value type sub-reference or an alias to a value-type sub-reference not both
773         Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
774         aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex;
775         return binding(aObject, aCoreIndex, aValueTypeIndex);
776     }
777
778     if (!data->hasBindingBit(coreIndex))
779         return 0;
780
781     QQmlAbstractBinding *binding = data->bindings;
782     while (binding && binding->propertyIndex() != coreIndex)
783         binding = binding->nextBinding();
784
785     if (binding && valueTypeIndex != -1) {
786         if (binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) {
787             int index = coreIndex | (valueTypeIndex << 16);
788             binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
789         }
790     }
791
792     return binding;
793 }
794
795 void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex,
796                                                   QObject **targetObject, int *targetBindingIndex)
797 {
798     int coreIndex = bindingIndex & 0x0000FFFF;
799     int valueTypeIndex = (bindingIndex & 0xFFFF0000)?(bindingIndex >> 16):-1;
800
801     QQmlData *data = QQmlData::get(object, false);
802     if (data) {
803         QQmlPropertyData *propertyData =
804             data->propertyCache?data->propertyCache->property(coreIndex):0;
805         if (propertyData && propertyData->isAlias()) {
806             QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
807
808             QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1;
809             if (vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) {
810                 // This will either be a value type sub-reference or an alias to a value-type sub-reference not both
811                 Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
812
813                 int aBindingIndex = aCoreIndex;
814                 if (aValueTypeIndex != -1)
815                     aBindingIndex |= aValueTypeIndex << 16;
816                 else if (valueTypeIndex != -1)
817                     aBindingIndex |= valueTypeIndex << 16;
818
819                 findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex);
820                 return;
821             }
822         }
823     }
824
825     *targetObject = object;
826     *targetBindingIndex = bindingIndex;
827 }
828
829 QQmlAbstractBinding *
830 QQmlPropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeIndex,
831                                         QQmlAbstractBinding *newBinding, WriteFlags flags)
832 {
833     QQmlData *data = QQmlData::get(object, 0 != newBinding);
834     QQmlAbstractBinding *binding = 0;
835
836     if (data) {
837         QQmlPropertyData *propertyData =
838             data->propertyCache?data->propertyCache->property(coreIndex):0;
839         if (propertyData && propertyData->isAlias()) {
840             QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
841
842             QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1;
843             if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) {
844                 if (newBinding) newBinding->destroy();
845                 return 0;
846             }
847
848             // This will either be a value type sub-reference or an alias to a value-type sub-reference not both
849             Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
850             aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex;
851             return setBinding(aObject, aCoreIndex, aValueTypeIndex, newBinding, flags);
852         }
853     }
854
855     if (data && data->hasBindingBit(coreIndex)) {
856         binding = data->bindings;
857
858         while (binding && binding->propertyIndex() != coreIndex)
859             binding = binding->nextBinding();
860     }
861
862     int index = coreIndex;
863     if (valueTypeIndex != -1)
864         index |= (valueTypeIndex << 16);
865
866     if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy)
867         binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
868
869     if (binding) {
870         binding->removeFromObject();
871         binding->setEnabled(false, 0);
872     }
873
874     if (newBinding) {
875         if (newBinding->propertyIndex() != index || newBinding->object() != object)
876             newBinding->retargetBinding(object, index);
877
878         Q_ASSERT(newBinding->propertyIndex() == index);
879         Q_ASSERT(newBinding->object() == object);
880
881         newBinding->addToObject();
882         newBinding->setEnabled(true, flags);
883     }
884
885     return binding;
886 }
887
888 QQmlAbstractBinding *
889 QQmlPropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valueTypeIndex,
890                                                 QQmlAbstractBinding *newBinding)
891 {
892     QQmlData *data = QQmlData::get(object, 0 != newBinding);
893     QQmlAbstractBinding *binding = 0;
894
895     if (data) {
896         QQmlPropertyData *propertyData =
897             data->propertyCache?data->propertyCache->property(coreIndex):0;
898         if (propertyData && propertyData->isAlias()) {
899             QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
900
901             QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1;
902             if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) {
903                 if (newBinding) newBinding->destroy();
904                 return 0;
905             }
906
907             // This will either be a value type sub-reference or an alias to a value-type sub-reference not both
908             Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
909             aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex;
910             return setBindingNoEnable(aObject, aCoreIndex, aValueTypeIndex, newBinding);
911         }
912     }
913
914     if (data && data->hasBindingBit(coreIndex)) {
915         binding = data->bindings;
916
917         while (binding && binding->propertyIndex() != coreIndex)
918             binding = binding->nextBinding();
919     }
920
921     int index = coreIndex;
922     if (valueTypeIndex != -1)
923         index |= (valueTypeIndex << 16);
924
925     if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy)
926         binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
927
928     if (binding)
929         binding->removeFromObject();
930
931     if (newBinding) {
932         if (newBinding->propertyIndex() != index || newBinding->object() != object)
933             newBinding->retargetBinding(object, index);
934
935         Q_ASSERT(newBinding->propertyIndex() == index);
936         Q_ASSERT(newBinding->object() == object);
937
938         newBinding->addToObject();
939     }
940
941     return binding;
942 }
943
944 /*!
945     Activates a shared binding which was previously created but not added to the
946     object.  This is needed when an optimized binding is invalidated.
947 */
948 QQmlAbstractBinding *QQmlPropertyPrivate::activateSharedBinding(QQmlContextData *context,
949                                                                 int sharedIdx, WriteFlags flags)
950 {
951     QQmlAbstractBinding *newBinding = 0;
952     newBinding = context->v8bindings->binding(sharedIdx);
953
954     if (!newBinding)
955         return newBinding;
956
957     // This binding now references the bindings object
958     context->v8bindings->addref();
959
960     QObject *object = newBinding->object();
961     int pi = newBinding->propertyIndex();
962
963     int core = pi & 0x0000FFFF;
964     int vt = (pi & 0xFFFF0000)?(pi >> 16):-1;
965
966     return setBinding(object, core, vt, newBinding, flags);
967 }
968
969 /*!
970     Returns the expression associated with this signal property, or 0 if no
971     signal expression exists.
972 */
973 QQmlBoundSignalExpression *
974 QQmlPropertyPrivate::signalExpression(const QQmlProperty &that)
975 {
976     if (!(that.type() & QQmlProperty::SignalProperty))
977         return 0;
978
979     QQmlData *data = QQmlData::get(that.d->object);
980     if (!data)
981         return 0;
982
983     QQmlAbstractBoundSignal *signalHandler = data->signalHandlers;
984
985     while (signalHandler && signalHandler->index() != QQmlPropertyPrivate::get(that)->signalIndex())
986         signalHandler = signalHandler->m_nextSignal;
987
988     if (signalHandler)
989         return signalHandler->expression();
990
991     return 0;
992 }
993
994 /*!
995     Set the signal expression associated with this signal property to \a expr.
996     Returns the existing signal expression (if any), otherwise null.
997
998     A reference to \a expr will be added by QML.  Ownership of the return value
999     reference is assumed by the caller.
1000 */
1001 QQmlBoundSignalExpressionPointer
1002 QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that,
1003                                          QQmlBoundSignalExpression *expr)
1004 {
1005     if (expr)
1006         expr->addref();
1007     return QQmlPropertyPrivate::takeSignalExpression(that, expr);
1008 }
1009
1010 /*!
1011     Set the signal expression associated with this signal property to \a expr.
1012     Returns the existing signal expression (if any), otherwise null.
1013
1014     Ownership of \a expr transfers to QML.  Ownership of the return value
1015     reference is assumed by the caller.
1016 */
1017 QQmlBoundSignalExpressionPointer
1018 QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that,
1019                                          QQmlBoundSignalExpression *expr)
1020 {
1021     if (!(that.type() & QQmlProperty::SignalProperty)) {
1022         if (expr)
1023             expr->release();
1024         return 0;
1025     }
1026
1027     QQmlData *data = QQmlData::get(that.d->object, 0 != expr);
1028     if (!data)
1029         return 0;
1030
1031     QQmlAbstractBoundSignal *signalHandler = data->signalHandlers;
1032
1033     while (signalHandler && signalHandler->index() != QQmlPropertyPrivate::get(that)->signalIndex())
1034         signalHandler = signalHandler->m_nextSignal;
1035
1036     if (signalHandler)
1037         return signalHandler->takeExpression(expr);
1038
1039     if (expr) {
1040         int signalIndex = QQmlPropertyPrivate::get(that)->signalIndex();
1041         QQmlBoundSignal *signal = new QQmlBoundSignal(that.d->object, signalIndex, that.d->object,
1042                                                       expr->context()->engine);
1043         signal->takeExpression(expr);
1044     }
1045     return 0;
1046 }
1047
1048 /*!
1049     Returns the property value.
1050 */
1051 QVariant QQmlProperty::read() const
1052 {
1053     if (!d)
1054         return QVariant();
1055     if (!d->object)
1056         return QVariant();
1057
1058     if (type() & SignalProperty) {
1059
1060         return QVariant();
1061
1062     } else if (type() & Property) {
1063
1064         return d->readValueProperty();
1065
1066     }
1067     return QVariant();
1068 }
1069
1070 /*!
1071 Return the \a name property value of \a object.  This method is equivalent to:
1072 \code
1073     QQmlProperty p(object, name);
1074     p.read();
1075 \endcode
1076 */
1077 QVariant QQmlProperty::read(QObject *object, const QString &name)
1078 {
1079     QQmlProperty p(object, name);
1080     return p.read();
1081 }
1082
1083 /*!
1084   Return the \a name property value of \a object using the
1085   \l{QQmlContext} {context} \a ctxt.  This method is
1086   equivalent to:
1087
1088   \code
1089     QQmlProperty p(object, name, context);
1090     p.read();
1091   \endcode
1092 */
1093 QVariant QQmlProperty::read(QObject *object, const QString &name, QQmlContext *ctxt)
1094 {
1095     QQmlProperty p(object, name, ctxt);
1096     return p.read();
1097 }
1098
1099 /*!
1100
1101   Return the \a name property value of \a object using the environment
1102   for instantiating QML components that is provided by \a engine. .
1103   This method is equivalent to:
1104
1105   \code
1106     QQmlProperty p(object, name, engine);
1107     p.read();
1108   \endcode
1109 */
1110 QVariant QQmlProperty::read(QObject *object, const QString &name, QQmlEngine *engine)
1111 {
1112     QQmlProperty p(object, name, engine);
1113     return p.read();
1114 }
1115
1116 QVariant QQmlPropertyPrivate::readValueProperty()
1117 {
1118     if (isValueType()) {
1119
1120         QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType);
1121         Q_ASSERT(valueType);
1122         valueType->read(object, core.coreIndex);
1123         return valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType);
1124
1125     } else if (core.isQList()) {
1126
1127         QQmlListProperty<QObject> prop;
1128         void *args[] = { &prop, 0 };
1129         QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
1130         return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType, engine));
1131
1132     } else if (core.isQObject()) {
1133
1134         QObject *rv = 0;
1135         void *args[] = { &rv, 0 };
1136         QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
1137         return QVariant::fromValue(rv);
1138
1139     } else {
1140
1141         if (!core.propType) // Unregistered type
1142             return object->metaObject()->property(core.coreIndex).read(object);
1143
1144         QVariant value;
1145         int status = -1;
1146         void *args[] = { 0, &value, &status };
1147         if (core.propType == QMetaType::QVariant) {
1148             args[0] = &value;
1149         } else {
1150             value = QVariant(core.propType, (void*)0);
1151             args[0] = value.data();
1152         }
1153         QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
1154         if (core.propType != QMetaType::QVariant && args[0] != value.data())
1155             return QVariant((QVariant::Type)core.propType, args[0]);
1156
1157         return value;
1158     }
1159 }
1160
1161 // helper function to allow assignment / binding to QList<QUrl> properties.
1162 static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context)
1163 {
1164     QList<QUrl> urls;
1165     if (value.userType() == qMetaTypeId<QUrl>()) {
1166         urls.append(value.toUrl());
1167     } else if (value.userType() == qMetaTypeId<QString>()) {
1168         urls.append(QUrl(value.toString()));
1169     } else if (value.userType() == qMetaTypeId<QByteArray>()) {
1170         urls.append(QUrl(QString::fromUtf8(value.toByteArray())));
1171     } else if (value.userType() == qMetaTypeId<QList<QUrl> >()) {
1172         urls = value.value<QList<QUrl> >();
1173     } else if (value.userType() == qMetaTypeId<QStringList>()) {
1174         QStringList urlStrings = value.value<QStringList>();
1175         for (int i = 0; i < urlStrings.size(); ++i)
1176             urls.append(QUrl(urlStrings.at(i)));
1177     } else if (value.userType() == qMetaTypeId<QList<QString> >()) {
1178         QList<QString> urlStrings = value.value<QList<QString> >();
1179         for (int i = 0; i < urlStrings.size(); ++i)
1180             urls.append(QUrl(urlStrings.at(i)));
1181     } // note: QList<QByteArray> is not currently supported.
1182
1183     QList<QUrl> resolvedUrls;
1184     for (int i = 0; i < urls.size(); ++i) {
1185         QUrl u = urls.at(i);
1186         if (context && u.isRelative() && !u.isEmpty())
1187             u = context->resolvedUrl(u);
1188         resolvedUrls.append(u);
1189     }
1190
1191     return QVariant::fromValue<QList<QUrl> >(resolvedUrls);
1192 }
1193
1194 //writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC!
1195 bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags)
1196 {
1197     if (!object || !prop.isWritable())
1198         return false;
1199
1200     QVariant v = value;
1201     if (prop.isEnumType()) {
1202         QMetaEnum menum = prop.enumerator();
1203         if (v.userType() == QVariant::String
1204 #ifdef QT3_SUPPORT
1205             || v.userType() == QVariant::CString
1206 #endif
1207             ) {
1208             bool ok;
1209             if (prop.isFlagType())
1210                 v = QVariant(menum.keysToValue(value.toByteArray(), &ok));
1211             else
1212                 v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
1213             if (!ok)
1214                 return false;
1215         } else if (v.userType() != QVariant::Int && v.userType() != QVariant::UInt) {
1216             int enumMetaTypeId = QMetaType::type(QByteArray(menum.scope() + QByteArray("::") + menum.name()));
1217             if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData())
1218                 return false;
1219             v = QVariant(*reinterpret_cast<const int *>(v.constData()));
1220         }
1221         v.convert(QVariant::Int);
1222     }
1223
1224     // the status variable is changed by qt_metacall to indicate what it did
1225     // this feature is currently only used by QtDBus and should not be depended
1226     // upon. Don't change it without looking into QDBusAbstractInterface first
1227     // -1 (unchanged): normal qt_metacall, result stored in argv[0]
1228     // changed: result stored directly in value, return the value of status
1229     int status = -1;
1230     void *argv[] = { v.data(), &v, &status, &flags };
1231     QMetaObject::metacall(object, QMetaObject::WriteProperty, idx, argv);
1232     return status;
1233 }
1234
1235 bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags)
1236 {
1237     return writeValueProperty(object, core, value, effectiveContext(), flags);
1238 }
1239
1240 bool
1241 QQmlPropertyPrivate::writeValueProperty(QObject *object,
1242                                         const QQmlPropertyData &core,
1243                                         const QVariant &value,
1244                                         QQmlContextData *context, WriteFlags flags)
1245 {
1246     // Remove any existing bindings on this property
1247     if (!(flags & DontRemoveBinding) && object) {
1248         QQmlAbstractBinding *binding = setBinding(object, core.coreIndex,
1249                                                           core.getValueTypeCoreIndex(),
1250                                                           0, flags);
1251         if (binding) binding->destroy();
1252     }
1253
1254     bool rv = false;
1255     if (core.isValueTypeVirtual()) {
1256
1257         QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType);
1258         writeBack->read(object, core.coreIndex);
1259
1260         QQmlPropertyData data = core;
1261         data.setFlags(QQmlPropertyData::Flag(core.valueTypeFlags));
1262         data.coreIndex = core.valueTypeCoreIndex;
1263         data.propType = core.valueTypePropType;
1264
1265         rv = write(writeBack, data, value, context, flags);
1266
1267         writeBack->write(object, core.coreIndex, flags);
1268
1269     } else {
1270
1271         rv = write(object, core, value, context, flags);
1272
1273     }
1274
1275     return rv;
1276 }
1277
1278 bool QQmlPropertyPrivate::write(QObject *object, 
1279                                         const QQmlPropertyData &property,
1280                                         const QVariant &value, QQmlContextData *context,
1281                                         WriteFlags flags)
1282 {
1283     int coreIdx = property.coreIndex;
1284     int status = -1;    //for dbus
1285
1286     if (property.isEnum()) {
1287         QMetaProperty prop = object->metaObject()->property(property.coreIndex);
1288         QVariant v = value;
1289         // Enum values come through the script engine as doubles
1290         if (value.userType() == QVariant::Double) {
1291             double integral;
1292             double fractional = modf(value.toDouble(), &integral);
1293             if (qFuzzyIsNull(fractional))
1294                 v.convert(QVariant::Int);
1295         }
1296         return writeEnumProperty(prop, coreIdx, object, v, flags);
1297     }
1298
1299     int propertyType = property.propType;
1300     int variantType = value.userType();
1301
1302     QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context);
1303
1304     if (propertyType == QVariant::Url) {
1305
1306         QUrl u;
1307         bool found = false;
1308         if (variantType == QVariant::Url) {
1309             u = value.toUrl();
1310             found = true;
1311         } else if (variantType == QVariant::ByteArray) {
1312             QString input(QString::fromUtf8(value.toByteArray()));
1313             // Encoded dir-separators defeat QUrl processing - decode them first
1314             input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
1315             u = QUrl(input);
1316             found = true;
1317         } else if (variantType == QVariant::String) {
1318             QString input(value.toString());
1319             // Encoded dir-separators defeat QUrl processing - decode them first
1320             input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
1321             u = QUrl(input);
1322             found = true;
1323         }
1324
1325         if (!found)
1326             return false;
1327
1328         if (context && u.isRelative() && !u.isEmpty())
1329             u = context->resolvedUrl(u);
1330         int status = -1;
1331         void *argv[] = { &u, 0, &status, &flags };
1332         QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv);
1333
1334     } else if (propertyType == qMetaTypeId<QList<QUrl> >()) {
1335         QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl> >();
1336         int status = -1;
1337         void *argv[] = { &urlSeq, 0, &status, &flags };
1338         QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv);
1339     } else if (variantType == propertyType) {
1340
1341         void *a[] = { (void *)value.constData(), 0, &status, &flags };
1342         QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a);
1343
1344     } else if (qMetaTypeId<QVariant>() == propertyType) {
1345
1346         void *a[] = { (void *)&value, 0, &status, &flags };
1347         QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a);
1348
1349     } else if (property.isQObject()) {
1350
1351         QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, value.userType());
1352
1353         if (valMo.isNull())
1354             return false;
1355
1356         QObject *o = *(QObject **)value.constData();
1357         QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType);
1358
1359         if (o) valMo = o;
1360
1361         if (QQmlMetaObject::canConvert(valMo, propMo)) {
1362             void *args[] = { &o, 0, &status, &flags };
1363             QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args);
1364         } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) {
1365             // In the case of a null QObject, we assign the null if there is
1366             // any change that the null variant type could be up or down cast to
1367             // the property type.
1368             void *args[] = { &o, 0, &status, &flags };
1369             QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args);
1370         } else {
1371             return false;
1372         }
1373
1374     } else if (property.isQList()) {
1375
1376         QQmlMetaObject listType;
1377
1378         if (enginePriv) {
1379             listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType));
1380         } else {
1381             QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType));
1382             if (!type) return false;
1383             listType = type->baseMetaObject();
1384         }
1385         if (listType.isNull()) return false;
1386
1387         QQmlListProperty<void> prop;
1388         void *args[] = { &prop, 0 };
1389         QMetaObject::metacall(object, QMetaObject::ReadProperty, coreIdx, args);
1390
1391         if (!prop.clear) return false;
1392
1393         prop.clear(&prop);
1394
1395         if (value.userType() == qMetaTypeId<QQmlListReference>()) {
1396             QQmlListReference qdlr = value.value<QQmlListReference>();
1397
1398             for (int ii = 0; ii < qdlr.count(); ++ii) {
1399                 QObject *o = qdlr.at(ii);
1400                 if (o && !QQmlMetaObject::canConvert(o, listType))
1401                     o = 0;
1402                 prop.append(&prop, (void *)o);
1403             }
1404         } else if (value.userType() == qMetaTypeId<QList<QObject *> >()) {
1405             const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
1406
1407             for (int ii = 0; ii < list.count(); ++ii) {
1408                 QObject *o = list.at(ii);
1409                 if (o && !QQmlMetaObject::canConvert(o, listType))
1410                     o = 0;
1411                 prop.append(&prop, (void *)o);
1412             }
1413         } else {
1414             QObject *o = enginePriv?enginePriv->toQObject(value):QQmlMetaType::toQObject(value);
1415             if (o && !QQmlMetaObject::canConvert(o, listType))
1416                 o = 0;
1417             prop.append(&prop, (void *)o);
1418         }
1419
1420     } else {
1421         Q_ASSERT(variantType != propertyType);
1422
1423         bool ok = false;
1424         QVariant v;
1425         if (variantType == QVariant::String)
1426             v = QQmlStringConverters::variantFromString(value.toString(), propertyType, &ok);
1427         if (!ok) {
1428             v = value;
1429             if (v.convert(propertyType)) {
1430                 ok = true;
1431             } else if ((uint)propertyType >= QVariant::UserType && variantType == QVariant::String) {
1432                 QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType);
1433                 if (con) {
1434                     v = con(value.toString());
1435                     if (v.userType() == propertyType)
1436                         ok = true;
1437                 }
1438             }
1439         }
1440         if (!ok) {
1441             // the only other option is that they are assigning a single value
1442             // to a sequence type property (eg, an int to a QList<int> property).
1443             // Note that we've already handled single-value assignment to QList<QUrl> properties.
1444             if (variantType == QVariant::Int && propertyType == qMetaTypeId<QList<int> >()) {
1445                 QList<int> list;
1446                 list << value.toInt();
1447                 v = QVariant::fromValue<QList<int> >(list);
1448                 ok = true;
1449             } else if (variantType == QVariant::Double && propertyType == qMetaTypeId<QList<qreal> >()) {
1450                 QList<qreal> list;
1451                 list << value.toReal();
1452                 v = QVariant::fromValue<QList<qreal> >(list);
1453                 ok = true;
1454             } else if (variantType == QVariant::Bool && propertyType == qMetaTypeId<QList<bool> >()) {
1455                 QList<bool> list;
1456                 list << value.toBool();
1457                 v = QVariant::fromValue<QList<bool> >(list);
1458                 ok = true;
1459             } else if (variantType == QVariant::String && propertyType == qMetaTypeId<QList<QString> >()) {
1460                 QList<QString> list;
1461                 list << value.toString();
1462                 v = QVariant::fromValue<QList<QString> >(list);
1463                 ok = true;
1464             } else if (variantType == QVariant::String && propertyType == qMetaTypeId<QStringList>()) {
1465                 QStringList list;
1466                 list << value.toString();
1467                 v = QVariant::fromValue<QStringList>(list);
1468                 ok = true;
1469             }
1470         }
1471
1472         if (ok) {
1473             void *a[] = { (void *)v.constData(), 0, &status, &flags};
1474             QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a);
1475         } else {
1476             return false;
1477         }
1478     }
1479
1480     return true;
1481 }
1482
1483 // Returns true if successful, false if an error description was set on expression
1484 bool QQmlPropertyPrivate::writeBinding(QObject *object,
1485                                                const QQmlPropertyData &core,
1486                                                QQmlContextData *context,
1487                                                QQmlJavaScriptExpression *expression,
1488                                                v8::Handle<v8::Value> result, bool isUndefined,
1489                                                WriteFlags flags)
1490 {
1491     Q_ASSERT(object);
1492     Q_ASSERT(core.coreIndex != -1);
1493
1494     QQmlEngine *engine = context->engine;
1495     QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine);
1496
1497 #define QUICK_STORE(cpptype, conversion) \
1498         { \
1499             cpptype o = (conversion); \
1500             int status = -1; \
1501             void *argv[] = { &o, 0, &status, &flags }; \
1502             QMetaObject::metacall(object, QMetaObject::WriteProperty, core.coreIndex, argv); \
1503             return true; \
1504         } \
1505
1506
1507     if (!isUndefined && !core.isValueTypeVirtual()) {
1508         switch (core.propType) {
1509         case QMetaType::Int:
1510             if (result->IsInt32())
1511                 QUICK_STORE(int, result->Int32Value())
1512             else if (result->IsNumber())
1513                 QUICK_STORE(int, qRound(result->NumberValue()))
1514             break;
1515         case QMetaType::Double:
1516             if (result->IsNumber())
1517                 QUICK_STORE(double, result->NumberValue())
1518             break;
1519         case QMetaType::Float:
1520             if (result->IsNumber())
1521                 QUICK_STORE(float, result->NumberValue())
1522             break;
1523         case QMetaType::QString:
1524             if (result->IsString())
1525                 QUICK_STORE(QString, v8engine->toString(result))
1526             break;
1527         default:
1528             break;
1529         }
1530     }
1531 #undef QUICK_STORE
1532
1533     int type = core.isValueTypeVirtual()?core.valueTypePropType:core.propType;
1534
1535     QQmlJavaScriptExpression::DeleteWatcher watcher(expression);
1536
1537     QVariant value;
1538     bool isVarProperty = core.isVarProperty();
1539
1540     if (isUndefined) {
1541     } else if (core.isQList()) {
1542         value = v8engine->toVariant(result, qMetaTypeId<QList<QObject *> >());
1543     } else if (result->IsNull() && core.isQObject()) {
1544         value = QVariant::fromValue((QObject *)0);
1545     } else if (core.propType == qMetaTypeId<QList<QUrl> >()) {
1546         value = resolvedUrlSequence(v8engine->toVariant(result, qMetaTypeId<QList<QUrl> >()), context);
1547     } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) {
1548         value = v8engine->toVariant(result, type);
1549     }
1550
1551     if (expression->hasError()) {
1552         return false;
1553     } else if (isVarProperty) {
1554         if (!result.IsEmpty() && result->IsFunction()
1555                 && !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
1556             // we explicitly disallow this case to avoid confusion.  Users can still store one
1557             // in an array in a var property if they need to, but the common case is user error.
1558             expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
1559             return false;
1560         }
1561
1562         typedef QQmlVMEMetaObject VMEMO;
1563         QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
1564         Q_ASSERT(vmemo);
1565         vmemo->setVMEProperty(core.coreIndex, result);
1566     } else if (isUndefined && core.isResettable()) {
1567         void *args[] = { 0 };
1568         QMetaObject::metacall(object, QMetaObject::ResetProperty, core.coreIndex, args);
1569     } else if (isUndefined && type == qMetaTypeId<QVariant>()) {
1570         writeValueProperty(object, core, QVariant(), context, flags);
1571     } else if (type == qMetaTypeId<QJSValue>()) {
1572         if (!result.IsEmpty() && result->IsFunction()
1573                 && !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
1574             expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
1575             return false;
1576         }
1577         writeValueProperty(object, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags);
1578     } else if (isUndefined) {
1579         QString errorStr = QLatin1String("Unable to assign [undefined] to ");
1580         if (!QMetaType::typeName(type))
1581             errorStr += QLatin1String("[unknown property type]");
1582         else
1583             errorStr += QLatin1String(QMetaType::typeName(type));
1584         expression->delayedError()->setErrorDescription(errorStr);
1585         return false;
1586     } else if (result->IsFunction()) {
1587         if (!result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty())
1588             expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
1589         else
1590             expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
1591         return false;
1592     } else if (!writeValueProperty(object, core, value, context, flags)) {
1593
1594         if (watcher.wasDeleted())
1595             return true;
1596
1597         const char *valueType = 0;
1598         const char *propertyType = 0;
1599
1600         if (value.userType() == QMetaType::QObjectStar) {
1601             if (QObject *o = *(QObject **)value.constData()) {
1602                 valueType = o->metaObject()->className();
1603
1604                 QQmlMetaObject propertyMetaObject = rawMetaObjectForType(QQmlEnginePrivate::get(engine), type);
1605                 if (!propertyMetaObject.isNull())
1606                     propertyType = propertyMetaObject.className();
1607             }
1608         } else if (value.userType() != QVariant::Invalid) {
1609             valueType = QMetaType::typeName(value.userType());
1610         }
1611
1612         if (!valueType)
1613             valueType = "null";
1614         if (!propertyType)
1615             propertyType = QMetaType::typeName(type);
1616         if (!propertyType)
1617             propertyType = "[unknown property type]";
1618
1619         expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign ") +
1620                                                         QLatin1String(valueType) +
1621                                                         QLatin1String(" to ") +
1622                                                         QLatin1String(propertyType));
1623         return false;
1624     }
1625
1626     return true;
1627 }
1628
1629 QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engine, int userType)
1630 {
1631     if (engine) {
1632         return engine->rawMetaObjectForType(userType);
1633     } else {
1634         QQmlType *type = QQmlMetaType::qmlType(userType);
1635         return QQmlMetaObject(type?type->baseMetaObject():0);
1636     }
1637 }
1638
1639 /*!
1640     Sets the property value to \a value and returns true.
1641     Returns false if the property can't be set because the
1642     \a value is the wrong type, for example.
1643  */
1644 bool QQmlProperty::write(const QVariant &value) const
1645 {
1646     return QQmlPropertyPrivate::write(*this, value, 0);
1647 }
1648
1649 /*!
1650   Writes \a value to the \a name property of \a object.  This method
1651   is equivalent to:
1652
1653   \code
1654     QQmlProperty p(object, name);
1655     p.write(value);
1656   \endcode
1657 */
1658 bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value)
1659 {
1660     QQmlProperty p(object, name);
1661     return p.write(value);
1662 }
1663
1664 /*!
1665   Writes \a value to the \a name property of \a object using the
1666   \l{QQmlContext} {context} \a ctxt.  This method is
1667   equivalent to:
1668
1669   \code
1670     QQmlProperty p(object, name, ctxt);
1671     p.write(value);
1672   \endcode
1673 */
1674 bool QQmlProperty::write(QObject *object,
1675                                  const QString &name,
1676                                  const QVariant &value,
1677                                  QQmlContext *ctxt)
1678 {
1679     QQmlProperty p(object, name, ctxt);
1680     return p.write(value);
1681 }
1682
1683 /*!
1684
1685   Writes \a value to the \a name property of \a object using the
1686   environment for instantiating QML components that is provided by
1687   \a engine.  This method is equivalent to:
1688
1689   \code
1690     QQmlProperty p(object, name, engine);
1691     p.write(value);
1692   \endcode
1693 */
1694 bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value,
1695                                  QQmlEngine *engine)
1696 {
1697     QQmlProperty p(object, name, engine);
1698     return p.write(value);
1699 }
1700
1701 /*!
1702     Resets the property and returns true if the property is
1703     resettable.  If the property is not resettable, nothing happens
1704     and false is returned.
1705 */
1706 bool QQmlProperty::reset() const
1707 {
1708     if (isResettable()) {
1709         void *args[] = { 0 };
1710         QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex, args);
1711         return true;
1712     } else {
1713         return false;
1714     }
1715 }
1716
1717 bool QQmlPropertyPrivate::write(const QQmlProperty &that,
1718                                         const QVariant &value, WriteFlags flags)
1719 {
1720     if (!that.d)
1721         return false;
1722     if (that.d->object && that.type() & QQmlProperty::Property &&
1723         that.d->core.isValid() && that.isWritable())
1724         return that.d->writeValueProperty(value, flags);
1725     else
1726         return false;
1727 }
1728
1729 /*!
1730     Returns true if the property has a change notifier signal, otherwise false.
1731 */
1732 bool QQmlProperty::hasNotifySignal() const
1733 {
1734     if (type() & Property && d->object) {
1735         return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal();
1736     }
1737     return false;
1738 }
1739
1740 /*!
1741     Returns true if the property needs a change notifier signal for bindings
1742     to remain upto date, false otherwise.
1743
1744     Some properties, such as attached properties or those whose value never
1745     changes, do not require a change notifier.
1746 */
1747 bool QQmlProperty::needsNotifySignal() const
1748 {
1749     return type() & Property && !property().isConstant();
1750 }
1751
1752 /*!
1753     Connects the property's change notifier signal to the
1754     specified \a method of the \a dest object and returns
1755     true. Returns false if this metaproperty does not
1756     represent a regular Qt property or if it has no
1757     change notifier signal, or if the \a dest object does
1758     not have the specified \a method.
1759 */
1760 bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const
1761 {
1762     if (!(type() & Property) || !d->object)
1763         return false;
1764
1765     QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex);
1766     if (prop.hasNotifySignal()) {
1767         return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection);
1768     } else {
1769         return false;
1770     }
1771 }
1772
1773 /*!
1774     Connects the property's change notifier signal to the
1775     specified \a slot of the \a dest object and returns
1776     true. Returns false if this metaproperty does not
1777     represent a regular Qt property or if it has no
1778     change notifier signal, or if the \a dest object does
1779     not have the specified \a slot.
1780 */
1781 bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const
1782 {
1783     if (!(type() & Property) || !d->object)
1784         return false;
1785
1786     QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex);
1787     if (prop.hasNotifySignal()) {
1788         QByteArray signal('2' + prop.notifySignal().methodSignature());
1789         return QObject::connect(d->object, signal.constData(), dest, slot);
1790     } else  {
1791         return false;
1792     }
1793 }
1794
1795 /*!
1796     Return the Qt metaobject index of the property.
1797 */
1798 int QQmlProperty::index() const
1799 {
1800     return d ? d->core.coreIndex : -1;
1801 }
1802
1803 int QQmlPropertyPrivate::valueTypeCoreIndex(const QQmlProperty &that)
1804 {
1805     return that.d ? that.d->core.getValueTypeCoreIndex() : -1;
1806 }
1807
1808 /*!
1809     Returns the "property index" for use in bindings.  The top 16 bits are the value type
1810     offset, and 0 otherwise.  The bottom 16 bits are the regular property index.
1811 */
1812 int QQmlPropertyPrivate::bindingIndex(const QQmlProperty &that)
1813 {
1814     if (!that.d)
1815         return -1;
1816     return bindingIndex(that.d->core);
1817 }
1818
1819 int QQmlPropertyPrivate::bindingIndex(const QQmlPropertyData &that)
1820 {
1821     int rv = that.coreIndex;
1822     if (rv != -1 && that.isValueTypeVirtual())
1823         rv = rv | (that.valueTypeCoreIndex << 16);
1824     return rv;
1825 }
1826
1827 QQmlPropertyData
1828 QQmlPropertyPrivate::saveValueType(const QQmlPropertyData &base,
1829                                    const QMetaObject *subObject, int subIndex,
1830                                    QQmlEngine *)
1831 {
1832     QMetaProperty subProp = subObject->property(subIndex);
1833
1834     QQmlPropertyData core = base;
1835     core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual);
1836     core.valueTypeFlags = QQmlPropertyData::flagsForProperty(subProp);
1837     core.valueTypeCoreIndex = subIndex;
1838     core.valueTypePropType = subProp.userType();
1839
1840     return core;
1841 }
1842
1843 QQmlProperty
1844 QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data,
1845                                      QQmlContextData *ctxt)
1846 {
1847     QQmlProperty prop;
1848
1849     prop.d = new QQmlPropertyPrivate;
1850     prop.d->object = object;
1851     prop.d->context = ctxt;
1852     prop.d->engine = ctxt?ctxt->engine:0;
1853
1854     prop.d->core = data;
1855
1856     return prop;
1857 }
1858
1859 /*!
1860     Return the signal corresponding to \a name
1861 */
1862 QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const QByteArray &name)
1863 {
1864     Q_ASSERT(mo);
1865     int methods = mo->methodCount();
1866     for (int ii = methods - 1; ii >= 2; --ii) { // >= 2 to block the destroyed signal
1867         QMetaMethod method = mo->method(ii);
1868
1869         if (method.name() == name && (method.methodType() & QMetaMethod::Signal))
1870             return method;
1871     }
1872
1873     // If no signal is found, but the signal is of the form "onBlahChanged",
1874     // return the notify signal for the property "Blah"
1875     if (name.endsWith("Changed")) {
1876         QByteArray propName = name.mid(0, name.length() - 7);
1877         int propIdx = mo->indexOfProperty(propName.constData());
1878         if (propIdx >= 0) {
1879             QMetaProperty prop = mo->property(propIdx);
1880             if (prop.hasNotifySignal())
1881                 return prop.notifySignal();
1882         }
1883     }
1884
1885     return QMetaMethod();
1886 }
1887
1888 /*! \internal
1889     If \a indexInSignalRange is true, \a index is treated as a signal index
1890     (see QObjectPrivate::signalIndex()), otherwise it is treated as a
1891     method index (QMetaMethod::methodIndex()).
1892 */
1893 static inline void flush_vme_signal(const QObject *object, int index, bool indexInSignalRange)
1894 {
1895     QQmlData *data = static_cast<QQmlData *>(QObjectPrivate::get(const_cast<QObject *>(object))->declarativeData);
1896     if (data && data->propertyCache) {
1897         QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index)
1898                                                         : data->propertyCache->method(index);
1899
1900         if (property && property->isVMESignal()) {
1901             QQmlVMEMetaObject *vme;
1902             if (indexInSignalRange)
1903                 vme = QQmlVMEMetaObject::getForSignal(const_cast<QObject *>(object), index);
1904             else
1905                 vme = QQmlVMEMetaObject::getForMethod(const_cast<QObject *>(object), index);
1906             vme->connectAliasSignal(index, indexInSignalRange);
1907         }
1908     }
1909 }
1910
1911 /*!
1912 Connect \a sender \a signal_index to \a receiver \a method_index with the specified
1913 \a type and \a types.  This behaves identically to QMetaObject::connect() except that
1914 it connects any lazy "proxy" signal connections set up by QML.
1915
1916 It is possible that this logic should be moved to QMetaObject::connect().
1917 */
1918 bool QQmlPropertyPrivate::connect(const QObject *sender, int signal_index,
1919                                           const QObject *receiver, int method_index,
1920                                           int type, int *types)
1921 {
1922     static const bool indexInSignalRange = false;
1923     flush_vme_signal(sender, signal_index, indexInSignalRange);
1924     flush_vme_signal(receiver, method_index, indexInSignalRange);
1925
1926     return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
1927 }
1928
1929 /*! \internal
1930     \a signal_index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1931     This is different from QMetaMethod::methodIndex().
1932 */
1933 void QQmlPropertyPrivate::flushSignal(const QObject *sender, int signal_index)
1934 {
1935     static const bool indexInSignalRange = true;
1936     flush_vme_signal(sender, signal_index, indexInSignalRange);
1937 }
1938
1939 QT_END_NAMESPACE