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 "qqmlcontext.h"
43 #include "qqmlcontext_p.h"
44 #include "qqmlcomponentattached_p.h"
46 #include "qqmlcomponent_p.h"
47 #include "qqmlexpression_p.h"
48 #include "qqmlengine_p.h"
49 #include "qqmlengine.h"
51 #include <private/qv4bindings_p.h>
52 #include <private/qv8bindings_p.h>
54 #include <qjsengine.h>
55 #include <QtCore/qvarlengtharray.h>
56 #include <private/qmetaobject_p.h>
57 #include <QtCore/qdebug.h>
61 QQmlContextPrivate::QQmlContextPrivate()
62 : data(0), notifyIndex(-1)
68 \brief The QQmlContext class defines a context within a QML engine.
72 Contexts allow data to be exposed to the QML components instantiated by the
75 Each QQmlContext 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 QQmlContext::setContextProperty(). The following example shows a Qt model
79 being bound to a context and then accessed from a QML file.
83 QStringListModel modelData;
84 QQmlContext *context = new QQmlContext(engine.rootContext());
85 context->setContextProperty("myModel", &modelData);
87 QQmlComponent component(&engine);
88 component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
89 QObject *window = component.create(context);
92 Note it is the responsibility of the creator to delete any QQmlContext 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 QQmlContext. 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 QQmlContext::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)
116 QQmlContext *context = new QQmlContext(engine.rootContext());
117 context->setContextObject(&myDataSet);
119 QQmlComponent component(&engine);
120 component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
121 component.create(context);
124 All properties added explicitly by QQmlContext::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 {QQmlEngine::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
141 QQmlContext *context1 = new QQmlContext(engine.rootContext());
142 QQmlContext *context2 = new QQmlContext(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 {qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Types to QML}
163 QQmlContext::QQmlContext(QQmlEngine *e, bool)
164 : QObject(*(new QQmlContextPrivate))
167 d->data = new QQmlContextData(this);
173 Create a new QQmlContext as a child of \a engine's root context, and the
176 QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent)
177 : QObject(*(new QQmlContextPrivate), parent)
180 d->data = new QQmlContextData(this);
182 d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):0);
186 Create a new QQmlContext with the given \a parentContext, and the
189 QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent)
190 : QObject(*(new QQmlContextPrivate), parent)
193 d->data = new QQmlContextData(this);
195 d->data->setParent(parentContext?QQmlContextData::get(parentContext):0);
201 QQmlContext::QQmlContext(QQmlContextData *data)
202 : QObject(*(new QQmlContextPrivate), 0)
209 Destroys the QQmlContext.
211 Any expressions, or sub-contexts dependent on this context will be
212 invalidated, but not destroyed (unless they are parented to the QQmlContext
215 QQmlContext::~QQmlContext()
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 QQmlContext::isValid() const
231 Q_D(const QQmlContext);
232 return d->data && d->data->isValid();
236 Return the context's QQmlEngine, or 0 if the context has no QQmlEngine or the
237 QQmlEngine was destroyed.
239 QQmlEngine *QQmlContext::engine() const
241 Q_D(const QQmlContext);
242 return d->data->engine;
246 Return the context's parent QQmlContext, or 0 if this context has no
247 parent or if the parent has been destroyed.
249 QQmlContext *QQmlContext::parentContext() const
251 Q_D(const QQmlContext);
252 return d->data->parent?d->data->parent->asQQmlContext():0;
256 Return the context object, or 0 if there is no context object.
258 QObject *QQmlContext::contextObject() const
260 Q_D(const QQmlContext);
261 return d->data->contextObject;
265 Set the context \a object.
267 void QQmlContext::setContextObject(QObject *object)
271 QQmlContextData *data = d->data;
273 if (data->isInternal) {
274 qWarning("QQmlContext: Cannot set context object for internal context.");
279 qWarning("QQmlContext: 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 QQmlContext::setContextProperty(const QString &name, const QVariant &value)
292 if (d->notifyIndex == -1)
293 d->notifyIndex = QMetaObjectPrivate::absoluteSignalCount(&QQmlContext::staticMetaObject);
295 QQmlContextData *data = d->data;
297 if (data->isInternal) {
298 qWarning("QQmlContext: Cannot set property on internal context.");
303 qWarning("QQmlContext: Cannot set property on invalid context.");
309 QObject *o = QQmlEnginePrivate::get(data->engine)->toQObject(value, &ok);
311 setContextProperty(name, o);
316 if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache();
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, d->notifyIndex, idx, 0);
331 Set the \a value of the \a name property on this context.
333 QQmlContext does \b not take ownership of \a value.
335 void QQmlContext::setContextProperty(const QString &name, QObject *value)
338 if (d->notifyIndex == -1)
339 d->notifyIndex = QMetaObjectPrivate::absoluteSignalCount(&QQmlContext::staticMetaObject);
341 QQmlContextData *data = d->data;
343 if (data->isInternal) {
344 qWarning("QQmlContext: Cannot set property on internal context.");
349 qWarning("QQmlContext: Cannot set property on invalid context.");
353 if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache();
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, d->notifyIndex, idx, 0);
368 Returns the value of the \a name property for this context
371 QVariant QQmlContext::contextProperty(const QString &name) const
373 Q_D(const QQmlContext);
377 QQmlContextData *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 QQmlPropertyData local;
387 QQmlPropertyData *property =
388 QQmlPropertyCache::property(data->engine, obj, name, data, 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 Returns the name of \a object in this context, or an empty string if \a object
406 is not named in the context. Objects are named by setContextProperty(), or by ids in
407 the case of QML created contexts.
409 If the object has multiple names, the first is returned.
411 QString QQmlContext::nameForObject(QObject *object) const
413 Q_D(const QQmlContext);
415 return d->data->findObjectId(object);
419 Resolves the URL \a src relative to the URL of the
420 containing component.
422 \sa QQmlEngine::baseUrl(), setBaseUrl()
424 QUrl QQmlContext::resolvedUrl(const QUrl &src)
427 return d->data->resolvedUrl(src);
430 QUrl QQmlContextData::resolvedUrl(const QUrl &src)
432 QQmlContextData *ctxt = this;
434 if (src.isRelative() && !src.isEmpty()) {
437 if(ctxt->url.isValid())
444 return ctxt->url.resolved(src);
446 return engine->baseUrl().resolved(src);
456 Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl.
458 Calling this function will override the url of the containing
459 component used by default.
463 void QQmlContext::setBaseUrl(const QUrl &baseUrl)
467 d->data->url = baseUrl;
468 d->data->urlString = baseUrl.toString();
472 Returns the base url of the component, or the containing component
475 QUrl QQmlContext::baseUrl() const
477 Q_D(const QQmlContext);
478 const QQmlContextData* data = d->data;
479 while (data && data->url.isEmpty())
488 int QQmlContextPrivate::context_count(QQmlListProperty<QObject> *prop)
490 QQmlContext *context = static_cast<QQmlContext*>(prop->object);
491 QQmlContextPrivate *d = QQmlContextPrivate::get(context);
492 int contextProperty = (int)(quintptr)prop->data;
494 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
497 return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count();
501 QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int index)
503 QQmlContext *context = static_cast<QQmlContext*>(prop->object);
504 QQmlContextPrivate *d = QQmlContextPrivate::get(context);
505 int contextProperty = (int)(quintptr)prop->data;
507 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
510 return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index);
515 QQmlContextData::QQmlContextData()
516 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
517 isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false),
518 publicContext(0), activeVMEData(0),
519 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
520 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
521 componentAttached(0), v4bindings(0), v8bindings(0)
525 QQmlContextData::QQmlContextData(QQmlContext *ctxt)
526 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
527 isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false),
528 publicContext(ctxt), activeVMEData(0),
529 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
530 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
531 componentAttached(0), v4bindings(0), v8bindings(0)
535 void QQmlContextData::emitDestruction()
537 if (!hasEmittedDestruction) {
538 hasEmittedDestruction = true;
540 // Emit the destruction signal - must be emitted before invalidate so that the
541 // context is still valid if bindings or resultant expression evaluation requires it
543 while (componentAttached) {
544 QQmlComponentAttached *a = componentAttached;
545 componentAttached = a->next;
546 if (componentAttached) componentAttached->prev = &componentAttached;
551 emit a->destruction();
554 QQmlContextData * child = childContexts;
556 child->emitDestruction();
557 child = child->nextChild;
563 void QQmlContextData::invalidate()
567 while (childContexts) {
568 if (childContexts->ownedByParent) {
569 childContexts->destroy();
571 childContexts->invalidate();
576 *prevChild = nextChild;
577 if (nextChild) nextChild->prevChild = prevChild;
586 void QQmlContextData::clearContext()
590 QQmlAbstractExpression *expression = expressions;
592 QQmlAbstractExpression *nextExpression = expression->m_nextExpression;
594 expression->m_prevExpression = 0;
595 expression->m_nextExpression = 0;
597 expression->setContext(0);
599 expression = nextExpression;
604 void QQmlContextData::destroy()
607 linkedContext->destroy();
609 if (engine) invalidate();
613 while (contextObjects) {
614 QQmlData *co = contextObjects;
615 contextObjects = contextObjects->nextContextObject;
618 co->outerContext = 0;
619 co->nextContextObject = 0;
620 co->prevContextObject = 0;
623 QQmlGuardedContextData *contextGuard = contextGuards;
624 while (contextGuard) {
625 QQmlGuardedContextData *next = contextGuard->m_next;
626 contextGuard->m_next = 0;
627 contextGuard->m_prev = 0;
628 contextGuard->m_contextData = 0;
634 propertyNames->release();
640 v4bindings->release();
643 v8bindings->release();
645 for (int ii = 0; ii < importedScripts.count(); ++ii) {
646 qPersistentDispose(importedScripts[ii]);
652 delete publicContext;
657 void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership)
662 nextChild = p->childContexts;
663 if (nextChild) nextChild->prevChild = &nextChild;
664 prevChild = &p->childContexts;
665 p->childContexts = this;
666 ownedByParent = parentTakesOwnership;
670 void QQmlContextData::refreshExpressionsRecursive(QQmlAbstractExpression *expression)
672 QQmlAbstractExpression::DeleteWatcher w(expression);
674 if (expression->m_nextExpression)
675 refreshExpressionsRecursive(expression->m_nextExpression);
678 expression->refresh();
681 static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh)
683 return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames);
686 void QQmlContextData::refreshExpressionsRecursive(bool isGlobal)
688 // For efficiency, we try and minimize the number of guards we have to create
689 if (expressions_to_run(this, isGlobal) && (nextChild || childContexts)) {
690 QQmlGuardedContextData guard(this);
693 childContexts->refreshExpressionsRecursive(isGlobal);
695 if (guard.isNull()) return;
698 nextChild->refreshExpressionsRecursive(isGlobal);
700 if (guard.isNull()) return;
702 if (expressions_to_run(this, isGlobal))
703 refreshExpressionsRecursive(expressions);
705 } else if (expressions_to_run(this, isGlobal)) {
707 refreshExpressionsRecursive(expressions);
709 } else if (nextChild && childContexts) {
711 QQmlGuardedContextData guard(this);
713 childContexts->refreshExpressionsRecursive(isGlobal);
715 if (!guard.isNull() && nextChild)
716 nextChild->refreshExpressionsRecursive(isGlobal);
718 } else if (nextChild) {
720 nextChild->refreshExpressionsRecursive(isGlobal);
722 } else if (childContexts) {
724 childContexts->refreshExpressionsRecursive(isGlobal);
729 // Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
730 // context-tree dependent caches in the expressions, and should occur every time the context tree
731 // *structure* (not values) changes.
732 void QQmlContextData::refreshExpressions()
734 bool isGlobal = (parent == 0);
736 // For efficiency, we try and minimize the number of guards we have to create
737 if (expressions_to_run(this, isGlobal) && childContexts) {
738 QQmlGuardedContextData guard(this);
740 childContexts->refreshExpressionsRecursive(isGlobal);
742 if (!guard.isNull() && expressions_to_run(this, isGlobal))
743 refreshExpressionsRecursive(expressions);
745 } else if (expressions_to_run(this, isGlobal)) {
747 refreshExpressionsRecursive(expressions);
749 } else if (childContexts) {
751 childContexts->refreshExpressionsRecursive(isGlobal);
756 void QQmlContextData::addObject(QObject *o)
758 QQmlData *data = QQmlData::get(o, true);
760 Q_ASSERT(data->context == 0);
762 data->context = this;
763 data->outerContext = this;
765 data->nextContextObject = contextObjects;
766 if (data->nextContextObject)
767 data->nextContextObject->prevContextObject = &data->nextContextObject;
768 data->prevContextObject = &contextObjects;
769 contextObjects = data;
772 void QQmlContextData::setIdProperty(int idx, QObject *obj)
775 idValues[idx].context = this;
778 void QQmlContextData::setIdPropertyData(QQmlIntegerCache *data)
780 Q_ASSERT(!propertyNames);
781 propertyNames = data;
782 propertyNames->addref();
784 idValueCount = data->count();
785 idValues = new ContextGuard[idValueCount];
788 QString QQmlContextData::findObjectId(const QObject *obj) const
793 for (int ii = 0; ii < idValueCount; ii++) {
794 if (idValues[ii] == obj)
795 return propertyNames->findId(ii);
799 QQmlContextPrivate *p = QQmlContextPrivate::get(publicContext);
800 for (int ii = 0; ii < p->propertyValues.count(); ++ii)
801 if (p->propertyValues.at(ii) == QVariant::fromValue((QObject *)obj))
802 return propertyNames->findId(ii);
806 return linkedContext->findObjectId(obj);
810 QQmlContext *QQmlContextData::asQQmlContext()
813 publicContext = new QQmlContext(this);
814 return publicContext;
817 QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate()
819 return QQmlContextPrivate::get(asQQmlContext());