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);
342 prof.addDetail(expression());
345 QQmlAbstractExpression::DeleteWatcher watcher(d);
347 if (d->property.propertyType() == qMetaTypeId<QQmlBinding *>()) {
349 int idx = d->property.index();
352 QQmlBinding *t = this;
354 void *a[] = { &t, 0, &status, &flags };
355 QMetaObject::metacall(d->property.object(),
356 QMetaObject::WriteProperty,
360 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->context()->engine);
361 ep->referenceScarceResources();
363 bool isUndefined = false;
365 v8::HandleScope handle_scope;
366 v8::Context::Scope scope(ep->v8engine()->context());
367 v8::Local<v8::Value> result = d->v8value(0, &isUndefined);
369 trace.event("writing binding result");
371 bool needsErrorData = false;
372 if (!watcher.wasDeleted() && !d->hasError())
373 needsErrorData = !QQmlPropertyPrivate::writeBinding(d->property, d->context(),
377 if (!watcher.wasDeleted()) {
379 if (needsErrorData) {
380 QUrl url = QUrl(d->url);
382 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
384 d->delayedError()->error.setUrl(url);
385 d->delayedError()->error.setLine(line);
386 d->delayedError()->error.setColumn(-1);
390 if (!d->delayedError()->addError(ep)) ep->warning(this->error());
397 ep->dereferenceScarceResources();
400 if (!watcher.wasDeleted())
403 QQmlBindingPrivate::printBindingLoopError(d->property);
407 void QQmlBindingPrivate::printBindingLoopError(QQmlProperty &prop)
409 qmlInfo(prop.object()) << QQmlBinding::tr("Binding loop detected for property \"%1\"").arg(prop.name());
412 void QQmlBindingPrivate::expressionChanged()
418 void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
422 setNotifyOnValueChanged(e);
428 bool QQmlBinding::enabled() const
430 Q_D(const QQmlBinding);
435 QString QQmlBinding::expression() const
437 return QQmlExpression::expression();
440 int QQmlBinding::propertyIndex() const
442 Q_D(const QQmlBinding);
443 return d->targetProperty;
446 QObject *QQmlBinding::object() const
448 Q_D(const QQmlBinding);
452 void QQmlBinding::retargetBinding(QObject *t, int i)
456 d->targetProperty = i;
459 QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index)
460 : m_object(o), m_index(index), m_bindings(0)
464 QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding()
467 QQmlAbstractBinding *binding = m_bindings;
468 binding->setEnabled(false, 0);
473 void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
476 QQmlAbstractBinding *bindings = m_bindings;
477 recursiveEnable(bindings, flags);
479 QQmlAbstractBinding *bindings = m_bindings;
480 recursiveDisable(bindings);
484 void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags)
489 recursiveEnable(b->m_nextBinding, flags);
492 b->setEnabled(true, flags);
495 void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b)
500 recursiveDisable(b->m_nextBinding);
503 b->setEnabled(false, 0);
506 void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags)
510 QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex)
512 QQmlAbstractBinding *binding = m_bindings;
514 while (binding && binding->propertyIndex() != propertyIndex)
515 binding = binding->m_nextBinding;
521 Removes a collection of bindings, corresponding to the set bits in \a mask.
523 void QQmlValueTypeProxyBinding::removeBindings(quint32 mask)
525 QQmlAbstractBinding *binding = m_bindings;
527 if (mask & (1 << (binding->propertyIndex() >> 24))) {
528 QQmlAbstractBinding *remove = binding;
529 binding = remove->m_nextBinding;
530 *remove->m_prevBinding = remove->m_nextBinding;
531 if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
532 remove->m_prevBinding = 0;
533 remove->m_nextBinding = 0;
536 binding = binding->m_nextBinding;
541 int QQmlValueTypeProxyBinding::propertyIndex() const
546 QObject *QQmlValueTypeProxyBinding::object() const