1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativebinding_p.h"
43 #include "private/qdeclarativebinding_p_p.h"
45 #include "qdeclarative.h"
46 #include "qdeclarativecontext.h"
47 #include "qdeclarativeinfo.h"
48 #include "private/qdeclarativecontext_p.h"
49 #include "private/qdeclarativecompiler_p.h"
50 #include "private/qdeclarativedata_p.h"
51 #include "private/qdeclarativestringconverters_p.h"
52 #include "private/qdeclarativestate_p_p.h"
53 #include "private/qdeclarativedebugtrace_p.h"
56 #include <QtCore/qdebug.h>
60 QDeclarativeAbstractBinding::QDeclarativeAbstractBinding()
61 : m_object(0), m_propertyIndex(-1), m_mePtr(0), m_prevBinding(0), m_nextBinding(0)
65 QDeclarativeAbstractBinding::~QDeclarativeAbstractBinding()
67 Q_ASSERT(m_prevBinding == 0);
68 Q_ASSERT(m_mePtr == 0);
72 Destroy the binding. Use this instead of calling delete.
74 Bindings are free to implement their own memory management, so the delete operator is not
75 necessarily safe. The default implementation clears the binding, removes it from the object
78 void QDeclarativeAbstractBinding::destroy()
87 Add this binding to \a object.
89 This transfers ownership of the binding to the object, marks the object's property as
92 However, it does not enable the binding itself or call update() on it.
94 void QDeclarativeAbstractBinding::addToObject(QObject *object, int index)
98 if (m_object == object && m_propertyIndex == index)
103 Q_ASSERT(!m_prevBinding);
106 m_propertyIndex = index;
108 QDeclarativeData *data = QDeclarativeData::get(object, true);
110 if (index & 0xFF000000) {
113 int coreIndex = index & 0xFFFFFF;
115 // Find the value type proxy (if there is one)
116 QDeclarativeValueTypeProxyBinding *proxy = 0;
117 if (data->hasBindingBit(coreIndex)) {
118 QDeclarativeAbstractBinding *b = data->bindings;
119 while (b && b->propertyIndex() != coreIndex)
120 b = b->m_nextBinding;
121 Q_ASSERT(b && b->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy);
122 proxy = static_cast<QDeclarativeValueTypeProxyBinding *>(b);
126 proxy = new QDeclarativeValueTypeProxyBinding(object, coreIndex);
127 proxy->addToObject(object, coreIndex);
130 m_nextBinding = proxy->m_bindings;
131 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
132 m_prevBinding = &proxy->m_bindings;
133 proxy->m_bindings = this;
136 m_nextBinding = data->bindings;
137 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
138 m_prevBinding = &data->bindings;
139 data->bindings = this;
141 data->setBindingBit(m_object, index);
146 Remove the binding from the object.
148 void QDeclarativeAbstractBinding::removeFromObject()
151 int index = propertyIndex();
153 *m_prevBinding = m_nextBinding;
154 if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding;
158 if (index & 0xFF000000) {
159 // Value type - we don't remove the proxy from the object. It will sit their happily
160 // doing nothing until it is removed by a write, a binding change or it is reused
161 // to hold more sub-bindings.
162 } else if (m_object) {
163 QDeclarativeData *data = QDeclarativeData::get(m_object, false);
164 if (data) data->clearBindingBit(index);
168 m_propertyIndex = -1;
172 static void bindingDummyDeleter(QDeclarativeAbstractBinding *)
176 QDeclarativeAbstractBinding::Pointer QDeclarativeAbstractBinding::weakPointer()
178 if (m_selfPointer.isNull())
179 m_selfPointer = QSharedPointer<QDeclarativeAbstractBinding>(this, bindingDummyDeleter);
181 return m_selfPointer.toWeakRef();
184 void QDeclarativeAbstractBinding::clear()
192 QString QDeclarativeAbstractBinding::expression() const
194 return QLatin1String("<Unknown>");
197 QObject *QDeclarativeAbstractBinding::object() const
202 int QDeclarativeAbstractBinding::propertyIndex() const
204 return m_propertyIndex;
207 void QDeclarativeAbstractBinding::setEnabled(bool enabled, QDeclarativePropertyPrivate::WriteFlags flags)
209 if (enabled) update(flags);
212 QDeclarativeBinding::Identifier QDeclarativeBinding::Invalid = -1;
214 void QDeclarativeBindingPrivate::refresh()
216 Q_Q(QDeclarativeBinding);
220 QDeclarativeBindingPrivate::QDeclarativeBindingPrivate()
221 : updating(false), enabled(false), deleted(0)
225 QDeclarativeBindingPrivate::~QDeclarativeBindingPrivate()
227 if (deleted) *deleted = true;
230 QDeclarativeBinding::QDeclarativeBinding(void *data, QDeclarativeRefCount *rc, QObject *obj,
231 QDeclarativeContextData *ctxt, const QString &url, int lineNumber,
233 : QDeclarativeExpression(ctxt, data, rc, obj, url, lineNumber, *new QDeclarativeBindingPrivate)
236 setNotifyOnValueChanged(true);
239 QDeclarativeBinding *
240 QDeclarativeBinding::createBinding(Identifier id, QObject *obj, QDeclarativeContext *ctxt,
241 const QString &url, int lineNumber, QObject *parent)
246 QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(ctxt);
248 QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(qmlEngine(obj));
249 QDeclarativeCompiledData *cdata = 0;
250 QDeclarativeTypeData *typeData = 0;
251 if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
252 typeData = engine->typeLoader.get(ctxtdata->url);
253 cdata = typeData->compiledData();
255 QDeclarativeBinding *rv = cdata ? new QDeclarativeBinding((void*)cdata->datas.at(id).constData(), cdata, obj, ctxtdata, url, lineNumber, parent) : 0;
261 QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContext *ctxt,
263 : QDeclarativeExpression(QDeclarativeContextData::get(ctxt), obj, str, *new QDeclarativeBindingPrivate)
266 setNotifyOnValueChanged(true);
269 QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContextData *ctxt,
271 : QDeclarativeExpression(ctxt, obj, str, *new QDeclarativeBindingPrivate)
274 setNotifyOnValueChanged(true);
277 QDeclarativeBinding::QDeclarativeBinding(const QScriptValue &func, QObject *obj, QDeclarativeContextData *ctxt, QObject *parent)
278 : QDeclarativeExpression(ctxt, obj, func, *new QDeclarativeBindingPrivate)
281 setNotifyOnValueChanged(true);
284 QDeclarativeBinding::~QDeclarativeBinding()
288 void QDeclarativeBinding::setTarget(const QDeclarativeProperty &prop)
290 Q_D(QDeclarativeBinding);
296 QDeclarativeProperty QDeclarativeBinding::property() const
298 Q_D(const QDeclarativeBinding);
302 void QDeclarativeBinding::setEvaluateFlags(EvaluateFlags flags)
304 Q_D(QDeclarativeBinding);
305 d->setEvaluateFlags(QDeclarativeQtScriptExpression::EvaluateFlags(static_cast<int>(flags)));
308 QDeclarativeBinding::EvaluateFlags QDeclarativeBinding::evaluateFlags() const
310 Q_D(const QDeclarativeBinding);
311 return QDeclarativeBinding::EvaluateFlags(static_cast<int>(d->evaluateFlags()));
315 class QDeclarativeBindingProfiler {
317 QDeclarativeBindingProfiler(QDeclarativeBinding *bind)
319 QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding);
320 QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Binding, bind->expression());
321 QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Binding, bind->sourceFile(), bind->lineNumber());
324 ~QDeclarativeBindingProfiler()
326 QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding);
330 void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags)
332 Q_D(QDeclarativeBinding);
334 if (!d->enabled || !d->context() || !d->context()->isValid())
338 QDeclarativeBindingProfiler prof(this);
340 bool wasDeleted = false;
341 d->deleted = &wasDeleted;
343 if (d->property.propertyType() == qMetaTypeId<QDeclarativeBinding *>()) {
345 int idx = d->property.index();
348 QDeclarativeBinding *t = this;
350 void *a[] = { &t, 0, &status, &flags };
351 QMetaObject::metacall(d->property.object(),
352 QMetaObject::WriteProperty,
359 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context()->engine);
361 bool isUndefined = false;
364 QScriptValue scriptValue = d->scriptValue(0, &isUndefined);
368 if (d->property.propertyTypeCategory() == QDeclarativeProperty::List) {
369 value = ep->scriptValueToVariant(scriptValue, qMetaTypeId<QList<QObject *> >());
370 } else if (scriptValue.isNull() &&
371 d->property.propertyTypeCategory() == QDeclarativeProperty::Object) {
372 value = QVariant::fromValue((QObject *)0);
374 value = ep->scriptValueToVariant(scriptValue, d->property.propertyType());
375 if (value.userType() == QMetaType::QObjectStar && !qvariant_cast<QObject*>(value)) {
376 // If the object is null, we extract the predicted type. While this isn't
377 // 100% reliable, in many cases it gives us better error messages if we
378 // assign this null-object to an incompatible property
379 int type = ep->objectClass->objectType(scriptValue);
381 value = QVariant(type, (void *)&o);
386 if (d->error.isValid()) {
388 } else if (isUndefined && d->property.isResettable()) {
392 } else if (isUndefined && d->property.propertyType() == qMetaTypeId<QVariant>()) {
394 QDeclarativePropertyPrivate::write(d->property, QVariant(), flags);
396 } else if (isUndefined) {
398 QUrl url = QUrl(d->url);
400 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
402 d->error.setUrl(url);
403 d->error.setLine(line);
404 d->error.setColumn(-1);
405 d->error.setDescription(QLatin1String("Unable to assign [undefined] to ") +
406 QLatin1String(QMetaType::typeName(d->property.propertyType())) +
407 QLatin1String(" ") + d->property.name());
409 } else if (!scriptValue.isRegExp() && scriptValue.isFunction()) {
411 QUrl url = QUrl(d->url);
413 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
415 d->error.setUrl(url);
416 d->error.setLine(line);
417 d->error.setColumn(-1);
418 d->error.setDescription(QLatin1String("Unable to assign a function to a property."));
420 } else if (d->property.object() &&
421 !QDeclarativePropertyPrivate::write(d->property, value, flags)) {
426 QUrl url = QUrl(d->url);
428 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
430 const char *valueType = 0;
431 if (value.userType() == QVariant::Invalid) valueType = "null";
432 else valueType = QMetaType::typeName(value.userType());
434 d->error.setUrl(url);
435 d->error.setLine(line);
436 d->error.setColumn(-1);
437 d->error.setDescription(QLatin1String("Unable to assign ") +
438 QLatin1String(valueType) +
439 QLatin1String(" to ") +
440 QLatin1String(QMetaType::typeName(d->property.propertyType())));
446 if (d->error.isValid()) {
447 if (!d->addError(ep)) ep->warning(this->error());
456 qmlInfo(d->property.object()) << tr("Binding loop detected for property \"%1\"").arg(d->property.name());
460 void QDeclarativeBindingPrivate::emitValueChanged()
462 Q_Q(QDeclarativeBinding);
466 void QDeclarativeBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
468 Q_D(QDeclarativeBinding);
470 setNotifyOnValueChanged(e);
476 bool QDeclarativeBinding::enabled() const
478 Q_D(const QDeclarativeBinding);
483 QString QDeclarativeBinding::expression() const
485 return QDeclarativeExpression::expression();
488 QDeclarativeValueTypeProxyBinding::QDeclarativeValueTypeProxyBinding(QObject *o, int index)
489 : m_object(o), m_index(index), m_bindings(0)
493 QDeclarativeValueTypeProxyBinding::~QDeclarativeValueTypeProxyBinding()
496 QDeclarativeAbstractBinding *binding = m_bindings;
497 binding->setEnabled(false, 0);
502 void QDeclarativeValueTypeProxyBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
505 QDeclarativeAbstractBinding *bindings = m_bindings;
506 recursiveEnable(bindings, flags);
508 QDeclarativeAbstractBinding *bindings = m_bindings;
509 recursiveDisable(bindings);
513 void QDeclarativeValueTypeProxyBinding::recursiveEnable(QDeclarativeAbstractBinding *b, QDeclarativePropertyPrivate::WriteFlags flags)
518 recursiveEnable(b->m_nextBinding, flags);
521 b->setEnabled(true, flags);
524 void QDeclarativeValueTypeProxyBinding::recursiveDisable(QDeclarativeAbstractBinding *b)
529 recursiveDisable(b->m_nextBinding);
532 b->setEnabled(false, 0);
535 void QDeclarativeValueTypeProxyBinding::update(QDeclarativePropertyPrivate::WriteFlags)
539 QDeclarativeAbstractBinding *QDeclarativeValueTypeProxyBinding::binding(int propertyIndex)
541 QDeclarativeAbstractBinding *binding = m_bindings;
543 while (binding && binding->propertyIndex() != propertyIndex)
544 binding = binding->m_nextBinding;
550 Removes a collection of bindings, corresponding to the set bits in \a mask.
552 void QDeclarativeValueTypeProxyBinding::removeBindings(quint32 mask)
554 QDeclarativeAbstractBinding *binding = m_bindings;
556 if (mask & (1 << (binding->propertyIndex() >> 24))) {
557 QDeclarativeAbstractBinding *remove = binding;
558 binding = remove->m_nextBinding;
559 *remove->m_prevBinding = remove->m_nextBinding;
560 if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
561 remove->m_prevBinding = 0;
562 remove->m_nextBinding = 0;
565 binding = binding->m_nextBinding;