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 <QtCore/qdebug.h>
60 QQmlContextPrivate::QQmlContextPrivate()
61 : data(0), notifyIndex(-1)
68 \brief The QQmlContext class defines a context within a QML engine.
71 Contexts allow data to be exposed to the QML components instantiated by the
74 Each QQmlContext contains a set of properties, distinct from its QObject
75 properties, that allow data to be explicitly bound to a context by name. The
76 context properties are defined and updated by calling
77 QQmlContext::setContextProperty(). The following example shows a Qt model
78 being bound to a context and then accessed from a QML file.
82 QStringListModel modelData;
83 QQmlContext *context = new QQmlContext(engine.rootContext());
84 context->setContextProperty("myModel", &modelData);
86 QQmlComponent component(&engine);
87 component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
88 QObject *window = component.create(context);
91 Note it is the responsibility of the creator to delete any QQmlContext it
92 constructs. If the \c context object in the example is no longer needed when the
93 \c window component instance is destroyed, the \c context must be destroyed explicitly.
94 The simplest way to ensure this is to set \c window as the parent of \c context.
96 To simplify binding and maintaining larger data sets, a context object can be set
97 on a QQmlContext. All the properties of the context object are available
98 by name in the context, as though they were all individually added through calls
99 to QQmlContext::setContextProperty(). Changes to the property's values are
100 detected through the property's notify signal. Setting a context object is both
101 faster and easier than manually adding and maintaing context property values.
103 The following example has the same effect as the previous one, but it uses a context
107 class MyDataSet : ... {
109 Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
115 QQmlContext *context = new QQmlContext(engine.rootContext());
116 context->setContextObject(&myDataSet);
118 QQmlComponent component(&engine);
119 component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
120 component.create(context);
123 All properties added explicitly by QQmlContext::setContextProperty() take
124 precedence over the context object's properties.
126 \section2 The Context Hierarchy
128 Contexts form a hierarchy. The root of this hierarchy is the QML engine's
129 \l {QQmlEngine::rootContext()}{root context}. Child contexts inherit
130 the context properties of their parents; if a child context sets a context property
131 that already exists in its parent, the new context property overrides that of the
134 The following example defines two contexts - \c context1 and \c context2. The
135 second context overrides the "b" context property inherited from the first with a
140 QQmlContext *context1 = new QQmlContext(engine.rootContext());
141 QQmlContext *context2 = new QQmlContext(context1);
143 context1->setContextProperty("a", 12);
144 context1->setContextProperty("b", 12);
146 context2->setContextProperty("b", 15);
149 While QML objects instantiated in a context are not strictly owned by that
150 context, their bindings are. If a context is destroyed, the property bindings of
151 outstanding QML objects will stop evaluating.
153 \warning Setting the context object or adding new context properties after an object
154 has been created in that context is an expensive operation (essentially forcing all bindings
155 to reevaluate). Thus whenever possible you should complete "setup" of the context
156 before using it to create any objects.
158 \sa {Using QML Bindings in C++ Applications}
162 QQmlContext::QQmlContext(QQmlEngine *e, bool)
163 : QObject(*(new QQmlContextPrivate))
166 d->data = new QQmlContextData(this);
172 Create a new QQmlContext as a child of \a engine's root context, and the
175 QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent)
176 : QObject(*(new QQmlContextPrivate), parent)
179 d->data = new QQmlContextData(this);
181 d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):0);
185 Create a new QQmlContext with the given \a parentContext, and the
188 QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent)
189 : QObject(*(new QQmlContextPrivate), parent)
192 d->data = new QQmlContextData(this);
194 d->data->setParent(parentContext?QQmlContextData::get(parentContext):0);
200 QQmlContext::QQmlContext(QQmlContextData *data)
201 : QObject(*(new QQmlContextPrivate), 0)
208 Destroys the QQmlContext.
210 Any expressions, or sub-contexts dependent on this context will be
211 invalidated, but not destroyed (unless they are parented to the QQmlContext
214 QQmlContext::~QQmlContext()
218 if (!d->data->isInternal)
223 Returns whether the context is valid.
225 To be valid, a context must have a engine, and it's contextObject(), if any,
226 must not have been deleted.
228 bool QQmlContext::isValid() const
230 Q_D(const QQmlContext);
231 return d->data && d->data->isValid();
235 Return the context's QQmlEngine, or 0 if the context has no QQmlEngine or the
236 QQmlEngine was destroyed.
238 QQmlEngine *QQmlContext::engine() const
240 Q_D(const QQmlContext);
241 return d->data->engine;
245 Return the context's parent QQmlContext, or 0 if this context has no
246 parent or if the parent has been destroyed.
248 QQmlContext *QQmlContext::parentContext() const
250 Q_D(const QQmlContext);
251 return d->data->parent?d->data->parent->asQQmlContext():0;
255 Return the context object, or 0 if there is no context object.
257 QObject *QQmlContext::contextObject() const
259 Q_D(const QQmlContext);
260 return d->data->contextObject;
264 Set the context \a object.
266 void QQmlContext::setContextObject(QObject *object)
270 QQmlContextData *data = d->data;
272 if (data->isInternal) {
273 qWarning("QQmlContext: Cannot set context object for internal context.");
278 qWarning("QQmlContext: Cannot set context object on invalid context.");
282 data->contextObject = object;
286 Set a the \a value of the \a name property on this context.
288 void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
291 if (d->notifyIndex == -1)
292 d->notifyIndex = this->metaObject()->methodCount();
294 QQmlContextData *data = d->data;
296 if (data->isInternal) {
297 qWarning("QQmlContext: Cannot set property on internal context.");
302 qWarning("QQmlContext: Cannot set property on invalid context.");
308 QObject *o = QQmlEnginePrivate::get(data->engine)->toQObject(value, &ok);
310 setContextProperty(name, o);
315 if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache();
317 int idx = data->propertyNames->value(name);
319 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
320 d->propertyValues.append(value);
322 data->refreshExpressions();
324 d->propertyValues[idx] = value;
325 QMetaObject::activate(this, idx + d->notifyIndex, 0);
330 Set the \a value of the \a name property on this context.
332 QQmlContext does \b not take ownership of \a value.
334 void QQmlContext::setContextProperty(const QString &name, QObject *value)
337 if (d->notifyIndex == -1)
338 d->notifyIndex = this->metaObject()->methodCount();
340 QQmlContextData *data = d->data;
342 if (data->isInternal) {
343 qWarning("QQmlContext: Cannot set property on internal context.");
348 qWarning("QQmlContext: Cannot set property on invalid context.");
352 if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache();
353 int idx = data->propertyNames->value(name);
356 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
357 d->propertyValues.append(QVariant::fromValue(value));
359 data->refreshExpressions();
361 d->propertyValues[idx] = QVariant::fromValue(value);
362 QMetaObject::activate(this, idx + d->notifyIndex, 0);
367 Returns the value of the \a name property for this context
370 QVariant QQmlContext::contextProperty(const QString &name) const
372 Q_D(const QQmlContext);
376 QQmlContextData *data = d->data;
378 if (data->propertyNames)
379 idx = data->propertyNames->value(name);
382 QByteArray utf8Name = name.toUtf8();
383 if (data->contextObject) {
384 QObject *obj = data->contextObject;
385 QQmlPropertyData local;
386 QQmlPropertyData *property =
387 QQmlPropertyCache::property(data->engine, obj, name, local);
389 if (property) value = obj->metaObject()->property(property->coreIndex).read(obj);
391 if (!value.isValid() && parentContext())
392 value = parentContext()->contextProperty(name);
394 if (idx >= d->propertyValues.count())
395 value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data());
397 value = d->propertyValues[idx];
404 Returns the name of \a object in this context, or an empty string if \a object
405 is not named in the context. Objects are named by setContextProperty(), or by ids in
406 the case of QML created contexts.
408 If the object has multiple names, the first is returned.
410 QString QQmlContext::nameForObject(QObject *object) const
412 Q_D(const QQmlContext);
414 return d->data->findObjectId(object);
418 Resolves the URL \a src relative to the URL of the
419 containing component.
421 \sa QQmlEngine::baseUrl(), setBaseUrl()
423 QUrl QQmlContext::resolvedUrl(const QUrl &src)
426 return d->data->resolvedUrl(src);
429 QUrl QQmlContextData::resolvedUrl(const QUrl &src)
431 QQmlContextData *ctxt = this;
433 if (src.isRelative() && !src.isEmpty()) {
436 if(ctxt->url.isValid())
443 return ctxt->url.resolved(src);
445 return engine->baseUrl().resolved(src);
455 Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl.
457 Calling this function will override the url of the containing
458 component used by default.
462 void QQmlContext::setBaseUrl(const QUrl &baseUrl)
466 d->data->url = baseUrl;
467 d->data->urlString = baseUrl.toString();
471 Returns the base url of the component, or the containing component
474 QUrl QQmlContext::baseUrl() const
476 Q_D(const QQmlContext);
477 const QQmlContextData* data = d->data;
478 while (data && data->url.isEmpty())
487 int QQmlContextPrivate::context_count(QQmlListProperty<QObject> *prop)
489 QQmlContext *context = static_cast<QQmlContext*>(prop->object);
490 QQmlContextPrivate *d = QQmlContextPrivate::get(context);
491 int contextProperty = (int)(quintptr)prop->data;
493 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
496 return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count();
500 QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int index)
502 QQmlContext *context = static_cast<QQmlContext*>(prop->object);
503 QQmlContextPrivate *d = QQmlContextPrivate::get(context);
504 int contextProperty = (int)(quintptr)prop->data;
506 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
509 return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index);
514 QQmlContextData::QQmlContextData()
515 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
516 isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false),
517 publicContext(0), activeVMEData(0),
518 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
519 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
520 componentAttached(0), v4bindings(0), v8bindings(0)
524 QQmlContextData::QQmlContextData(QQmlContext *ctxt)
525 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
526 isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false),
527 publicContext(ctxt), activeVMEData(0),
528 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
529 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
530 componentAttached(0), v4bindings(0), v8bindings(0)
534 void QQmlContextData::emitDestruction()
536 if (!hasEmittedDestruction) {
537 hasEmittedDestruction = true;
539 // Emit the destruction signal - must be emitted before invalidate so that the
540 // context is still valid if bindings or resultant expression evaluation requires it
542 while (componentAttached) {
543 QQmlComponentAttached *a = componentAttached;
544 componentAttached = a->next;
545 if (componentAttached) componentAttached->prev = &componentAttached;
550 emit a->destruction();
553 QQmlContextData * child = childContexts;
555 child->emitDestruction();
556 child = child->nextChild;
562 void QQmlContextData::invalidate()
566 while (childContexts) {
567 if (childContexts->ownedByParent) {
568 childContexts->destroy();
570 childContexts->invalidate();
575 *prevChild = nextChild;
576 if (nextChild) nextChild->prevChild = prevChild;
585 void QQmlContextData::clearContext()
589 QQmlAbstractExpression *expression = expressions;
591 QQmlAbstractExpression *nextExpression = expression->m_nextExpression;
593 expression->m_prevExpression = 0;
594 expression->m_nextExpression = 0;
596 expression->setContext(0);
598 expression = nextExpression;
603 void QQmlContextData::destroy()
606 linkedContext->destroy();
608 if (engine) invalidate();
612 while (contextObjects) {
613 QQmlData *co = contextObjects;
614 contextObjects = contextObjects->nextContextObject;
617 co->outerContext = 0;
618 co->nextContextObject = 0;
619 co->prevContextObject = 0;
622 QQmlGuardedContextData *contextGuard = contextGuards;
623 while (contextGuard) {
624 QQmlGuardedContextData *next = contextGuard->m_next;
625 contextGuard->m_next = 0;
626 contextGuard->m_prev = 0;
627 contextGuard->m_contextData = 0;
633 propertyNames->release();
639 v4bindings->release();
642 v8bindings->release();
644 for (int ii = 0; ii < importedScripts.count(); ++ii) {
645 qPersistentDispose(importedScripts[ii]);
651 delete publicContext;
656 void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership)
661 nextChild = p->childContexts;
662 if (nextChild) nextChild->prevChild = &nextChild;
663 prevChild = &p->childContexts;
664 p->childContexts = this;
665 ownedByParent = parentTakesOwnership;
669 void QQmlContextData::refreshExpressionsRecursive(QQmlAbstractExpression *expression)
671 QQmlAbstractExpression::DeleteWatcher w(expression);
673 if (expression->m_nextExpression)
674 refreshExpressionsRecursive(expression->m_nextExpression);
677 expression->refresh();
680 static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh)
682 return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames);
685 void QQmlContextData::refreshExpressionsRecursive(bool isGlobal)
687 // For efficiency, we try and minimize the number of guards we have to create
688 if (expressions_to_run(this, isGlobal) && (nextChild || childContexts)) {
689 QQmlGuardedContextData guard(this);
692 childContexts->refreshExpressionsRecursive(isGlobal);
694 if (guard.isNull()) return;
697 nextChild->refreshExpressionsRecursive(isGlobal);
699 if (guard.isNull()) return;
701 if (expressions_to_run(this, isGlobal))
702 refreshExpressionsRecursive(expressions);
704 } else if (expressions_to_run(this, isGlobal)) {
706 refreshExpressionsRecursive(expressions);
708 } else if (nextChild && childContexts) {
710 QQmlGuardedContextData guard(this);
712 childContexts->refreshExpressionsRecursive(isGlobal);
714 if (!guard.isNull() && nextChild)
715 nextChild->refreshExpressionsRecursive(isGlobal);
717 } else if (nextChild) {
719 nextChild->refreshExpressionsRecursive(isGlobal);
721 } else if (childContexts) {
723 childContexts->refreshExpressionsRecursive(isGlobal);
728 // Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
729 // context-tree dependent caches in the expressions, and should occur every time the context tree
730 // *structure* (not values) changes.
731 void QQmlContextData::refreshExpressions()
733 bool isGlobal = (parent == 0);
735 // For efficiency, we try and minimize the number of guards we have to create
736 if (expressions_to_run(this, isGlobal) && childContexts) {
737 QQmlGuardedContextData guard(this);
739 childContexts->refreshExpressionsRecursive(isGlobal);
741 if (!guard.isNull() && expressions_to_run(this, isGlobal))
742 refreshExpressionsRecursive(expressions);
744 } else if (expressions_to_run(this, isGlobal)) {
746 refreshExpressionsRecursive(expressions);
748 } else if (childContexts) {
750 childContexts->refreshExpressionsRecursive(isGlobal);
755 void QQmlContextData::addObject(QObject *o)
757 QQmlData *data = QQmlData::get(o, true);
759 Q_ASSERT(data->context == 0);
761 data->context = this;
762 data->outerContext = this;
764 data->nextContextObject = contextObjects;
765 if (data->nextContextObject)
766 data->nextContextObject->prevContextObject = &data->nextContextObject;
767 data->prevContextObject = &contextObjects;
768 contextObjects = data;
771 void QQmlContextData::setIdProperty(int idx, QObject *obj)
774 idValues[idx].context = this;
777 void QQmlContextData::setIdPropertyData(QQmlIntegerCache *data)
779 Q_ASSERT(!propertyNames);
780 propertyNames = data;
781 propertyNames->addref();
783 idValueCount = data->count();
784 idValues = new ContextGuard[idValueCount];
787 QString QQmlContextData::findObjectId(const QObject *obj) const
792 for (int ii = 0; ii < idValueCount; ii++) {
793 if (idValues[ii] == obj)
794 return propertyNames->findId(ii);
798 QQmlContextPrivate *p = QQmlContextPrivate::get(publicContext);
799 for (int ii = 0; ii < p->propertyValues.count(); ++ii)
800 if (p->propertyValues.at(ii) == QVariant::fromValue((QObject *)obj))
801 return propertyNames->findId(ii);
805 return linkedContext->findObjectId(obj);
809 QQmlContext *QQmlContextData::asQQmlContext()
812 publicContext = new QQmlContext(this);
813 return publicContext;
816 QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate()
818 return QQmlContextPrivate::get(asQQmlContext());