diff options
Diffstat (limited to 'src/declarative/qml/qdeclarativecomponent.cpp')
-rw-r--r-- | src/declarative/qml/qdeclarativecomponent.cpp | 1351 |
1 files changed, 0 insertions, 1351 deletions
diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp deleted file mode 100644 index c168c8f4eb..0000000000 --- a/src/declarative/qml/qdeclarativecomponent.cpp +++ /dev/null @@ -1,1351 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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. -** -** 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdeclarativecomponent.h" -#include "qdeclarativecomponent_p.h" -#include "qdeclarativecomponentattached_p.h" - -#include "qdeclarativecompiler_p.h" -#include "qdeclarativecontext_p.h" -#include "qdeclarativeengine_p.h" -#include "qdeclarativevme_p.h" -#include "qdeclarative.h" -#include "qdeclarativeengine.h" -#include "qdeclarativebinding_p.h" -#include "qdeclarativebinding_p_p.h" -#include "qdeclarativeglobal_p.h" -#include "qdeclarativescript_p.h" -#include <private/qdeclarativeprofilerservice_p.h> -#include <private/qdeclarativeenginedebugservice_p.h> -#include "qdeclarativeincubator.h" -#include "qdeclarativeincubator_p.h" - -#include <private/qv8engine_p.h> -#include <private/qv8include_p.h> - -#include <QStack> -#include <QStringList> -#include <QtCore/qdebug.h> -#include <qdeclarativeinfo.h> - -QT_BEGIN_NAMESPACE - -class QDeclarativeComponentExtension : public QV8Engine::Deletable -{ -public: - QDeclarativeComponentExtension(QV8Engine *); - virtual ~QDeclarativeComponentExtension(); - - v8::Persistent<v8::Function> incubationConstructor; - v8::Persistent<v8::Script> initialProperties; - v8::Persistent<v8::Function> forceCompletion; -}; -V8_DEFINE_EXTENSION(QDeclarativeComponentExtension, componentExtension); - -/* - Try to do what's necessary for a reasonable display of the type - name, but no more (just enough for the client to do more extensive cleanup). - - Should only be called when debugging is enabled. -*/ -static inline QString buildTypeNameForDebug(const QMetaObject *metaObject) -{ - static const QString qmlMarker(QLatin1String("_QML")); - static const QChar underscore(QLatin1Char('_')); - static const QChar asterisk(QLatin1Char('*')); - QDeclarativeType *type = QDeclarativeMetaType::qmlType(metaObject); - QString typeName = type ? type->qmlTypeName() : QString::fromUtf8(metaObject->className()); - if (!type) { - //### optimize further? - int marker = typeName.indexOf(qmlMarker); - if (marker != -1 && marker < typeName.count() - 1) { - if (typeName[marker + 1] == underscore) { - const QString className = typeName.left(marker) + asterisk; - type = QDeclarativeMetaType::qmlType(QMetaType::type(className.toUtf8())); - if (type) - typeName = type->qmlTypeName(); - } - } - } - return typeName; -} - -/*! - \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 2.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, QObject *parent) - : QObject(*(new QDeclarativeComponentPrivate), parent) -{ - Q_D(QDeclarativeComponent); - d->engine = engine; - d->cc = cc; - cc->addref(); - d->start = start; - 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) -{ -} - -/*! - 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 publicContext 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 *publicContext) -{ - Q_D(QDeclarativeComponent); - - Q_ASSERT(publicContext); - QDeclarativeContextData *context = QDeclarativeContextData::get(publicContext); - - return d->beginCreate(context); -} - -QObject * -QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context) -{ - 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; - } - - QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); - - bool isRoot = enginePriv->inProgressCreations == 0; - enginePriv->inProgressCreations++; - state.errors.clear(); - state.completePending = true; - - if (isRoot) - QDeclarativeProfilerService::startRange(QDeclarativeProfilerService::Creating); - - enginePriv->referenceScarceResources(); - state.vme.init(context, cc, start, creationContext); - QObject *rv = state.vme.execute(&state.errors); - enginePriv->dereferenceScarceResources(); - - if (rv) { - QDeclarativeData *ddata = QDeclarativeData::get(rv); - Q_ASSERT(ddata); - ddata->indestructible = true; - } - - if (enginePriv->isDebugging && rv) { - if (!context->isInternal) - context->asQDeclarativeContextPrivate()->instances.append(rv); - QDeclarativeEngineDebugService::instance()->objectCreated(engine, rv); - if (isRoot) { - QDeclarativeProfilerService::rangeData(QDeclarativeProfilerService::Creating, - buildTypeNameForDebug(rv->metaObject())); - QDeclarativeData *data = QDeclarativeData::get(rv); - Q_ASSERT(data); - QDeclarativeProfilerService::rangeLocation(QDeclarativeProfilerService::Creating, - cc->url, data->lineNumber, data->columnNumber); - } - } - - return rv; -} - -void QDeclarativeComponentPrivate::beginDeferred(QDeclarativeEnginePrivate *enginePriv, - QObject *object, ConstructionState *state) -{ - enginePriv->inProgressCreations++; - state->errors.clear(); - state->completePending = true; - - state->vme.initDeferred(object); - state->vme.execute(&state->errors); -} - -void QDeclarativeComponentPrivate::complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state) -{ - if (state->completePending) { - state->vme.complete(); - - 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); - - QDeclarativeProfilerService::endRange(QDeclarativeProfilerService::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)->activeVME) { // XXX should only be allowed during begin - QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); - a->add(&p->activeVME->componentAttached); - } else { - QDeclarativeData *d = QDeclarativeData::get(obj); - Q_ASSERT(d); - Q_ASSERT(d->context); - a->add(&d->context->componentAttached); - } - - return a; -} - -void QDeclarativeComponent::create(QDeclarativeIncubator &i, QDeclarativeContext *context, - QDeclarativeContext *forContext) -{ - Q_D(QDeclarativeComponent); - - if (!context) - context = d->engine->rootContext(); - - QDeclarativeContextData *contextData = QDeclarativeContextData::get(context); - QDeclarativeContextData *forContextData = contextData; - if (forContext) forContextData = QDeclarativeContextData::get(forContext); - - if (!contextData->isValid()) { - qWarning("QDeclarativeComponent: Cannot create a component in an invalid context"); - return; - } - - if (contextData->engine != d->engine) { - qWarning("QDeclarativeComponent: Must create component in context from the same QDeclarativeEngine"); - return; - } - - if (!isReady()) { - qWarning("QDeclarativeComponent: Component is not ready"); - return; - } - - i.clear(); - QDeclarativeIncubatorPrivate *p = i.d; - - QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(d->engine); - - p->component = d->cc; p->component->addref(); - p->vme.init(contextData, d->cc, d->start, d->creationContext); - - enginePriv->incubate(i, forContextData); -} - -class QV8IncubatorResource : public QV8ObjectResource, - public QDeclarativeIncubator -{ -V8_RESOURCE_TYPE(IncubatorType) -public: - QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous); - - static v8::Handle<v8::Value> StatusChangedGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info); - static v8::Handle<v8::Value> StatusGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info); - static v8::Handle<v8::Value> ObjectGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info); - static v8::Handle<v8::Value> ForceCompletionGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info); - static v8::Handle<v8::Value> ForceCompletion(const v8::Arguments &args); - - static void StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, - const v8::AccessorInfo& info); - - void dispose(); - - v8::Persistent<v8::Object> me; - QDeclarativeGuard<QObject> parent; - v8::Persistent<v8::Value> valuemap; - v8::Persistent<v8::Object> qmlGlobal; -protected: - virtual void statusChanged(Status); - virtual void setInitialState(QObject *); -}; - -static void QDeclarativeComponent_setQmlParent(QObject *me, QObject *parent) -{ - if (parent) { - me->setParent(parent); - typedef QDeclarativePrivate::AutoParentFunction APF; - QList<APF> functions = QDeclarativeMetaType::parentFunctions(); - - bool needParent = false; - for (int ii = 0; ii < functions.count(); ++ii) { - QDeclarativePrivate::AutoParentResult res = functions.at(ii)(me, parent); - 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."); - } -} - -/*! - \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. -*/ -void QDeclarativeComponent::createObject(QDeclarativeV8Function *args) -{ - Q_D(QDeclarativeComponent); - Q_ASSERT(d->engine); - Q_ASSERT(args); - - QObject *parent = 0; - v8::Local<v8::Object> valuemap; - - if (args->Length() >= 1) - parent = args->engine()->toQObject((*args)[0]); - - if (args->Length() >= 2) { - v8::Local<v8::Value> v = (*args)[1]; - if (!v->IsObject() || v->IsArray()) { - qmlInfo(this) << tr("createObject: value is not an object"); - args->returnValue(v8::Null()); - return; - } - valuemap = v8::Local<v8::Object>::Cast(v); - } - - QV8Engine *v8engine = args->engine(); - - QDeclarativeContext *ctxt = creationContext(); - if (!ctxt) ctxt = d->engine->rootContext(); - - QObject *rv = beginCreate(ctxt); - - if (!rv) { - args->returnValue(v8::Null()); - return; - } - - QDeclarativeComponent_setQmlParent(rv, parent); - - v8::Handle<v8::Value> ov = v8engine->newQObject(rv); - Q_ASSERT(ov->IsObject()); - v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov); - - if (!valuemap.IsEmpty()) { - QDeclarativeComponentExtension *e = componentExtension(v8engine); - // Try catch isn't needed as the function itself is loaded with try/catch - v8::Handle<v8::Value> function = e->initialProperties->Run(args->qmlGlobal()); - v8::Handle<v8::Value> args[] = { object, valuemap }; - v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args); - } - - d->completeCreate(); - - Q_ASSERT(QDeclarativeData::get(rv)); - QDeclarativeData::get(rv)->setImplicitDestructible(); - - if (!rv) - args->returnValue(v8::Null()); - else - args->returnValue(object); -} - -/*! - \qmlmethod object Component::incubateObject(Item parent, object properties, enum mode) - - Creates an incubator for instance of this component. Incubators allow new component - instances to be instantiated asynchronously and not cause freezes in the UI. - - The \a parent argument specifies the parent the created instance will have. Omitting the - parameter or passing null will create anobject with no parent. In this case, a reference - to the created object must be maintained by the application of the object will eventually - be garbage collected. - - The \a properties argument is specified as a map of property-value items which will be - set on the created object during its construction. \a mode may be Qt.Synchronous or - Qt.Asynchronous and controls whether the instance is created synchronously or asynchronously. - The default is asynchronously. In some circumstances, even if Qt.Synchronous is specified, - the incubator may create the object asynchronously. This happens if the component calling - incubateObject() is itself being created asynchronously. - - All three arguments are optional. - - If successful, the method returns an incubator, otherwise null. The incubator has the following - properties: - - \list - \i status The status of the incubator. Valid values are Component.Ready, Component.Loading and - Component.Error. - \i object The created object instance. Will only be available once the incubator is in the - Ready status. - \i onStatusChanged Specifies a callback function to be invoked when the status changes. The - status is passed as a parameter to the callback. - \i forceCompletion() Call to complete incubation synchronously. - \endlist - - The following example demonstrates how to use an incubator: - - \js - var component = Qt.createComponent("Button.qml"); - - var incubator = component.incubateObject(parent, { x: 10, y: 10 }); - if (incubator.status != Component.Ready) { - incubator.onStatusChanged = function(status) { - if (status == Component.Ready) { - print ("Object", incubator.object, "is now ready!"); - } - } - } else { - print ("Object", incubator.object, "is ready immediately!"); - } - \endjs -*/ - -void QDeclarativeComponent::incubateObject(QDeclarativeV8Function *args) -{ - Q_D(QDeclarativeComponent); - Q_ASSERT(d->engine); - Q_UNUSED(d); - Q_ASSERT(args); - - QObject *parent = 0; - v8::Local<v8::Object> valuemap; - QDeclarativeIncubator::IncubationMode mode = QDeclarativeIncubator::Asynchronous; - - if (args->Length() >= 1) - parent = args->engine()->toQObject((*args)[0]); - - if (args->Length() >= 2) { - v8::Local<v8::Value> v = (*args)[1]; - if (v->IsNull()) { - } else if (!v->IsObject() || v->IsArray()) { - qmlInfo(this) << tr("createObject: value is not an object"); - args->returnValue(v8::Null()); - return; - } else { - valuemap = v8::Local<v8::Object>::Cast(v); - } - } - - if (args->Length() >= 3) { - quint32 v = (*args)[2]->Uint32Value(); - if (v == 0) - mode = QDeclarativeIncubator::Asynchronous; - else if (v == 1) - mode = QDeclarativeIncubator::AsynchronousIfNested; - } - - QDeclarativeComponentExtension *e = componentExtension(args->engine()); - - QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode); - v8::Local<v8::Object> o = e->incubationConstructor->NewInstance(); - o->SetExternalResource(r); - - if (!valuemap.IsEmpty()) { - r->valuemap = qPersistentNew(valuemap); - r->qmlGlobal = qPersistentNew(args->qmlGlobal()); - } - r->parent = parent; - r->me = qPersistentNew(o); - - create(*r, creationContext()); - - if (r->status() == QDeclarativeIncubator::Null) { - r->dispose(); - args->returnValue(v8::Null()); - } else { - args->returnValue(o); - } -} - -// XXX used by QSGLoader -void QDeclarativeComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate) -{ - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - QV8Engine *v8engine = ep->v8engine(); - - v8::HandleScope handle_scope; - v8::Context::Scope scope(v8engine->context()); - v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate); - Q_ASSERT(ov->IsObject()); - v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov); - - if (!valuemap.IsEmpty()) { - QDeclarativeComponentExtension *e = componentExtension(v8engine); - // Try catch isn't needed as the function itself is loaded with try/catch - v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal); - v8::Handle<v8::Value> args[] = { object, valuemap }; - v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args); - } - - QDeclarativeData *ddata = QDeclarativeData::get(toCreate); - Q_ASSERT(ddata); - ddata->setImplicitDestructible(); -} - - -QDeclarativeComponentExtension::QDeclarativeComponentExtension(QV8Engine *engine) -{ - v8::HandleScope handle_scope; - v8::Context::Scope scope(engine->context()); - - forceCompletion = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceCompletion, engine)); - - { - v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); - ft->InstanceTemplate()->SetHasExternalResource(true); - ft->InstanceTemplate()->SetInternalFieldCount(1); - ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"), - QV8IncubatorResource::StatusChangedGetter, - QV8IncubatorResource::StatusChangedSetter); - ft->InstanceTemplate()->SetAccessor(v8::String::New("status"), - QV8IncubatorResource::StatusGetter); - ft->InstanceTemplate()->SetAccessor(v8::String::New("object"), - QV8IncubatorResource::ObjectGetter); - ft->InstanceTemplate()->SetAccessor(v8::String::New("forceCompletion"), - QV8IncubatorResource::ForceCompletionGetter); - incubationConstructor = qPersistentNew(ft->GetFunction()); - } - - { -#define INITIALPROPERTIES_SOURCE \ - "(function(object, values) {"\ - "try {"\ - "for(var property in values) {" \ - "try {"\ - "var properties = property.split(\".\");"\ - "var o = object;"\ - "for (var ii = 0; ii < properties.length - 1; ++ii) {"\ - "o = o[properties[ii]];"\ - "}"\ - "o[properties[properties.length - 1]] = values[property];"\ - "} catch(e) {}"\ - "}"\ - "} catch(e) {}"\ - "})" - initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE))); -#undef INITIALPROPERTIES_SOURCE - } -} - -v8::Handle<v8::Value> QV8IncubatorResource::ObjectGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info) -{ - QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This()); - return r->engine->newQObject(r->object()); -} - -v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletionGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info) -{ - QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This()); - return componentExtension(r->engine)->forceCompletion; -} - -v8::Handle<v8::Value> QV8IncubatorResource::ForceCompletion(const v8::Arguments &args) -{ - QV8IncubatorResource *r = v8_resource_cast<QV8IncubatorResource>(args.This()); - if (!r) - V8THROW_TYPE("Not an incubator object"); - - r->forceCompletion(); - - return v8::Undefined(); -} - -v8::Handle<v8::Value> QV8IncubatorResource::StatusGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info) -{ - QV8IncubatorResource *r = v8_resource_check<QV8IncubatorResource>(info.This()); - return v8::Integer::NewFromUnsigned(r->status()); -} - -v8::Handle<v8::Value> QV8IncubatorResource::StatusChangedGetter(v8::Local<v8::String>, - const v8::AccessorInfo& info) -{ - return info.This()->GetInternalField(0); -} - -void QV8IncubatorResource::StatusChangedSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, - const v8::AccessorInfo& info) -{ - info.This()->SetInternalField(0, value); -} - -QDeclarativeComponentExtension::~QDeclarativeComponentExtension() -{ - qPersistentDispose(incubationConstructor); - qPersistentDispose(initialProperties); - qPersistentDispose(forceCompletion); -} - -QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m) -: QV8ObjectResource(engine), QDeclarativeIncubator(m) -{ -} - -void QV8IncubatorResource::setInitialState(QObject *o) -{ - QDeclarativeComponent_setQmlParent(o, parent); - - if (!valuemap.IsEmpty()) { - QDeclarativeComponentExtension *e = componentExtension(engine); - - v8::HandleScope handle_scope; - v8::Context::Scope scope(engine->context()); - - v8::Handle<v8::Value> function = e->initialProperties->Run(qmlGlobal); - v8::Handle<v8::Value> args[] = { engine->newQObject(o), valuemap }; - v8::Handle<v8::Function>::Cast(function)->Call(engine->global(), 2, args); - - qPersistentDispose(valuemap); - qPersistentDispose(qmlGlobal); - } -} - -void QV8IncubatorResource::dispose() -{ - qPersistentDispose(valuemap); - qPersistentDispose(qmlGlobal); - // No further status changes are forthcoming, so we no long need a self reference - qPersistentDispose(me); -} - -void QV8IncubatorResource::statusChanged(Status s) -{ - if (s == Ready) { - Q_ASSERT(QDeclarativeData::get(object())); - QDeclarativeData::get(object())->setImplicitDestructible(); - } - - if (!me.IsEmpty()) { // Will be false in synchronous mode - v8::HandleScope scope; - v8::Local<v8::Value> callback = me->GetInternalField(0); - - if (!callback.IsEmpty() && !callback->IsUndefined()) { - - if (callback->IsFunction()) { - v8::Context::Scope context_scope(engine->context()); - v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(callback); - v8::Handle<v8::Value> args[] = { v8::Integer::NewFromUnsigned(s) }; - v8::TryCatch tc; - f->Call(me, 1, args); - if (tc.HasCaught()) { - QDeclarativeError error; - QDeclarativeExpressionPrivate::exceptionToError(tc.Message(), error); - QDeclarativeEnginePrivate::warning(QDeclarativeEnginePrivate::get(engine->engine()), - error); - } - } - } - } - - if (s == Ready || s == Error) - dispose(); -} - -QT_END_NAMESPACE |