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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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 "qdeclarativeinfo.h"
50 #include "private/qdeclarativev4bindings_p.h"
52 #include <qscriptengine.h>
53 #include <QtCore/qvarlengtharray.h>
54 #include <QtCore/qdebug.h>
56 #include <private/qscriptdeclarativeclass_p.h>
60 QDeclarativeContextPrivate::QDeclarativeContextPrivate()
61 : data(0), notifyIndex(-1)
66 \class QDeclarativeContext
68 \brief The QDeclarativeContext class defines a context within a QML engine.
71 Contexts allow data to be exposed to the QML components instantiated by the
74 Each QDeclarativeContext 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 QDeclarativeContext::setContextProperty(). The following example shows a Qt model
78 being bound to a context and then accessed from a QML file.
81 QDeclarativeEngine engine;
82 QStringListModel modelData;
83 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
84 context->setContextProperty("myModel", &modelData);
86 QDeclarativeComponent component(&engine);
87 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
88 QObject *window = component.create(context);
91 Note it is the responsibility of the creator to delete any QDeclarativeContext 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 QDeclarativeContext. 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 QDeclarativeContext::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)
114 QDeclarativeEngine engine;
115 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
116 context->setContextObject(&myDataSet);
118 QDeclarativeComponent component(&engine);
119 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
120 component.create(context);
123 All properties added explicitly by QDeclarativeContext::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 {QDeclarativeEngine::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
139 QDeclarativeEngine engine;
140 QDeclarativeContext *context1 = new QDeclarativeContext(engine.rootContext());
141 QDeclarativeContext *context2 = new QDeclarativeContext(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 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *e, bool)
163 : QObject(*(new QDeclarativeContextPrivate))
165 Q_D(QDeclarativeContext);
166 d->data = new QDeclarativeContextData(this);
172 Create a new QDeclarativeContext as a child of \a engine's root context, and the
175 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *engine, QObject *parent)
176 : QObject(*(new QDeclarativeContextPrivate), parent)
178 Q_D(QDeclarativeContext);
179 d->data = new QDeclarativeContextData(this);
181 d->data->setParent(engine?QDeclarativeContextData::get(engine->rootContext()):0);
185 Create a new QDeclarativeContext with the given \a parentContext, and the
188 QDeclarativeContext::QDeclarativeContext(QDeclarativeContext *parentContext, QObject *parent)
189 : QObject(*(new QDeclarativeContextPrivate), parent)
191 Q_D(QDeclarativeContext);
192 d->data = new QDeclarativeContextData(this);
194 d->data->setParent(parentContext?QDeclarativeContextData::get(parentContext):0);
200 QDeclarativeContext::QDeclarativeContext(QDeclarativeContextData *data)
201 : QObject(*(new QDeclarativeContextPrivate), 0)
203 Q_D(QDeclarativeContext);
208 Destroys the QDeclarativeContext.
210 Any expressions, or sub-contexts dependent on this context will be
211 invalidated, but not destroyed (unless they are parented to the QDeclarativeContext
214 QDeclarativeContext::~QDeclarativeContext()
216 Q_D(QDeclarativeContext);
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 QDeclarativeContext::isValid() const
230 Q_D(const QDeclarativeContext);
231 return d->data && d->data->isValid();
235 Return the context's QDeclarativeEngine, or 0 if the context has no QDeclarativeEngine or the
236 QDeclarativeEngine was destroyed.
238 QDeclarativeEngine *QDeclarativeContext::engine() const
240 Q_D(const QDeclarativeContext);
241 return d->data->engine;
245 Return the context's parent QDeclarativeContext, or 0 if this context has no
246 parent or if the parent has been destroyed.
248 QDeclarativeContext *QDeclarativeContext::parentContext() const
250 Q_D(const QDeclarativeContext);
251 return d->data->parent?d->data->parent->asQDeclarativeContext():0;
255 Return the context object, or 0 if there is no context object.
257 QObject *QDeclarativeContext::contextObject() const
259 Q_D(const QDeclarativeContext);
260 return d->data->contextObject;
264 Set the context \a object.
266 void QDeclarativeContext::setContextObject(QObject *object)
268 Q_D(QDeclarativeContext);
270 QDeclarativeContextData *data = d->data;
272 if (data->isInternal) {
273 qWarning("QDeclarativeContext: Cannot set context object for internal context.");
278 qWarning("QDeclarativeContext: 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 QDeclarativeContext::setContextProperty(const QString &name, const QVariant &value)
290 Q_D(QDeclarativeContext);
291 if (d->notifyIndex == -1)
292 d->notifyIndex = this->metaObject()->methodCount();
294 QDeclarativeContextData *data = d->data;
296 if (data->isInternal) {
297 qWarning("QDeclarativeContext: Cannot set property on internal context.");
302 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
308 QObject *o = QDeclarativeEnginePrivate::get(data->engine)->toQObject(value, &ok);
310 setContextProperty(name, o);
315 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache();
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 QDeclarativeContext does \bold not take ownership of \a value.
334 void QDeclarativeContext::setContextProperty(const QString &name, QObject *value)
336 Q_D(QDeclarativeContext);
337 if (d->notifyIndex == -1)
338 d->notifyIndex = this->metaObject()->methodCount();
340 QDeclarativeContextData *data = d->data;
342 if (data->isInternal) {
343 qWarning("QDeclarativeContext: Cannot set property on internal context.");
348 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
352 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache();
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 QDeclarativeContext::contextProperty(const QString &name) const
372 Q_D(const QDeclarativeContext);
376 QDeclarativeContextData *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 QDeclarativePropertyCache::Data local;
386 QDeclarativePropertyCache::Data *property =
387 QDeclarativePropertyCache::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 Resolves the URL \a src relative to the URL of the
405 containing component.
407 \sa QDeclarativeEngine::baseUrl(), setBaseUrl()
409 QUrl QDeclarativeContext::resolvedUrl(const QUrl &src)
411 Q_D(QDeclarativeContext);
412 return d->data->resolvedUrl(src);
415 QUrl QDeclarativeContextData::resolvedUrl(const QUrl &src)
417 QDeclarativeContextData *ctxt = this;
419 if (src.isRelative() && !src.isEmpty()) {
422 if(ctxt->url.isValid())
429 return ctxt->url.resolved(src);
431 return engine->baseUrl().resolved(src);
441 Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl.
443 Calling this function will override the url of the containing
444 component used by default.
448 void QDeclarativeContext::setBaseUrl(const QUrl &baseUrl)
450 Q_D(QDeclarativeContext);
452 d->data->url = baseUrl;
456 Returns the base url of the component, or the containing component
459 QUrl QDeclarativeContext::baseUrl() const
461 Q_D(const QDeclarativeContext);
462 const QDeclarativeContextData* data = d->data;
463 while (data && data->url.isEmpty())
472 int QDeclarativeContextPrivate::context_count(QDeclarativeListProperty<QObject> *prop)
474 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
475 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
476 int contextProperty = (int)(quintptr)prop->data;
478 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
481 return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count();
485 QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty<QObject> *prop, int index)
487 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
488 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
489 int contextProperty = (int)(quintptr)prop->data;
491 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
494 return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index);
499 QDeclarativeContextData::QDeclarativeContextData()
500 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), publicContext(0),
501 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
502 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0),
503 linkedContext(0), componentAttached(0)
507 QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt)
508 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), publicContext(ctxt),
509 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
510 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), optimizedBindings(0),
511 linkedContext(0), componentAttached(0)
515 void QDeclarativeContextData::invalidate()
517 while (childContexts) {
518 if (childContexts->ownedByParent) {
519 childContexts->destroy();
521 childContexts->invalidate();
525 while (componentAttached) {
526 QDeclarativeComponentAttached *a = componentAttached;
527 componentAttached = a->next;
528 if (componentAttached) componentAttached->prev = &componentAttached;
533 emit a->destruction();
537 *prevChild = nextChild;
538 if (nextChild) nextChild->prevChild = prevChild;
547 void QDeclarativeContextData::clearContext()
550 while (componentAttached) {
551 QDeclarativeComponentAttached *a = componentAttached;
552 componentAttached = a->next;
553 if (componentAttached) componentAttached->prev = &componentAttached;
558 emit a->destruction();
562 QDeclarativeAbstractExpression *expression = expressions;
564 QDeclarativeAbstractExpression *nextExpression = expression->m_nextExpression;
566 expression->m_context = 0;
567 expression->m_prevExpression = 0;
568 expression->m_nextExpression = 0;
570 expression = nextExpression;
575 void QDeclarativeContextData::destroy()
578 linkedContext->destroy();
580 if (engine) invalidate();
584 while (contextObjects) {
585 QDeclarativeData *co = contextObjects;
586 contextObjects = contextObjects->nextContextObject;
589 co->outerContext = 0;
590 co->nextContextObject = 0;
591 co->prevContextObject = 0;
594 QDeclarativeGuardedContextData *contextGuard = contextGuards;
595 while (contextGuard) {
596 QDeclarativeGuardedContextData *next = contextGuard->m_next;
597 contextGuard->m_next = 0;
598 contextGuard->m_prev = 0;
599 contextGuard->m_contextData = 0;
605 propertyNames->release();
610 if (optimizedBindings)
611 optimizedBindings->release();
613 for (int ii = 0; ii < importedScripts.count(); ++ii) {
614 qPersistentDispose(importedScripts[ii]);
620 delete publicContext;
625 void QDeclarativeContextData::setParent(QDeclarativeContextData *p, bool parentTakesOwnership)
630 nextChild = p->childContexts;
631 if (nextChild) nextChild->prevChild = &nextChild;
632 prevChild = &p->childContexts;
633 p->childContexts = this;
634 ownedByParent = parentTakesOwnership;
639 Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
640 context-tree dependent caches in the expressions, and should occur every time the context tree
641 *structure* (not values) changes.
643 void QDeclarativeContextData::refreshExpressions()
645 QDeclarativeContextData *child = childContexts;
647 child->refreshExpressions();
648 child = child->nextChild;
651 QDeclarativeAbstractExpression *expression = expressions;
653 expression->refresh();
654 expression = expression->m_nextExpression;
658 void QDeclarativeContextData::addObject(QObject *o)
660 QDeclarativeData *data = QDeclarativeData::get(o, true);
662 Q_ASSERT(data->context == 0);
664 data->context = this;
665 data->outerContext = this;
667 data->nextContextObject = contextObjects;
668 if (data->nextContextObject)
669 data->nextContextObject->prevContextObject = &data->nextContextObject;
670 data->prevContextObject = &contextObjects;
671 contextObjects = data;
674 void QDeclarativeContextData::setIdProperty(int idx, QObject *obj)
677 idValues[idx].context = this;
680 void QDeclarativeContextData::setIdPropertyData(QDeclarativeIntegerCache *data)
682 Q_ASSERT(!propertyNames);
683 propertyNames = data;
684 propertyNames->addref();
686 idValueCount = data->count();
687 idValues = new ContextGuard[idValueCount];
690 QString QDeclarativeContextData::findObjectId(const QObject *obj) const
692 if (!idValues || !propertyNames)
695 for (int i=0; i<idValueCount; i++) {
696 if (idValues[i] == obj)
697 return propertyNames->findId(i);
701 return linkedContext->findObjectId(obj);
705 QDeclarativeContext *QDeclarativeContextData::asQDeclarativeContext()
708 publicContext = new QDeclarativeContext(this);
709 return publicContext;
712 QDeclarativeContextPrivate *QDeclarativeContextData::asQDeclarativeContextPrivate()
714 return QDeclarativeContextPrivate::get(asQDeclarativeContext());