| /**************************************************************************** |
| ** |
| ** 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$ |
| ** 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 "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 |