diff options
Diffstat (limited to 'src/declarative/qml/qmlcomponent.cpp')
-rw-r--r-- | src/declarative/qml/qmlcomponent.cpp | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp new file mode 100644 index 0000000..78137e8 --- /dev/null +++ b/src/declarative/qml/qmlcomponent.cpp @@ -0,0 +1,547 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlcomponent.h" +#include "qmlcomponent_p.h" +#include "private/qmlcontext_p.h" +#include "private/qmlengine_p.h" +#include "qmlvme_p.h" +#include "qml.h" +#include <QStack> +#include <qfxperf.h> +#include <QStringList> +#include <qmlengine.h> +#include <QFileInfo> +#include <qmlbindablevalue.h> +#include "qmlcompiledcomponent_p.h" +#include <QtCore/qdebug.h> +#include <QApplication> + +#include "qmlscriptparser_p.h" + +QT_BEGIN_NAMESPACE +class QByteArray; + +/*! + \class QmlComponent + \brief The QmlComponent class encapsulates a QML component description. + \mainclass +*/ + +/*! + \qmlclass Component QmlComponent + \brief The Component element encapsulates a QML component description. + + Components are reusable, encapsulated Qml element with a well-defined interface. + They are often defined in \l {components}{Component Files}. + + The \e Component element allows defining components within a QML file. + This can be useful for reusing a small component within a single QML + file, or for defining a component that logically belongs with the + file containing it. + + \qml +Item { + Component { + id: RedSquare + Rect { + color: "red" + width: 10 + height: 10 + } + } + ComponentInstance { component: RedSquare } + ComponentInstance { component: RedSquare; x: 20 } +} + \endqml +*/ +QML_DEFINE_TYPE(QmlComponent,Component); + +/*! + \enum QmlComponent::Status + + Specifies the loading status of the QmlComponent. + + \value Null This QmlComponent has no data. Call loadUrl() or setData() to add QML content. + \value Ready This QmlComponent is ready and create() may be called. + \value Loading This QmlComponent is loading network data. + \value Error An error has occured. Calling errorDescription() to retrieve a description. +*/ + +void QmlComponentPrivate::typeDataReady() +{ + Q_Q(QmlComponent); + + Q_ASSERT(typeData); + + fromTypeData(typeData); + typeData = 0; + + emit q->statusChanged(q->status()); +} + +void QmlComponentPrivate::fromTypeData(QmlCompositeTypeData *data) +{ + url = QUrl(data->url); + QmlCompiledComponent *c = data->toCompiledComponent(engine); + + if (!c) { + Q_ASSERT(data->status == QmlCompositeTypeData::Error); + + errors = data->errors; + + } else { + + cc = c; + + } + + data->release(); +} + +void QmlComponentPrivate::clear() +{ + if (typeData) { + typeData->remWaiter(this); + typeData->release(); + typeData = 0; + } + + if (cc) { + cc->release(); + cc = 0; + } +} + +/*! + \internal +*/ +QmlComponent::QmlComponent(QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ +} + +/*! + Destruct the QmlComponent. +*/ +QmlComponent::~QmlComponent() +{ + Q_D(QmlComponent); + if (d->typeData) { + d->typeData->remWaiter(d); + d->typeData->release(); + } + if (d->cc) + d->cc->release(); + + for(int ii = 0; ii < d->bindValues.count(); ++ii) + QmlEnginePrivate::clear(d->bindValues[ii]); + for(int ii = 0; ii < d->parserStatus.count(); ++ii) + QmlEnginePrivate::clear(d->parserStatus[ii]); +} + +/*! + Returns the component's current \l{QmlComponent::Status} {status}. + */ +QmlComponent::Status QmlComponent::status() const +{ + Q_D(const QmlComponent); + + if (d->typeData) + return Loading; + else if (!d->errors.isEmpty()) + return Error; + else if (d->engine && d->cc) + return Ready; + else + return Null; +} + +/*! + Returns true if the component is in the Null state, false otherwise. + + Equivalent to status() == QmlComponent::Null. +*/ +bool QmlComponent::isNull() const +{ + return status() == Null; +} + +/*! + Returns true if the component is in the Ready state, false otherwise. + + Equivalent to status() == QmlComponent::Ready. +*/ +bool QmlComponent::isReady() const +{ + return status() == Ready; +} + +/*! + Returns true if the component is in the Error state, false otherwise. + + Equivalent to status() == QmlComponent::Error. +*/ +bool QmlComponent::isError() const +{ + return status() == Error; +} + +/*! + Returns true if the component is in the Loading state, false otherwise. + + Equivalent to status() == QmlComponent::Loading. +*/ +bool QmlComponent::isLoading() const +{ + return status() == Loading; +} + +/*! + \fn void QmlComponent::statusChanged(QmlComponent::Status status) + + Emitted whenever the component's status changes. \a status will be the + new status. +*/ + +/*! + Create a QmlComponent with no data and give it the specified + \a engine and \a parent. Set the data with setData(). +*/ +QmlComponent::QmlComponent(QmlEngine *engine, QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; +} + +/*! + Create a QmlComponent from the given \a url and give it the + specified \a parent and \a engine. + + \sa loadUrl() +*/ +QmlComponent::QmlComponent(QmlEngine *engine, const QUrl &url, QObject *parent) +: QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; + loadUrl(url); +} + +/*! + Create a QmlComponent from the given QML \a data and give it the + specified \a parent and \a engine. If \a url is provided, it is used to set + the component name, and to provide a base path for items resolved + by this component. + + \sa setData() +*/ +QmlComponent::QmlComponent(QmlEngine *engine, const QByteArray &data, const QUrl &url, QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; + setData(data,url); +} + +/*! + \internal +*/ +QmlComponent::QmlComponent(QmlEngine *engine, QmlCompiledComponent *cc, int start, int count, QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; + d->cc = cc; + cc->addref(); + d->start = start; + d->count = count; +} + +/*! + Sets the QmlComponent to use the given QML \a data. If \a url + is provided, it is used to set the component name and to provide + a base path for items resolved by this component. +*/ +void QmlComponent::setData(const QByteArray &data, const QUrl &url) +{ + Q_D(QmlComponent); + + d->clear(); + + d->url = url; + + QmlCompositeTypeData *typeData = + d->engine->d_func()->typeManager.getImmediate(data, url); + + if (typeData->status == QmlCompositeTypeData::Waiting) { + + d->typeData = typeData; + d->typeData->addWaiter(d); + + } else { + + d->fromTypeData(typeData); + + } + + emit statusChanged(status()); +} + +/*! + Load the QmlComponent from the provided \a url. +*/ +void QmlComponent::loadUrl(const QUrl &url) +{ + Q_D(QmlComponent); + + d->clear(); + + d->url = url; + + QmlCompositeTypeData *data = + d->engine->d_func()->typeManager.get(url); + + if (data->status == QmlCompositeTypeData::Waiting) { + + d->typeData = data; + d->typeData->addWaiter(d); + + } else { + + d->fromTypeData(data); + + } + + emit statusChanged(status()); +} + +/*! + Return the list of errors that occured during the last compile or create + operation. An empty list is returned if isError() is not set. +*/ +QList<QmlError> QmlComponent::errors() const +{ + Q_D(const QmlComponent); + if (isError()) + return d->errors; + else + return QList<QmlError>(); +} + +/*! + Return the component URL. This is the URL passed to either the constructor, + or the loadUrl() or setData() methods. +*/ +QUrl QmlComponent::url() const +{ + Q_D(const QmlComponent); + return d->url; +} + +/*! + \internal +*/ +QmlComponent::QmlComponent(QmlComponentPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Create an object instance from this component. Returns 0 if creation + failed. \a context specifies the context within which to create the object + instance. + + If \a context is 0 (the default), it will create the instance in the + engine' s \l {QmlEngine::rootContext()}{root context}. +*/ +QObject *QmlComponent::create(QmlContext *context) +{ + Q_D(QmlComponent); + + if (!context) + context = d->engine->rootContext(); + + if (context->engine() != d->engine) { + qWarning("QmlComponent::create(): Must create component in context from the same QmlEngine"); + return 0; + } + + QObject *rv = beginCreate(context); + completeCreate(); + return rv; +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QmlComponent::create() to create a + component. + + Create an object instance from this component. Returns 0 if creation + failed. \a context specifies the context within which to create the object + instance. + + When QmlComponent constructs an instance, it occurs in three steps: + \list 1 + \i The object hierarchy is created, and constant values are assigned. + \i Property bindings are evaluated for the the first time. + \i If applicable, QmlParserStatus::componentComplete() is called on objects. + \endlist + QmlComponent::beginCreate() differs from QmlComponent::create() in that it + only performs step 1. QmlComponent::completeCreate() must be called to + complete steps 2 and 3. + + This breaking point is sometimes useful when using attached properties to + communicate information to an instantiated component, as it allows their + initial values to be configured before property bindings take effect. +*/ +QObject *QmlComponent::beginCreate(QmlContext *context) +{ + Q_D(QmlComponent); + + if (!context) { + qWarning("QmlComponent::beginCreate(): Cannot create a component in a null context"); + return 0; + } + + if (context->engine() != d->engine) { + qWarning("QmlComponent::beginCreate(): Must create component in context from the same QmlEngine"); + return 0; + } + + if (d->completePending) { + qWarning("QmlComponent: Cannot create new component instance before completing the previous"); + return 0; + } + + if (!isReady()) { + qWarning("QmlComponent: Component is not ready"); + return 0; + } + + if (!d->engine->d_func()->rootComponent) + d->engine->d_func()->rootComponent = this; + + QmlContext *ctxt = + new QmlContext(context, 0); + static_cast<QmlContextPrivate*>(ctxt->d_ptr)->url = d->cc->url; + if(d->start != -1) { + // ### FIXME + static_cast<QmlContextPrivate*>(ctxt->d_ptr)->startLine = d->cc->bytecode.at(d->start - 1).line; + static_cast<QmlContextPrivate*>(ctxt->d_ptr)->endLine = d->cc->bytecode.at(d->start - 1).createComponent.endLine; + } + ctxt->activate(); + + QmlVME vme; + QObject *rv = vme.run(ctxt, d->cc, d->start, d->count); + + if (vme.isError()) + d->errors = vme.errors(); + + ctxt->deactivate(); + + QmlEnginePrivate *ep = d->engine->d_func(); + if (ep->rootComponent == this) { + ep->rootComponent = 0; + + d->bindValues = ep->bindValues; + d->parserStatus = ep->parserStatus; + ep->bindValues.clear(); + ep->parserStatus.clear(); + d->completePending = true; + } + + if (rv) { + QFx_setParent_noEvent(ctxt, rv); + } else { + delete ctxt; + } + + return rv; +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QmlComponent::create() to create a + component. + + Complete a component creation begin with QmlComponent::beginCreate(). +*/ +void QmlComponent::completeCreate() +{ + Q_D(QmlComponent); + if (d->completePending) { + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindInit> bi; +#endif + for (int ii = 0; ii < d->bindValues.count(); ++ii) { + QmlEnginePrivate::SimpleList<QmlBindableValue> bv = + d->bindValues.at(ii); + for (int jj = 0; jj < bv.count; ++jj) { + if(bv.at(jj)) + bv.at(jj)->init(); + } + QmlEnginePrivate::clear(bv); + } + } + + for (int ii = 0; ii < d->parserStatus.count(); ++ii) { + QmlEnginePrivate::SimpleList<QmlParserStatus> ps = + d->parserStatus.at(ii); + + for (int jj = 0; jj < ps.count; ++jj) { + QmlParserStatus *status = ps.at(jj); + if (status && status->d) { + status->d = 0; + status->componentComplete(); + } + } + QmlEnginePrivate::clear(ps); + } + + d->bindValues.clear(); + d->parserStatus.clear(); + d->completePending = false; + } +} +QT_END_NAMESPACE |