diff options
Diffstat (limited to 'src/declarative/qml/qdeclarativecomponent.cpp')
-rw-r--r-- | src/declarative/qml/qdeclarativecomponent.cpp | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp new file mode 100644 index 0000000000..8238252db2 --- /dev/null +++ b/src/declarative/qml/qdeclarativecomponent.cpp @@ -0,0 +1,1078 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecomponent.h" +#include "private/qdeclarativecomponent_p.h" + +#include "private/qdeclarativecompiler_p.h" +#include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativevme_p.h" +#include "qdeclarative.h" +#include "qdeclarativeengine.h" +#include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativebinding_p_p.h" +#include "private/qdeclarativeglobal_p.h" +#include "private/qdeclarativescriptparser_p.h" +#include "private/qdeclarativedebugtrace_p.h" +#include "private/qdeclarativeenginedebug_p.h" +#include <QtScript/qscriptvalueiterator.h> + +#include <QStack> +#include <QStringList> +#include <QtCore/qdebug.h> +#include <QApplication> +#include <qdeclarativeinfo.h> + +QT_BEGIN_NAMESPACE + +class QByteArray; + +/*! + \class QDeclarativeComponent + \since 4.7 + \brief The QDeclarativeComponent class encapsulates a QML component definition. + \mainclass + + Components are reusable, encapsulated QML elements with well-defined interfaces. + They are often defined in \l {qdeclarativedocuments.html}{Component Files}. + + A QDeclarativeComponent instance can be created from a QML file. + For example, if there is a \c main.qml file like this: + + \qml + import QtQuick 1.0 + + Item { + width: 200 + height: 200 + } + \endqml + + The following code loads this QML file as a component, creates an instance of + this component using create(), and then queries the \l Item's \l {Item::}{width} + value: + + \code + QDeclarativeEngine *engine = new QDeclarativeEngine; + QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml")); + + QObject *myObject = component.create(); + QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(myObject); + int width = item->width(); // width = 200 + \endcode + + + \section2 Network Components + + If the URL passed to QDeclarativeComponent is a network resource, or if the QML document references a + network resource, the QDeclarativeComponent has to fetch the network data before it is able to create + objects. In this case, the QDeclarativeComponent will have a \l {QDeclarativeComponent::Loading}{Loading} + \l {QDeclarativeComponent::status()}{status}. An application will have to wait until the component + is \l {QDeclarativeComponent::Ready}{Ready} before calling \l {QDeclarativeComponent::create()}. + + The following example shows how to load a QML file from a network resource. After creating + the QDeclarativeComponent, it tests whether the component is loading. If it is, it connects to the + QDeclarativeComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method + directly. Note that QDeclarativeComponent::isLoading() may be false for a network component if the + component has been cached and is ready immediately. + + \code + MyApplication::MyApplication() + { + // ... + component = new QDeclarativeComponent(engine, QUrl("http://www.example.com/main.qml")); + if (component->isLoading()) + QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), + this, SLOT(continueLoading())); + else + continueLoading(); + } + + void MyApplication::continueLoading() + { + if (component->isError()) { + qWarning() << component->errors(); + } else { + QObject *myObject = component->create(); + } + } + \endcode + + \sa {Using QML Bindings in C++ Applications}, {Integrating QML Code with Existing Qt UI Code} +*/ + +/*! + \qmlclass Component QDeclarativeComponent + \ingroup qml-utility-elements + \since 4.7 + \brief The Component element encapsulates a QML component definition. + + Components are reusable, encapsulated QML elements with well-defined interfaces. + + Components are often defined by \l {qdeclarativedocuments.html}{component files} - + that is, \c .qml files. The \e Component element essentially allows QML components + to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file. + This may be useful for reusing a small component within a QML file, or for defining + a component that logically belongs with other QML components within a file. + + For example, here is a component that is used by multiple \l Loader objects. + It contains a single item, a \l Rectangle: + + \snippet doc/src/snippets/declarative/component.qml 0 + + Notice that while a \l Rectangle by itself would be automatically + rendered and displayed, this is not the case for the above rectangle + because it is defined inside a \c Component. The component encapsulates the + QML elements within, as if they were defined in a separate QML + file, and is not loaded until requested (in this case, by the + two \l Loader objects). + + Defining a \c Component is similar to defining a \l {QML Document}{QML document}. + A QML document has a single top-level item that defines the behaviors and + properties of that component, and cannot define properties or behaviors outside + of that top-level item. In the same way, a \c Component definition contains a single + top level item (which in the above example is a \l Rectangle) and cannot define any + data outside of this item, with the exception of an \e id (which in the above example + is \e redSquare). + + The \c Component element is commonly used to provide graphical components + for views. For example, the ListView::delegate property requires a \c Component + to specify how each list item is to be displayed. + + \c Component objects can also be created dynamically using + \l{QML:Qt::createComponent()}{Qt.createComponent()}. +*/ + +/*! + \qmlattachedsignal Component::onCompleted() + + Emitted after component "startup" has completed. This can be used to + execute script code at startup, once the full QML environment has been + established. + + The \c {Component::onCompleted} attached property can be applied to + any element. The order of running the \c onCompleted scripts is + undefined. + + \qml + Rectangle { + Component.onCompleted: console.log("Completed Running!") + Rectangle { + Component.onCompleted: console.log("Nested Completed Running!") + } + } + \endqml +*/ + +/*! + \qmlattachedsignal Component::onDestruction() + + Emitted as the component begins destruction. This can be used to undo + work done in the onCompleted signal, or other imperative code in your + application. + + The \c {Component::onDestruction} attached property can be applied to + any element. However, it applies to the destruction of the component as + a whole, and not the destruction of the specific object. The order of + running the \c onDestruction scripts is undefined. + + \qml + Rectangle { + Component.onDestruction: console.log("Destruction Beginning!") + Rectangle { + Component.onDestruction: console.log("Nested Destruction Beginning!") + } + } + \endqml + + \sa QtDeclarative +*/ + +/*! + \enum QDeclarativeComponent::Status + + Specifies the loading status of the QDeclarativeComponent. + + \value Null This QDeclarativeComponent has no data. Call loadUrl() or setData() to add QML content. + \value Ready This QDeclarativeComponent is ready and create() may be called. + \value Loading This QDeclarativeComponent is loading network data. + \value Error An error has occurred. Call errors() to retrieve a list of \{QDeclarativeError}{errors}. +*/ + +void QDeclarativeComponentPrivate::typeDataReady(QDeclarativeTypeData *) +{ + Q_Q(QDeclarativeComponent); + + Q_ASSERT(typeData); + + fromTypeData(typeData); + typeData = 0; + + emit q->statusChanged(q->status()); +} + +void QDeclarativeComponentPrivate::typeDataProgress(QDeclarativeTypeData *, qreal p) +{ + Q_Q(QDeclarativeComponent); + + progress = p; + + emit q->progressChanged(p); +} + +void QDeclarativeComponentPrivate::fromTypeData(QDeclarativeTypeData *data) +{ + url = data->finalUrl(); + QDeclarativeCompiledData *c = data->compiledData(); + + if (!c) { + Q_ASSERT(data->isError()); + state.errors = data->errors(); + } else { + cc = c; + } + + data->release(); +} + +void QDeclarativeComponentPrivate::clear() +{ + if (typeData) { + typeData->unregisterCallback(this); + typeData->release(); + typeData = 0; + } + + if (cc) { + cc->release(); + cc = 0; + } +} + +/*! + \internal +*/ +QDeclarativeComponent::QDeclarativeComponent(QObject *parent) + : QObject(*(new QDeclarativeComponentPrivate), parent) +{ +} + +/*! + Destruct the QDeclarativeComponent. +*/ +QDeclarativeComponent::~QDeclarativeComponent() +{ + Q_D(QDeclarativeComponent); + + if (d->state.completePending) { + qWarning("QDeclarativeComponent: Component destroyed while completion pending"); + d->completeCreate(); + } + + if (d->typeData) { + d->typeData->unregisterCallback(d); + d->typeData->release(); + } + if (d->cc) + d->cc->release(); +} + +/*! + \qmlproperty enumeration Component::status + This property holds the status of component loading. It can be one of: + \list + \o Component.Null - no data is available for the component + \o Component.Ready - the component has been loaded, and can be used to create instances. + \o Component.Loading - the component is currently being loaded + \o Component.Error - an error occurred while loading the component. + Calling errorString() will provide a human-readable description of any errors. + \endlist + */ + +/*! + \property QDeclarativeComponent::status + The component's current \l{QDeclarativeComponent::Status} {status}. + */ +QDeclarativeComponent::Status QDeclarativeComponent::status() const +{ + Q_D(const QDeclarativeComponent); + + if (d->typeData) + return Loading; + else if (!d->state.errors.isEmpty()) + return Error; + else if (d->engine && d->cc) + return Ready; + else + return Null; +} + +/*! + Returns true if status() == QDeclarativeComponent::Null. +*/ +bool QDeclarativeComponent::isNull() const +{ + return status() == Null; +} + +/*! + Returns true if status() == QDeclarativeComponent::Ready. +*/ +bool QDeclarativeComponent::isReady() const +{ + return status() == Ready; +} + +/*! + Returns true if status() == QDeclarativeComponent::Error. +*/ +bool QDeclarativeComponent::isError() const +{ + return status() == Error; +} + +/*! + Returns true if status() == QDeclarativeComponent::Loading. +*/ +bool QDeclarativeComponent::isLoading() const +{ + return status() == Loading; +} + +/*! + \qmlproperty real Component::progress + The progress of loading the component, from 0.0 (nothing loaded) + to 1.0 (finished). +*/ + +/*! + \property QDeclarativeComponent::progress + The progress of loading the component, from 0.0 (nothing loaded) + to 1.0 (finished). +*/ +qreal QDeclarativeComponent::progress() const +{ + Q_D(const QDeclarativeComponent); + return d->progress; +} + +/*! + \fn void QDeclarativeComponent::progressChanged(qreal progress) + + Emitted whenever the component's loading progress changes. \a progress will be the + current progress between 0.0 (nothing loaded) and 1.0 (finished). +*/ + +/*! + \fn void QDeclarativeComponent::statusChanged(QDeclarativeComponent::Status status) + + Emitted whenever the component's status changes. \a status will be the + new status. +*/ + +/*! + Create a QDeclarativeComponent with no data and give it the specified + \a engine and \a parent. Set the data with setData(). +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QObject *parent) + : QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; +} + +/*! + Create a QDeclarativeComponent from the given \a url and give it the + specified \a parent and \a engine. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + \sa loadUrl() +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QUrl &url, QObject *parent) +: QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; + loadUrl(url); +} + +/*! + Create a QDeclarativeComponent from the given \a fileName and give it the specified + \a parent and \a engine. + + \sa loadUrl() +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QString &fileName, + QObject *parent) +: QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; + loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName))); +} + +/*! + \internal +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QDeclarativeCompiledData *cc, int start, int count, QObject *parent) + : QObject(*(new QDeclarativeComponentPrivate), parent) +{ + Q_D(QDeclarativeComponent); + d->engine = engine; + d->cc = cc; + cc->addref(); + d->start = start; + d->count = count; + d->url = cc->url; + d->progress = 1.0; +} + +/*! + Sets the QDeclarativeComponent 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 QDeclarativeComponent::setData(const QByteArray &data, const QUrl &url) +{ + Q_D(QDeclarativeComponent); + + d->clear(); + + d->url = url; + + QDeclarativeTypeData *typeData = QDeclarativeEnginePrivate::get(d->engine)->typeLoader.get(data, url); + + if (typeData->isCompleteOrError()) { + d->fromTypeData(typeData); + } else { + d->typeData = typeData; + d->typeData->registerCallback(d); + } + + d->progress = 1.0; + emit statusChanged(status()); + emit progressChanged(d->progress); +} + +/*! +Returns the QDeclarativeContext the component was created in. This is only +valid for components created directly from QML. +*/ +QDeclarativeContext *QDeclarativeComponent::creationContext() const +{ + Q_D(const QDeclarativeComponent); + if(d->creationContext) + return d->creationContext->asQDeclarativeContext(); + + return qmlContext(this); +} + +/*! + Load the QDeclarativeComponent from the provided \a url. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. +*/ +void QDeclarativeComponent::loadUrl(const QUrl &url) +{ + Q_D(QDeclarativeComponent); + + d->clear(); + + if ((url.isRelative() && !url.isEmpty()) + || url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929 + d->url = d->engine->baseUrl().resolved(url); + else + d->url = url; + + if (url.isEmpty()) { + QDeclarativeError error; + error.setDescription(tr("Invalid empty URL")); + d->state.errors << error; + return; + } + + QDeclarativeTypeData *data = QDeclarativeEnginePrivate::get(d->engine)->typeLoader.get(d->url); + + if (data->isCompleteOrError()) { + d->fromTypeData(data); + d->progress = 1.0; + } else { + d->typeData = data; + d->typeData->registerCallback(d); + d->progress = data->progress(); + } + + emit statusChanged(status()); + emit progressChanged(d->progress); +} + +/*! + Return the list of errors that occurred during the last compile or create + operation. An empty list is returned if isError() is not set. +*/ +QList<QDeclarativeError> QDeclarativeComponent::errors() const +{ + Q_D(const QDeclarativeComponent); + if (isError()) + return d->state.errors; + else + return QList<QDeclarativeError>(); +} + +/*! + \qmlmethod string Component::errorString() + + Returns a human-readable description of any errors. + + The string includes the file, location, and description of each error. + If multiple errors are present they are separated by a newline character. + + If no errors are present, an empty string is returned. +*/ + +/*! + \internal + errorString is only meant as a way to get the errors in script +*/ +QString QDeclarativeComponent::errorString() const +{ + Q_D(const QDeclarativeComponent); + QString ret; + if(!isError()) + return ret; + foreach(const QDeclarativeError &e, d->state.errors) { + ret += e.url().toString() + QLatin1Char(':') + + QString::number(e.line()) + QLatin1Char(' ') + + e.description() + QLatin1Char('\n'); + } + return ret; +} + +/*! + \qmlproperty url Component::url + The component URL. This is the URL that was used to construct the component. +*/ + +/*! + \property QDeclarativeComponent::url + The component URL. This is the URL passed to either the constructor, + or the loadUrl() or setData() methods. +*/ +QUrl QDeclarativeComponent::url() const +{ + Q_D(const QDeclarativeComponent); + return d->url; +} + +/*! + \internal +*/ +QDeclarativeComponent::QDeclarativeComponent(QDeclarativeComponentPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlmethod object Component::createObject(Item parent, object properties) + + Creates and returns an object instance of this component that will have + the given \a parent and \a properties. The \a properties argument is optional. + Returns null if object creation fails. + + The object will be created in the same context as the one in which the component + was created. This function will always return null when called on components + which were not created in QML. + + If you wish to create an object without setting a parent, specify \c null for + the \a parent value. Note that if the returned object is to be displayed, you + must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} + property, or else the object will not be visible. + + If a \a parent is not provided to createObject(), a reference to the returned object must be held so that + it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, + since setting the Item parent does not change object ownership; only the graphical parent is changed. + + As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a + map of initial property values for the created object. These values are applied before object + creation is finalized. (This is more efficient than setting property values after object creation, + particularly where large sets of property values are defined, and also allows property bindings + to be set up before the object is created.) + + The \a properties argument is specified as a map of property-value items. For example, the code + below creates an object with initial \c x and \c y values of 100 and 200, respectively: + + \js + var component = Qt.createComponent("Button.qml"); + if (component.status == Component.Ready) + component.createObject(parent, {"x": 100, "y": 100}); + \endjs + + Dynamically created instances can be deleted with the \c destroy() method. + See \l {Dynamic Object Management in QML} for more information. +*/ + +/*! + \internal + A version of create which returns a scriptObject, for use in script. + This function will only work on components created in QML. + + Sets graphics object parent because forgetting to do this is a frequent + and serious problem. +*/ +QScriptValue QDeclarativeComponent::createObject(QObject* parent) +{ + Q_D(QDeclarativeComponent); + return d->createObject(parent, QScriptValue(QScriptValue::NullValue)); +} + +/*! + \internal + Overloadable method allows properties to be set during creation +*/ +QScriptValue QDeclarativeComponent::createObject(QObject* parent, const QScriptValue& valuemap) +{ + Q_D(QDeclarativeComponent); + + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("createObject: value is not an object"); + return QScriptValue(QScriptValue::NullValue); + } + return d->createObject(parent, valuemap); +} + +QScriptValue QDeclarativeComponentPrivate::createObject(QObject *publicParent, const QScriptValue valuemap) +{ + Q_Q(QDeclarativeComponent); + QDeclarativeContext* ctxt = q->creationContext(); + if(!ctxt && engine) + ctxt = engine->rootContext(); + if (!ctxt) + return QScriptValue(QScriptValue::NullValue); + QObject* ret = q->beginCreate(ctxt); + if (!ret) { + q->completeCreate(); + return QScriptValue(QScriptValue::NullValue); + } + + if (publicParent) { + ret->setParent(publicParent); + QList<QDeclarativePrivate::AutoParentFunction> functions = QDeclarativeMetaType::parentFunctions(); + + bool needParent = false; + + for (int ii = 0; ii < functions.count(); ++ii) { + QDeclarativePrivate::AutoParentResult res = functions.at(ii)(ret, publicParent); + if (res == QDeclarativePrivate::Parented) { + needParent = false; + break; + } else if (res == QDeclarativePrivate::IncompatibleParent) { + needParent = true; + } + } + + if (needParent) + qWarning("QDeclarativeComponent: Created graphical object was not placed in the graphics scene."); + } + + QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine); + QDeclarativeData::get(ret, true)->setImplicitDestructible(); + QScriptValue newObject = priv->objectClass->newQObject(ret, QMetaType::QObjectStar); + + if (valuemap.isObject() && !valuemap.isArray()) { + //Iterate through and assign properties + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + QScriptValue prop = newObject; + QString propName = it.name(); + int index = propName.indexOf(QLatin1Char('.')); + if (index > 0) { + QString subProp = propName; + int lastIndex = 0; + while (index > 0) { + subProp = propName.mid(lastIndex, index - lastIndex); + prop = prop.property(subProp); + lastIndex = index + 1; + index = propName.indexOf(QLatin1Char('.'), index + 1); + } + prop.setProperty(propName.mid(propName.lastIndexOf(QLatin1Char('.')) + 1), it.value()); + } else { + newObject.setProperty(propName, it.value()); + } + } + } + + q->completeCreate(); + + return newObject; +} + +/*! + 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 {QDeclarativeEngine::rootContext()}{root context}. +*/ +QObject *QDeclarativeComponent::create(QDeclarativeContext *context) +{ + Q_D(QDeclarativeComponent); + + if (!context) + context = d->engine->rootContext(); + + QObject *rv = beginCreate(context); + completeCreate(); + return rv; +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QDeclarativeComponent::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 QDeclarativeComponent 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, QDeclarativeParserStatus::componentComplete() is called on objects. + \endlist + QDeclarativeComponent::beginCreate() differs from QDeclarativeComponent::create() in that it + only performs step 1. QDeclarativeComponent::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 *QDeclarativeComponent::beginCreate(QDeclarativeContext *context) +{ + Q_D(QDeclarativeComponent); + QObject *rv = d->beginCreate(context?QDeclarativeContextData::get(context):0, QBitField()); + if (rv) { + QDeclarativeData *ddata = QDeclarativeData::get(rv); + Q_ASSERT(ddata); + ddata->indestructible = true; + } + return rv; +} + +QObject * +QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context, const QBitField &bindings) +{ + Q_Q(QDeclarativeComponent); + if (!context) { + qWarning("QDeclarativeComponent: Cannot create a component in a null context"); + return 0; + } + + if (!context->isValid()) { + qWarning("QDeclarativeComponent: Cannot create a component in an invalid context"); + return 0; + } + + if (context->engine != engine) { + qWarning("QDeclarativeComponent: Must create component in context from the same QDeclarativeEngine"); + return 0; + } + + if (state.completePending) { + qWarning("QDeclarativeComponent: Cannot create new component instance before completing the previous"); + return 0; + } + + if (!q->isReady()) { + qWarning("QDeclarativeComponent: Component is not ready"); + return 0; + } + + return begin(context, creationContext, cc, start, count, &state, 0, bindings); +} + +QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentContext, + QDeclarativeContextData *componentCreationContext, + QDeclarativeCompiledData *component, int start, int count, + ConstructionState *state, QList<QDeclarativeError> *errors, + const QBitField &bindings) +{ + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(parentContext->engine); + bool isRoot = !enginePriv->inBeginCreate; + + Q_ASSERT(!isRoot || state); // Either this isn't a root component, or a state data must be provided + Q_ASSERT((state != 0) ^ (errors != 0)); // One of state or errors (but not both) must be provided + + if (isRoot) { + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating); + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, component->url); + } + + QDeclarativeContextData *ctxt = new QDeclarativeContextData; + ctxt->isInternal = true; + ctxt->url = component->url; + ctxt->imports = component->importCache; + + // Nested global imports + if (componentCreationContext && start != -1) + ctxt->importedScripts = componentCreationContext->importedScripts; + + component->importCache->addref(); + ctxt->setParent(parentContext); + + enginePriv->inBeginCreate = true; + + QDeclarativeVME vme; + QObject *rv = vme.run(ctxt, component, start, count, bindings); + + if (vme.isError()) { + if(errors) *errors = vme.errors(); + else state->errors = vme.errors(); + } + + if (isRoot) { + enginePriv->inBeginCreate = false; + + state->bindValues = enginePriv->bindValues; + state->parserStatus = enginePriv->parserStatus; + state->finalizedParserStatus = enginePriv->finalizedParserStatus; + state->componentAttached = enginePriv->componentAttached; + if (state->componentAttached) + state->componentAttached->prev = &state->componentAttached; + + enginePriv->componentAttached = 0; + enginePriv->bindValues.clear(); + enginePriv->parserStatus.clear(); + enginePriv->finalizedParserStatus.clear(); + state->completePending = true; + enginePriv->inProgressCreations++; + } + + if (enginePriv->isDebugging && rv) { + if (!parentContext->isInternal) + parentContext->asQDeclarativeContextPrivate()->instances.append(rv); + QDeclarativeEngineDebugServer::instance()->objectCreated(parentContext->engine, rv); + } + + return rv; +} + +void QDeclarativeComponentPrivate::beginDeferred(QDeclarativeEnginePrivate *enginePriv, + QObject *object, ConstructionState *state) +{ + bool isRoot = !enginePriv->inBeginCreate; + enginePriv->inBeginCreate = true; + + QDeclarativeVME vme; + vme.runDeferred(object); + + if (vme.isError()) + state->errors = vme.errors(); + + if (isRoot) { + enginePriv->inBeginCreate = false; + + state->bindValues = enginePriv->bindValues; + state->parserStatus = enginePriv->parserStatus; + state->finalizedParserStatus = enginePriv->finalizedParserStatus; + state->componentAttached = enginePriv->componentAttached; + if (state->componentAttached) + state->componentAttached->prev = &state->componentAttached; + + enginePriv->componentAttached = 0; + enginePriv->bindValues.clear(); + enginePriv->parserStatus.clear(); + enginePriv->finalizedParserStatus.clear(); + state->completePending = true; + enginePriv->inProgressCreations++; + } +} + +void QDeclarativeComponentPrivate::complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state) +{ + if (state->completePending) { + + for (int ii = 0; ii < state->bindValues.count(); ++ii) { + QDeclarativeEnginePrivate::SimpleList<QDeclarativeAbstractBinding> bv = + state->bindValues.at(ii); + for (int jj = 0; jj < bv.count; ++jj) { + if(bv.at(jj)) { + // XXX akennedy + bv.at(jj)->m_mePtr = 0; + bv.at(jj)->setEnabled(true, QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + QDeclarativeEnginePrivate::clear(bv); + } + + for (int ii = 0; ii < state->parserStatus.count(); ++ii) { + QDeclarativeEnginePrivate::SimpleList<QDeclarativeParserStatus> ps = + state->parserStatus.at(ii); + + for (int jj = ps.count - 1; jj >= 0; --jj) { + QDeclarativeParserStatus *status = ps.at(jj); + if (status && status->d) { + status->d = 0; + status->componentComplete(); + } + } + QDeclarativeEnginePrivate::clear(ps); + } + + for (int ii = 0; ii < state->finalizedParserStatus.count(); ++ii) { + QPair<QDeclarativeGuard<QObject>, int> status = state->finalizedParserStatus.at(ii); + QObject *obj = status.first; + if (obj) { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, + status.second, args); + } + } + + //componentComplete() can register additional finalization objects + //that are then never handled. Handle them manually here. + if (1 == enginePriv->inProgressCreations) { + for (int ii = 0; ii < enginePriv->finalizedParserStatus.count(); ++ii) { + QPair<QDeclarativeGuard<QObject>, int> status = enginePriv->finalizedParserStatus.at(ii); + QObject *obj = status.first; + if (obj) { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, + status.second, args); + } + } + enginePriv->finalizedParserStatus.clear(); + } + + while (state->componentAttached) { + QDeclarativeComponentAttached *a = state->componentAttached; + a->rem(); + QDeclarativeData *d = QDeclarativeData::get(a->parent()); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + emit a->completed(); + } + + state->bindValues.clear(); + state->parserStatus.clear(); + state->finalizedParserStatus.clear(); + state->completePending = false; + + enginePriv->inProgressCreations--; + if (0 == enginePriv->inProgressCreations) { + while (enginePriv->erroredBindings) { + enginePriv->warning(enginePriv->erroredBindings->error); + enginePriv->erroredBindings->removeError(); + } + } + } +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QDeclarativeComponent::create() to create a + component. + + Complete a component creation begin with QDeclarativeComponent::beginCreate(). +*/ +void QDeclarativeComponent::completeCreate() +{ + Q_D(QDeclarativeComponent); + d->completeCreate(); +} + +void QDeclarativeComponentPrivate::completeCreate() +{ + if (state.completePending) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + complete(ep, &state); + + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Creating); + } +} + +QDeclarativeComponentAttached::QDeclarativeComponentAttached(QObject *parent) +: QObject(parent), prev(0), next(0) +{ +} + +QDeclarativeComponentAttached::~QDeclarativeComponentAttached() +{ + if (prev) *prev = next; + if (next) next->prev = prev; + prev = 0; + next = 0; +} + +/*! + \internal +*/ +QDeclarativeComponentAttached *QDeclarativeComponent::qmlAttachedProperties(QObject *obj) +{ + QDeclarativeComponentAttached *a = new QDeclarativeComponentAttached(obj); + + QDeclarativeEngine *engine = qmlEngine(obj); + if (!engine) + return a; + + if (QDeclarativeEnginePrivate::get(engine)->inBeginCreate) { + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); + a->add(&p->componentAttached); + } else { + QDeclarativeData *d = QDeclarativeData::get(obj); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + } + + return a; +} + +QT_END_NAMESPACE |