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 "qdeclarativecontext.h"
43 #include "private/qdeclarativecontext_p.h"
45 #include "private/qdeclarativecomponent_p.h"
46 #include "private/qdeclarativeexpression_p.h"
47 #include "private/qdeclarativeengine_p.h"
48 #include "qdeclarativeengine.h"
49 #include "private/qdeclarativecompiledbindings_p.h"
50 #include "qdeclarativeinfo.h"
51 #include "private/qdeclarativeglobalscriptclass_p.h"
53 #include <qscriptengine.h>
54 #include <QtCore/qvarlengtharray.h>
55 #include <QtCore/qdebug.h>
57 #include <private/qscriptdeclarativeclass_p.h>
61 QDeclarativeContextPrivate::QDeclarativeContextPrivate()
62 : data(0), notifyIndex(-1)
67 \class QDeclarativeContext
69 \brief The QDeclarativeContext class defines a context within a QML engine.
72 Contexts allow data to be exposed to the QML components instantiated by the
75 Each QDeclarativeContext contains a set of properties, distinct from its QObject
76 properties, that allow data to be explicitly bound to a context by name. The
77 context properties are defined and updated by calling
78 QDeclarativeContext::setContextProperty(). The following example shows a Qt model
79 being bound to a context and then accessed from a QML file.
82 QDeclarativeEngine engine;
83 QStringListModel modelData;
84 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
85 context->setContextProperty("myModel", &modelData);
87 QDeclarativeComponent component(&engine);
88 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
89 QObject *window = component.create(context);
92 Note it is the responsibility of the creator to delete any QDeclarativeContext it
93 constructs. If the \c context object in the example is no longer needed when the
94 \c window component instance is destroyed, the \c context must be destroyed explicitly.
95 The simplest way to ensure this is to set \c window as the parent of \c context.
97 To simplify binding and maintaining larger data sets, a context object can be set
98 on a QDeclarativeContext. All the properties of the context object are available
99 by name in the context, as though they were all individually added through calls
100 to QDeclarativeContext::setContextProperty(). Changes to the property's values are
101 detected through the property's notify signal. Setting a context object is both
102 faster and easier than manually adding and maintaing context property values.
104 The following example has the same effect as the previous one, but it uses a context
108 class MyDataSet : ... {
110 Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
115 QDeclarativeEngine engine;
116 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
117 context->setContextObject(&myDataSet);
119 QDeclarativeComponent component(&engine);
120 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
121 component.create(context);
124 All properties added explicitly by QDeclarativeContext::setContextProperty() take
125 precedence over the context object's properties.
127 \section2 The Context Hierarchy
129 Contexts form a hierarchy. The root of this hierarchy is the QML engine's
130 \l {QDeclarativeEngine::rootContext()}{root context}. Child contexts inherit
131 the context properties of their parents; if a child context sets a context property
132 that already exists in its parent, the new context property overrides that of the
135 The following example defines two contexts - \c context1 and \c context2. The
136 second context overrides the "b" context property inherited from the first with a
140 QDeclarativeEngine engine;
141 QDeclarativeContext *context1 = new QDeclarativeContext(engine.rootContext());
142 QDeclarativeContext *context2 = new QDeclarativeContext(context1);
144 context1->setContextProperty("a", 12);
145 context1->setContextProperty("b", 12);
147 context2->setContextProperty("b", 15);
150 While QML objects instantiated in a context are not strictly owned by that
151 context, their bindings are. If a context is destroyed, the property bindings of
152 outstanding QML objects will stop evaluating.
154 \warning Setting the context object or adding new context properties after an object
155 has been created in that context is an expensive operation (essentially forcing all bindings
156 to reevaluate). Thus whenever possible you should complete "setup" of the context
157 before using it to create any objects.
159 \sa {Using QML Bindings in C++ Applications}
163 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *e, bool)
164 : QObject(*(new QDeclarativeContextPrivate))
166 Q_D(QDeclarativeContext);
167 d->data = new QDeclarativeContextData(this);
173 Create a new QDeclarativeContext as a child of \a engine's root context, and the
176 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *engine, QObject *parent)
177 : QObject(*(new QDeclarativeContextPrivate), parent)
179 Q_D(QDeclarativeContext);
180 d->data = new QDeclarativeContextData(this);
182 d->data->setParent(engine?QDeclarativeContextData::get(engine->rootContext()):0);
186 Create a new QDeclarativeContext with the given \a parentContext, and the
189 QDeclarativeContext::QDeclarativeContext(QDeclarativeContext *parentContext, QObject *parent)
190 : QObject(*(new QDeclarativeContextPrivate), parent)
192 Q_D(QDeclarativeContext);
193 d->data = new QDeclarativeContextData(this);
195 d->data->setParent(parentContext?QDeclarativeContextData::get(parentContext):0);
201 QDeclarativeContext::QDeclarativeContext(QDeclarativeContextData *data)
202 : QObject(*(new QDeclarativeContextPrivate), 0)
204 Q_D(QDeclarativeContext);
209 Destroys the QDeclarativeContext.
211 Any expressions, or sub-contexts dependent on this context will be
212 invalidated, but not destroyed (unless they are parented to the QDeclarativeContext
215 QDeclarativeContext::~QDeclarativeContext()
217 Q_D(QDeclarativeContext);
219 if (!d->data->isInternal)
224 Returns whether the context is valid.
226 To be valid, a context must have a engine, and it's contextObject(), if any,
227 must not have been deleted.
229 bool QDeclarativeContext::isValid() const
231 Q_D(const QDeclarativeContext);
232 return d->data && d->data->isValid();
236 Return the context's QDeclarativeEngine, or 0 if the context has no QDeclarativeEngine or the
237 QDeclarativeEngine was destroyed.
239 QDeclarativeEngine *QDeclarativeContext::engine() const
241 Q_D(const QDeclarativeContext);
242 return d->data->engine;
246 Return the context's parent QDeclarativeContext, or 0 if this context has no
247 parent or if the parent has been destroyed.
249 QDeclarativeContext *QDeclarativeContext::parentContext() const
251 Q_D(const QDeclarativeContext);
252 return d->data->parent?d->data->parent->asQDeclarativeContext():0;
256 Return the context object, or 0 if there is no context object.
258 QObject *QDeclarativeContext::contextObject() const
260 Q_D(const QDeclarativeContext);
261 return d->data->contextObject;
265 Set the context \a object.
267 void QDeclarativeContext::setContextObject(QObject *object)
269 Q_D(QDeclarativeContext);
271 QDeclarativeContextData *data = d->data;
273 if (data->isInternal) {
274 qWarning("QDeclarativeContext: Cannot set context object for internal context.");
279 qWarning("QDeclarativeContext: Cannot set context object on invalid context.");
283 data->contextObject = object;
287 Set a the \a value of the \a name property on this context.
289 void QDeclarativeContext::setContextProperty(const QString &name, const QVariant &value)
291 Q_D(QDeclarativeContext);
292 if (d->notifyIndex == -1)
293 d->notifyIndex = this->metaObject()->methodCount();
295 QDeclarativeContextData *data = d->data;
297 if (data->isInternal) {
298 qWarning("QDeclarativeContext: Cannot set property on internal context.");
303 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
309 QObject *o = QDeclarativeEnginePrivate::get(data->engine)->toQObject(value, &ok);
311 setContextProperty(name, o);
316 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache(data->engine);
318 int idx = data->propertyNames->value(name);
320 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
321 d->propertyValues.append(value);
323 data->refreshExpressions();
325 d->propertyValues[idx] = value;
326 QMetaObject::activate(this, idx + d->notifyIndex, 0);
331 Set the \a value of the \a name property on this context.
333 QDeclarativeContext does \bold not take ownership of \a value.
335 void QDeclarativeContext::setContextProperty(const QString &name, QObject *value)
337 Q_D(QDeclarativeContext);
338 if (d->notifyIndex == -1)
339 d->notifyIndex = this->metaObject()->methodCount();
341 QDeclarativeContextData *data = d->data;
343 if (data->isInternal) {
344 qWarning("QDeclarativeContext: Cannot set property on internal context.");
349 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
353 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache(data->engine);
354 int idx = data->propertyNames->value(name);
357 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
358 d->propertyValues.append(QVariant::fromValue(value));
360 data->refreshExpressions();
362 d->propertyValues[idx] = QVariant::fromValue(value);
363 QMetaObject::activate(this, idx + d->notifyIndex, 0);
368 Returns the value of the \a name property for this context
371 QVariant QDeclarativeContext::contextProperty(const QString &name) const
373 Q_D(const QDeclarativeContext);
377 QDeclarativeContextData *data = d->data;
379 if (data->propertyNames)
380 idx = data->propertyNames->value(name);
383 QByteArray utf8Name = name.toUtf8();
384 if (data->contextObject) {
385 QObject *obj = data->contextObject;
386 QDeclarativePropertyCache::Data local;
387 QDeclarativePropertyCache::Data *property =
388 QDeclarativePropertyCache::property(data->engine, obj, name, local);
390 if (property) value = obj->metaObject()->property(property->coreIndex).read(obj);
392 if (!value.isValid() && parentContext())
393 value = parentContext()->contextProperty(name);
395 if (idx >= d->propertyValues.count())
396 value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data());
398 value = d->propertyValues[idx];
405 Resolves the URL \a src relative to the URL of the
406 containing component.
408 \sa QDeclarativeEngine::baseUrl(), setBaseUrl()
410 QUrl QDeclarativeContext::resolvedUrl(const QUrl &src)
412 Q_D(QDeclarativeContext);
413 return d->data->resolvedUrl(src);
416 QUrl QDeclarativeContextData::resolvedUrl(const QUrl &src)
418 QDeclarativeContextData *ctxt = this;
420 if (src.isRelative() && !src.isEmpty()) {
423 if(ctxt->url.isValid())
430 return ctxt->url.resolved(src);
432 return engine->baseUrl().resolved(src);
442 Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl.
444 Calling this function will override the url of the containing
445 component used by default.
449 void QDeclarativeContext::setBaseUrl(const QUrl &baseUrl)
451 Q_D(QDeclarativeContext);
453 d->data->url = baseUrl;
457 Returns the base url of the component, or the containing component
460 QUrl QDeclarativeContext::baseUrl() const
462 Q_D(const QDeclarativeContext);
463 const QDeclarativeContextData* data = d->data;
464 while (data && data->url.isEmpty())
473 int QDeclarativeContextPrivate::context_count(QDeclarativeListProperty<QObject> *prop)
475 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
476 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
477 int contextProperty = (int)(quintptr)prop->data;
479 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
482 return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count();
486 QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty<QObject> *prop, int index)
488 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
489 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
490 int contextProperty = (int)(quintptr)prop->data;
492 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
495 return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index);
500 QDeclarativeContextData::QDeclarativeContextData()
501 : parent(0), engine(0), isInternal(false), publicContext(0), propertyNames(0), contextObject(0),
502 imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0),
503 contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0), linkedContext(0),
508 QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt)
509 : parent(0), engine(0), isInternal(false), publicContext(ctxt), propertyNames(0), contextObject(0),
510 imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0),
511 contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0), linkedContext(0),
516 void QDeclarativeContextData::invalidate()
518 while (childContexts)
519 childContexts->invalidate();
521 while (componentAttached) {
522 QDeclarativeComponentAttached *a = componentAttached;
523 componentAttached = a->next;
524 if (componentAttached) componentAttached->prev = &componentAttached;
529 emit a->destruction();
533 *prevChild = nextChild;
534 if (nextChild) nextChild->prevChild = prevChild;
543 void QDeclarativeContextData::clearContext()
546 while (componentAttached) {
547 QDeclarativeComponentAttached *a = componentAttached;
548 componentAttached = a->next;
549 if (componentAttached) componentAttached->prev = &componentAttached;
554 emit a->destruction();
558 QDeclarativeAbstractExpression *expression = expressions;
560 QDeclarativeAbstractExpression *nextExpression = expression->m_nextExpression;
562 expression->m_context = 0;
563 expression->m_prevExpression = 0;
564 expression->m_nextExpression = 0;
566 expression = nextExpression;
571 void QDeclarativeContextData::destroy()
574 linkedContext->destroy();
576 if (engine) invalidate();
580 while (contextObjects) {
581 QDeclarativeData *co = contextObjects;
582 contextObjects = contextObjects->nextContextObject;
585 co->outerContext = 0;
586 co->nextContextObject = 0;
587 co->prevContextObject = 0;
590 QDeclarativeGuardedContextData *contextGuard = contextGuards;
591 while (contextGuard) {
592 QDeclarativeGuardedContextData *next = contextGuard->m_next;
593 contextGuard->m_next = 0;
594 contextGuard->m_prev = 0;
595 contextGuard->m_contextData = 0;
601 propertyNames->release();
606 if (optimizedBindings)
607 optimizedBindings->release();
612 delete publicContext;
617 void QDeclarativeContextData::setParent(QDeclarativeContextData *p)
622 nextChild = p->childContexts;
623 if (nextChild) nextChild->prevChild = &nextChild;
624 prevChild = &p->childContexts;
625 p->childContexts = this;
630 Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
631 context-tree dependent caches in the expressions, and should occur every time the context tree
632 *structure* (not values) changes.
634 void QDeclarativeContextData::refreshExpressions()
636 QDeclarativeContextData *child = childContexts;
638 child->refreshExpressions();
639 child = child->nextChild;
642 QDeclarativeAbstractExpression *expression = expressions;
644 expression->refresh();
645 expression = expression->m_nextExpression;
649 void QDeclarativeContextData::addObject(QObject *o)
651 QDeclarativeData *data = QDeclarativeData::get(o, true);
653 Q_ASSERT(data->context == 0);
655 data->context = this;
656 data->outerContext = this;
658 data->nextContextObject = contextObjects;
659 if (data->nextContextObject)
660 data->nextContextObject->prevContextObject = &data->nextContextObject;
661 data->prevContextObject = &contextObjects;
662 contextObjects = data;
665 void QDeclarativeContextData::addImportedScript(const QDeclarativeParser::Object::ScriptBlock &script)
670 QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
671 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
673 const QString &code = script.code;
674 const QString &url = script.file;
675 const QDeclarativeParser::Object::ScriptBlock::Pragmas &pragmas = script.pragmas;
677 Q_ASSERT(!url.isEmpty());
679 if (pragmas & QDeclarativeParser::Object::ScriptBlock::Shared) {
681 QHash<QString, QScriptValue>::Iterator iter = enginePriv->m_sharedScriptImports.find(url);
682 if (iter == enginePriv->m_sharedScriptImports.end()) {
683 QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
685 scriptContext->pushScope(enginePriv->contextClass->newUrlContext(url));
686 scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject());
688 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine);
689 scriptContext->pushScope(scope);
691 scriptEngine->evaluate(code, url, 1);
693 if (scriptEngine->hasUncaughtException()) {
694 QDeclarativeError error;
695 QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error);
696 enginePriv->warning(error);
699 scriptEngine->popContext();
701 iter = enginePriv->m_sharedScriptImports.insert(url, scope);
704 importedScripts.append(*iter);
708 QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine);
710 scriptContext->pushScope(enginePriv->contextClass->newUrlContext(this, 0, url));
711 scriptContext->pushScope(enginePriv->globalClass->staticGlobalObject());
713 QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(scriptEngine);
714 scriptContext->pushScope(scope);
716 scriptEngine->evaluate(code, url, 1);
718 if (scriptEngine->hasUncaughtException()) {
719 QDeclarativeError error;
720 QDeclarativeExpressionPrivate::exceptionToError(scriptEngine, error);
721 enginePriv->warning(error);
724 scriptEngine->popContext();
726 importedScripts.append(scope);
731 void QDeclarativeContextData::setIdProperty(int idx, QObject *obj)
734 idValues[idx].context = this;
737 void QDeclarativeContextData::setIdPropertyData(QDeclarativeIntegerCache *data)
739 Q_ASSERT(!propertyNames);
740 propertyNames = data;
741 propertyNames->addref();
743 idValueCount = data->count();
744 idValues = new ContextGuard[idValueCount];
747 QString QDeclarativeContextData::findObjectId(const QObject *obj) const
749 if (!idValues || !propertyNames)
752 for (int i=0; i<idValueCount; i++) {
753 if (idValues[i] == obj)
754 return propertyNames->findId(i);
758 return linkedContext->findObjectId(obj);
762 QDeclarativeContext *QDeclarativeContextData::asQDeclarativeContext()
765 publicContext = new QDeclarativeContext(this);
766 return publicContext;
769 QDeclarativeContextPrivate *QDeclarativeContextData::asQDeclarativeContextPrivate()
771 return QDeclarativeContextPrivate::get(asQDeclarativeContext());