source: trunk/src/declarative/qml/qdeclarativebinding.cpp@ 900

Last change on this file since 900 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 15.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativebinding_p.h"
43#include "private/qdeclarativebinding_p_p.h"
44
45#include "qdeclarative.h"
46#include "qdeclarativecontext.h"
47#include "qdeclarativeinfo.h"
48#include "private/qdeclarativecontext_p.h"
49#include "private/qdeclarativedata_p.h"
50#include "private/qdeclarativestringconverters_p.h"
51#include "private/qdeclarativestate_p_p.h"
52
53#include <QVariant>
54#include <QtCore/qdebug.h>
55
56QT_BEGIN_NAMESPACE
57
58QDeclarativeAbstractBinding::QDeclarativeAbstractBinding()
59: m_object(0), m_propertyIndex(-1), m_mePtr(0), m_prevBinding(0), m_nextBinding(0)
60{
61}
62
63QDeclarativeAbstractBinding::~QDeclarativeAbstractBinding()
64{
65 Q_ASSERT(m_prevBinding == 0);
66 Q_ASSERT(m_mePtr == 0);
67}
68
69/*!
70Destroy the binding. Use this instead of calling delete.
71
72Bindings are free to implement their own memory management, so the delete operator is not
73necessarily safe. The default implementation clears the binding, removes it from the object
74and calls delete.
75*/
76void QDeclarativeAbstractBinding::destroy()
77{
78 removeFromObject();
79 clear();
80
81 delete this;
82}
83
84/*!
85Add this binding to \a object.
86
87This transfers ownership of the binding to the object, marks the object's property as
88being bound.
89
90However, it does not enable the binding itself or call update() on it.
91*/
92void QDeclarativeAbstractBinding::addToObject(QObject *object, int index)
93{
94 Q_ASSERT(object);
95
96 if (m_object == object && m_propertyIndex == index)
97 return;
98
99 removeFromObject();
100
101 Q_ASSERT(!m_prevBinding);
102
103 m_object = object;
104 m_propertyIndex = index;
105
106 QDeclarativeData *data = QDeclarativeData::get(object, true);
107
108 if (index & 0xFF000000) {
109 // Value type
110
111 int coreIndex = index & 0xFFFFFF;
112
113 // Find the value type proxy (if there is one)
114 QDeclarativeValueTypeProxyBinding *proxy = 0;
115 if (data->hasBindingBit(coreIndex)) {
116 QDeclarativeAbstractBinding *b = data->bindings;
117 while (b && b->propertyIndex() != coreIndex)
118 b = b->m_nextBinding;
119 Q_ASSERT(b && b->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy);
120 proxy = static_cast<QDeclarativeValueTypeProxyBinding *>(b);
121 }
122
123 if (!proxy) {
124 proxy = new QDeclarativeValueTypeProxyBinding(object, coreIndex);
125 proxy->addToObject(object, coreIndex);
126 }
127
128 m_nextBinding = proxy->m_bindings;
129 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
130 m_prevBinding = &proxy->m_bindings;
131 proxy->m_bindings = this;
132
133 } else {
134 m_nextBinding = data->bindings;
135 if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
136 m_prevBinding = &data->bindings;
137 data->bindings = this;
138
139 data->setBindingBit(m_object, index);
140 }
141}
142
143/*!
144Remove the binding from the object.
145*/
146void QDeclarativeAbstractBinding::removeFromObject()
147{
148 if (m_prevBinding) {
149 int index = propertyIndex();
150
151 *m_prevBinding = m_nextBinding;
152 if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding;
153 m_prevBinding = 0;
154 m_nextBinding = 0;
155
156 if (index & 0xFF000000) {
157 // Value type - we don't remove the proxy from the object. It will sit their happily
158 // doing nothing until it is removed by a write, a binding change or it is reused
159 // to hold more sub-bindings.
160 } else if (m_object) {
161 QDeclarativeData *data = QDeclarativeData::get(m_object, false);
162 if (data) data->clearBindingBit(index);
163 }
164
165 m_object = 0;
166 m_propertyIndex = -1;
167 }
168}
169
170static void bindingDummyDeleter(QDeclarativeAbstractBinding *)
171{
172}
173
174QDeclarativeAbstractBinding::Pointer QDeclarativeAbstractBinding::weakPointer()
175{
176 if (m_selfPointer.isNull())
177 m_selfPointer = QSharedPointer<QDeclarativeAbstractBinding>(this, bindingDummyDeleter);
178
179 return m_selfPointer.toWeakRef();
180}
181
182void QDeclarativeAbstractBinding::clear()
183{
184 if (m_mePtr) {
185 *m_mePtr = 0;
186 m_mePtr = 0;
187 }
188}
189
190QString QDeclarativeAbstractBinding::expression() const
191{
192 return QLatin1String("<Unknown>");
193}
194
195QObject *QDeclarativeAbstractBinding::object() const
196{
197 return m_object;
198}
199
200int QDeclarativeAbstractBinding::propertyIndex() const
201{
202 return m_propertyIndex;
203}
204
205void QDeclarativeAbstractBinding::setEnabled(bool enabled, QDeclarativePropertyPrivate::WriteFlags flags)
206{
207 if (enabled) update(flags);
208}
209
210void QDeclarativeBindingPrivate::refresh()
211{
212 Q_Q(QDeclarativeBinding);
213 q->update();
214}
215
216QDeclarativeBindingPrivate::QDeclarativeBindingPrivate()
217: updating(false), enabled(false), deleted(0)
218{
219}
220
221QDeclarativeBindingPrivate::~QDeclarativeBindingPrivate()
222{
223 if (deleted) *deleted = true;
224}
225
226QDeclarativeBinding::QDeclarativeBinding(void *data, QDeclarativeRefCount *rc, QObject *obj,
227 QDeclarativeContextData *ctxt, const QString &url, int lineNumber,
228 QObject *parent)
229: QDeclarativeExpression(ctxt, data, rc, obj, url, lineNumber, *new QDeclarativeBindingPrivate)
230{
231 setParent(parent);
232 setNotifyOnValueChanged(true);
233}
234
235QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContext *ctxt,
236 QObject *parent)
237: QDeclarativeExpression(QDeclarativeContextData::get(ctxt), obj, str, *new QDeclarativeBindingPrivate)
238{
239 setParent(parent);
240 setNotifyOnValueChanged(true);
241}
242
243QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContextData *ctxt,
244 QObject *parent)
245: QDeclarativeExpression(ctxt, obj, str, *new QDeclarativeBindingPrivate)
246{
247 setParent(parent);
248 setNotifyOnValueChanged(true);
249}
250
251QDeclarativeBinding::~QDeclarativeBinding()
252{
253}
254
255void QDeclarativeBinding::setTarget(const QDeclarativeProperty &prop)
256{
257 Q_D(QDeclarativeBinding);
258 d->property = prop;
259
260 update();
261}
262
263QDeclarativeProperty QDeclarativeBinding::property() const
264{
265 Q_D(const QDeclarativeBinding);
266 return d->property;
267}
268
269void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags)
270{
271 Q_D(QDeclarativeBinding);
272
273 if (!d->enabled || !d->context() || !d->context()->isValid())
274 return;
275
276 if (!d->updating) {
277 d->updating = true;
278 bool wasDeleted = false;
279 d->deleted = &wasDeleted;
280
281 if (d->property.propertyType() == qMetaTypeId<QDeclarativeBinding *>()) {
282
283 int idx = d->property.index();
284 Q_ASSERT(idx != -1);
285
286 QDeclarativeBinding *t = this;
287 int status = -1;
288 void *a[] = { &t, 0, &status, &flags };
289 QMetaObject::metacall(d->property.object(),
290 QMetaObject::WriteProperty,
291 idx, a);
292
293 if (wasDeleted)
294 return;
295
296 } else {
297 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context()->engine);
298
299 bool isUndefined = false;
300 QVariant value;
301
302 QScriptValue scriptValue = d->scriptValue(0, &isUndefined);
303 if (wasDeleted)
304 return;
305
306 if (d->property.propertyTypeCategory() == QDeclarativeProperty::List) {
307 value = ep->scriptValueToVariant(scriptValue, qMetaTypeId<QList<QObject *> >());
308 } else if (scriptValue.isNull() &&
309 d->property.propertyTypeCategory() == QDeclarativeProperty::Object) {
310 value = QVariant::fromValue((QObject *)0);
311 } else {
312 value = ep->scriptValueToVariant(scriptValue, d->property.propertyType());
313 if (value.userType() == QMetaType::QObjectStar && !qvariant_cast<QObject*>(value)) {
314 // If the object is null, we extract the predicted type. While this isn't
315 // 100% reliable, in many cases it gives us better error messages if we
316 // assign this null-object to an incompatible property
317 int type = ep->objectClass->objectType(scriptValue);
318 QObject *o = 0;
319 value = QVariant(type, (void *)&o);
320 }
321 }
322
323
324 if (d->error.isValid()) {
325
326 } else if (isUndefined && d->property.isResettable()) {
327
328 d->property.reset();
329
330 } else if (isUndefined && d->property.propertyType() == qMetaTypeId<QVariant>()) {
331
332 QDeclarativePropertyPrivate::write(d->property, QVariant(), flags);
333
334 } else if (isUndefined) {
335
336 QUrl url = QUrl(d->url);
337 int line = d->line;
338 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
339
340 d->error.setUrl(url);
341 d->error.setLine(line);
342 d->error.setColumn(-1);
343 d->error.setDescription(QLatin1String("Unable to assign [undefined] to ") +
344 QLatin1String(QMetaType::typeName(d->property.propertyType())) +
345 QLatin1String(" ") + d->property.name());
346
347 } else if (!scriptValue.isRegExp() && scriptValue.isFunction()) {
348
349 QUrl url = QUrl(d->url);
350 int line = d->line;
351 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
352
353 d->error.setUrl(url);
354 d->error.setLine(line);
355 d->error.setColumn(-1);
356 d->error.setDescription(QLatin1String("Unable to assign a function to a property."));
357
358 } else if (d->property.object() &&
359 !QDeclarativePropertyPrivate::write(d->property, value, flags)) {
360
361 if (wasDeleted)
362 return;
363
364 QUrl url = QUrl(d->url);
365 int line = d->line;
366 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
367
368 const char *valueType = 0;
369 if (value.userType() == QVariant::Invalid) valueType = "null";
370 else valueType = QMetaType::typeName(value.userType());
371
372 d->error.setUrl(url);
373 d->error.setLine(line);
374 d->error.setColumn(-1);
375 d->error.setDescription(QLatin1String("Unable to assign ") +
376 QLatin1String(valueType) +
377 QLatin1String(" to ") +
378 QLatin1String(QMetaType::typeName(d->property.propertyType())));
379 }
380
381 if (wasDeleted)
382 return;
383
384 if (d->error.isValid()) {
385 if (!d->addError(ep)) ep->warning(this->error());
386 } else {
387 d->removeError();
388 }
389 }
390
391 d->updating = false;
392 d->deleted = 0;
393 } else {
394 qmlInfo(d->property.object()) << tr("Binding loop detected for property \"%1\"").arg(d->property.name());
395 }
396}
397
398void QDeclarativeBindingPrivate::emitValueChanged()
399{
400 Q_Q(QDeclarativeBinding);
401 q->update();
402}
403
404void QDeclarativeBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
405{
406 Q_D(QDeclarativeBinding);
407 d->enabled = e;
408 setNotifyOnValueChanged(e);
409
410 if (e)
411 update(flags);
412}
413
414bool QDeclarativeBinding::enabled() const
415{
416 Q_D(const QDeclarativeBinding);
417
418 return d->enabled;
419}
420
421QString QDeclarativeBinding::expression() const
422{
423 return QDeclarativeExpression::expression();
424}
425
426QDeclarativeValueTypeProxyBinding::QDeclarativeValueTypeProxyBinding(QObject *o, int index)
427: m_object(o), m_index(index), m_bindings(0)
428{
429}
430
431QDeclarativeValueTypeProxyBinding::~QDeclarativeValueTypeProxyBinding()
432{
433 while (m_bindings) {
434 QDeclarativeAbstractBinding *binding = m_bindings;
435 binding->setEnabled(false, 0);
436 binding->destroy();
437 }
438}
439
440void QDeclarativeValueTypeProxyBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
441{
442 if (e) {
443 QDeclarativeAbstractBinding *bindings = m_bindings;
444 recursiveEnable(bindings, flags);
445 } else {
446 QDeclarativeAbstractBinding *bindings = m_bindings;
447 recursiveDisable(bindings);
448 }
449}
450
451void QDeclarativeValueTypeProxyBinding::recursiveEnable(QDeclarativeAbstractBinding *b, QDeclarativePropertyPrivate::WriteFlags flags)
452{
453 if (!b)
454 return;
455
456 recursiveEnable(b->m_nextBinding, flags);
457
458 if (b)
459 b->setEnabled(true, flags);
460}
461
462void QDeclarativeValueTypeProxyBinding::recursiveDisable(QDeclarativeAbstractBinding *b)
463{
464 if (!b)
465 return;
466
467 recursiveDisable(b->m_nextBinding);
468
469 if (b)
470 b->setEnabled(false, 0);
471}
472
473void QDeclarativeValueTypeProxyBinding::update(QDeclarativePropertyPrivate::WriteFlags)
474{
475}
476
477QDeclarativeAbstractBinding *QDeclarativeValueTypeProxyBinding::binding(int propertyIndex)
478{
479 QDeclarativeAbstractBinding *binding = m_bindings;
480
481 while (binding && binding->propertyIndex() != propertyIndex)
482 binding = binding->m_nextBinding;
483
484 return binding;
485}
486
487/*!
488Removes a collection of bindings, corresponding to the set bits in \a mask.
489*/
490void QDeclarativeValueTypeProxyBinding::removeBindings(quint32 mask)
491{
492 QDeclarativeAbstractBinding *binding = m_bindings;
493 while (binding) {
494 if (mask & (1 << (binding->propertyIndex() >> 24))) {
495 QDeclarativeAbstractBinding *remove = binding;
496 binding = remove->m_nextBinding;
497 *remove->m_prevBinding = remove->m_nextBinding;
498 if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
499 remove->m_prevBinding = 0;
500 remove->m_nextBinding = 0;
501 remove->destroy();
502 } else {
503 binding = binding->m_nextBinding;
504 }
505 }
506}
507
508QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.