1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qqmlbinding_p.h"
43 #include "qqmlbinding_p_p.h"
46 #include "qqmlcontext.h"
48 #include "qqmlcompiler_p.h"
49 #include "qqmldata_p.h"
50 #include <private/qqmlprofilerservice_p.h>
51 #include <private/qqmltrace_p.h>
54 #include <QtCore/qdebug.h>
58 QQmlAbstractBinding::QQmlAbstractBinding()
59 : m_prevBinding(0), m_nextBinding(0)
63 QQmlAbstractBinding::~QQmlAbstractBinding()
65 Q_ASSERT(m_prevBinding == 0);
66 Q_ASSERT(*m_mePtr == 0);
70 Destroy the binding. Use this instead of calling delete.
72 Bindings are free to implement their own memory management, so the delete operator is not
73 necessarily safe. The default implementation clears the binding, removes it from the object
76 void QQmlAbstractBinding::destroy()
85 Add this binding to \a object.
87 This transfers ownership of the binding to the object, marks the object's property as
90 However, it does not enable the binding itself or call update() on it.
92 void QQmlAbstractBinding::addToObject()
94 Q_ASSERT(!m_prevBinding);
96 QObject *obj = object();
99 int index = propertyIndex();
101 QQmlData *data = QQmlData::get(obj, true);
103 if (index & 0xFF000000) {
106 int coreIndex = index & 0xFFFFFF;
108 // Find the value type proxy (if there is one)
109 QQmlValueTypeProxyBinding *proxy = 0;
110 if (data->hasBindingBit(coreIndex)) {
111 QQmlAbstractBinding *b = data->bindings;
112 while (b && b->propertyIndex() != coreIndex)
113 b = b->m_nextBinding;
114 Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy);
115 proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
119 proxy = new QQmlValueTypeProxyBinding(obj, coreIndex);
121 Q_ASSERT(proxy->propertyIndex() == coreIndex);
122 Q_ASSERT(proxy->object() == obj);
124 proxy->addToObject();
127 m_nextBinding = proxy->m_bindings;
128 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
129 m_prevBinding = &proxy->m_bindings;
130 proxy->m_bindings = this;
133 m_nextBinding = data->bindings;
134 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
135 m_prevBinding = &data->bindings;
136 data->bindings = this;
138 data->setBindingBit(obj, index);
143 Remove the binding from the object.
145 void QQmlAbstractBinding::removeFromObject()
148 int index = propertyIndex();
150 *m_prevBinding = m_nextBinding;
151 if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding;
155 if (index & 0xFF000000) {
156 // Value type - we don't remove the proxy from the object. It will sit their happily
157 // doing nothing until it is removed by a write, a binding change or it is reused
158 // to hold more sub-bindings.
159 } else if (QObject *obj = object()) {
160 QQmlData *data = QQmlData::get(obj, false);
161 if (data) data->clearBindingBit(index);
166 static void bindingDummyDeleter(QQmlAbstractBinding *)
170 QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer()
172 if (m_mePtr.value().isNull())
173 m_mePtr.value() = QSharedPointer<QQmlAbstractBinding>(this, bindingDummyDeleter);
175 return m_mePtr.value().toWeakRef();
178 void QQmlAbstractBinding::clear()
180 if (!m_mePtr.isNull()) {
186 void QQmlAbstractBinding::retargetBinding(QObject *, int)
188 qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding.");
191 QString QQmlAbstractBinding::expression() const
193 return QLatin1String("<Unknown>");
196 void QQmlAbstractBinding::setEnabled(bool enabled, QQmlPropertyPrivate::WriteFlags flags)
198 if (enabled) update(flags);
201 QQmlBinding::Identifier QQmlBinding::Invalid = -1;
203 void QQmlBindingPrivate::refresh()
209 QQmlBindingPrivate::QQmlBindingPrivate()
210 : updating(false), enabled(false), target(), targetProperty(0)
214 QQmlBindingPrivate::~QQmlBindingPrivate()
219 QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt,
220 const QString &url, int lineNumber, QObject *parent)
225 QQmlContextData *ctxtdata = QQmlContextData::get(ctxt);
227 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(ctxt->engine());
228 QQmlCompiledData *cdata = 0;
229 QQmlTypeData *typeData = 0;
230 if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
231 typeData = engine->typeLoader.get(ctxtdata->url);
232 cdata = typeData->compiledData();
234 QQmlBinding *rv = cdata ? new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, url, lineNumber, 0, parent) : 0;
242 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt,
244 : QQmlExpression(QQmlContextData::get(ctxt), obj, str, *new QQmlBindingPrivate)
247 setNotifyOnValueChanged(true);
250 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt,
252 : QQmlExpression(ctxt, obj, str, *new QQmlBindingPrivate)
255 setNotifyOnValueChanged(true);
258 QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj,
259 QQmlContextData *ctxt,
260 const QString &url, int lineNumber, int columnNumber,
262 : QQmlExpression(ctxt, obj, str, isRewritten, url, lineNumber, columnNumber, *new QQmlBindingPrivate)
265 setNotifyOnValueChanged(true);
271 To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>.
273 v8::Handle<v8::Function> function;
274 new QQmlBinding(&function, scope, ctxt);
276 QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt,
278 : QQmlExpression(ctxt, obj, functionPtr, *new QQmlBindingPrivate)
281 setNotifyOnValueChanged(true);
284 QQmlBinding::~QQmlBinding()
288 void QQmlBinding::setTarget(const QQmlProperty &prop)
292 d->target = d->property.object();
293 d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex();
298 void QQmlBinding::setTarget(QObject *object,
299 const QQmlPropertyData &core,
300 QQmlContextData *ctxt)
303 d->property = QQmlPropertyPrivate::restore(object, core, ctxt);
304 d->target = d->property.object();
305 d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex();
310 QQmlProperty QQmlBinding::property() const
312 Q_D(const QQmlBinding);
316 void QQmlBinding::setEvaluateFlags(EvaluateFlags flags)
319 d->setRequiresThisObject(flags & RequiresThisObject);
322 QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const
324 Q_D(const QQmlBinding);
325 return d->requiresThisObject()?RequiresThisObject:None;
328 void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
332 if (!d->enabled || !d->context() || !d->context()->isValid())
335 QQmlTrace trace("General Binding Update");
336 trace.addDetail("URL", d->url);
337 trace.addDetail("Line", d->line);
338 trace.addDetail("Column", d->columnNumber);
341 QQmlBindingProfiler prof(d->url, d->line, d->column);
343 prof.addDetail(expression());
346 QQmlAbstractExpression::DeleteWatcher watcher(d);
348 if (d->property.propertyType() == qMetaTypeId<QQmlBinding *>()) {
350 int idx = d->property.index();
353 QQmlBinding *t = this;
355 void *a[] = { &t, 0, &status, &flags };
356 QMetaObject::metacall(d->property.object(),
357 QMetaObject::WriteProperty,
361 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->context()->engine);
362 ep->referenceScarceResources();
364 bool isUndefined = false;
366 v8::HandleScope handle_scope;
367 v8::Context::Scope scope(ep->v8engine()->context());
368 v8::Local<v8::Value> result = d->v8value(0, &isUndefined);
370 trace.event("writing binding result");
372 bool needsErrorData = false;
373 if (!watcher.wasDeleted() && !d->hasError())
374 needsErrorData = !QQmlPropertyPrivate::writeBinding(d->property, d->context(),
378 if (!watcher.wasDeleted()) {
380 if (needsErrorData) {
381 QUrl url = QUrl(d->url);
383 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
385 d->delayedError()->error.setUrl(url);
386 d->delayedError()->error.setLine(line);
387 d->delayedError()->error.setColumn(-1);
391 if (!d->delayedError()->addError(ep)) ep->warning(this->error());
398 ep->dereferenceScarceResources();
401 if (!watcher.wasDeleted())
404 QQmlBindingPrivate::printBindingLoopError(d->property);
408 void QQmlBindingPrivate::printBindingLoopError(QQmlProperty &prop)
410 qmlInfo(prop.object()) << QQmlBinding::tr("Binding loop detected for property \"%1\"").arg(prop.name());
413 void QQmlBindingPrivate::expressionChanged()
419 void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
423 setNotifyOnValueChanged(e);
429 bool QQmlBinding::enabled() const
431 Q_D(const QQmlBinding);
436 QString QQmlBinding::expression() const
438 return QQmlExpression::expression();
441 int QQmlBinding::propertyIndex() const
443 Q_D(const QQmlBinding);
444 return d->targetProperty;
447 QObject *QQmlBinding::object() const
449 Q_D(const QQmlBinding);
453 void QQmlBinding::retargetBinding(QObject *t, int i)
457 d->targetProperty = i;
460 QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index)
461 : m_object(o), m_index(index), m_bindings(0)
465 QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding()
468 QQmlAbstractBinding *binding = m_bindings;
469 binding->setEnabled(false, 0);
474 void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
477 QQmlAbstractBinding *bindings = m_bindings;
478 recursiveEnable(bindings, flags);
480 QQmlAbstractBinding *bindings = m_bindings;
481 recursiveDisable(bindings);
485 void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags)
490 recursiveEnable(b->m_nextBinding, flags);
493 b->setEnabled(true, flags);
496 void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b)
501 recursiveDisable(b->m_nextBinding);
504 b->setEnabled(false, 0);
507 void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags)
511 QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex)
513 QQmlAbstractBinding *binding = m_bindings;
515 while (binding && binding->propertyIndex() != propertyIndex)
516 binding = binding->m_nextBinding;
522 Removes a collection of bindings, corresponding to the set bits in \a mask.
524 void QQmlValueTypeProxyBinding::removeBindings(quint32 mask)
526 QQmlAbstractBinding *binding = m_bindings;
528 if (mask & (1 << (binding->propertyIndex() >> 24))) {
529 QQmlAbstractBinding *remove = binding;
530 binding = remove->m_nextBinding;
531 *remove->m_prevBinding = remove->m_nextBinding;
532 if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
533 remove->m_prevBinding = 0;
534 remove->m_nextBinding = 0;
537 binding = binding->m_nextBinding;
542 int QQmlValueTypeProxyBinding::propertyIndex() const
547 QObject *QQmlValueTypeProxyBinding::object() const