diff options
Diffstat (limited to 'src/declarative/util')
20 files changed, 1801 insertions, 510 deletions
diff --git a/src/declarative/util/qdeclarativeanimation.cpp b/src/declarative/util/qdeclarativeanimation.cpp index 81c548b..461d626 100644 --- a/src/declarative/util/qdeclarativeanimation.cpp +++ b/src/declarative/util/qdeclarativeanimation.cpp @@ -132,7 +132,7 @@ QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QDeclarativeAbstrac using the \c start() and \c stop() methods. By default, animations are not running. Though, when the animations are assigned to properties, - as property value sources, they are set to running by default. + as property value sources using the \e on syntax, they are set to running by default. */ bool QDeclarativeAbstractAnimation::isRunning() const { @@ -1877,13 +1877,17 @@ void QDeclarativePropertyAnimation::setTo(const QVariant &t) } /*! - \qmlproperty QEasingCurve PropertyAnimation::easing - \brief the easing curve used for the transition. + \qmlproperty enum PropertyAnimation::easing.type + \qmlproperty real PropertyAnimation::easing.amplitude + \qmlproperty real PropertyAnimation::easing.overshoot + \qmlproperty real PropertyAnimation::easing.period + \brief the easing curve used for the animation. - For the easing you can specify the following parameters: type, amplitude, period and overshoot. + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot (more details provided after the table). \qml - PropertyAnimation { properties: "y"; easing.type: "InOutElastc"; easing.amplitude: 2.0; easing.period: 1.5 } + PropertyAnimation { properties: "y"; easing.type: "InOutElastic"; easing.amplitude: 2.0; easing.period: 1.5 } \endqml Available types are: @@ -2447,12 +2451,15 @@ QDeclarativeParentAnimation::QDeclarativeParentAnimation(QObject *parent) QDeclarative_setParent_noEvent(d->topLevelGroup, this); d->startAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup); d->topLevelGroup->addAnimation(d->startAction); d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup); d->topLevelGroup->addAnimation(d->ag); d->endAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup); d->topLevelGroup->addAnimation(d->endAction); } @@ -2743,4 +2750,99 @@ QAbstractAnimation *QDeclarativeParentAnimation::qtAnimation() return d->topLevelGroup; } +/*! + \qmlclass AnchorAnimation QDeclarativeAnchorAnimation + \since 4.7 + \inherits Animation + \brief The AnchorAnimation element allows you to animate anchor changes. + + AnchorAnimation will animated any changes specified by a state's AnchorChanges. + In the following snippet we animate the addition of a right anchor to our item. + \qml + Item { + id: myItem + width: 100 + } + ... + State { + AnchorChanges { + target: myItem + anchors.right: container.right + } + } + ... + Transition { + //smoothly reanchor myItem and move into new position + AnchorAnimation {} + } + \endqml + + \sa AnchorChanges +*/ + +QDeclarativeAnchorAnimation::QDeclarativeAnchorAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QDeclarativeAnchorAnimationPrivate), parent) +{ + Q_D(QDeclarativeAnchorAnimation); + d->va = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(d->va, this); +} + +QDeclarativeAnchorAnimation::~QDeclarativeAnchorAnimation() +{ +} + +QAbstractAnimation *QDeclarativeAnchorAnimation::qtAnimation() +{ + Q_D(QDeclarativeAnchorAnimation); + return d->va; +} + +/*! + \qmlproperty list<Item> AnchorAnimation::targets + The items to reanchor. + + If no targets are specified all AnchorChanges will be + animated by the AnchorAnimation. +*/ +QDeclarativeListProperty<QDeclarativeItem> QDeclarativeAnchorAnimation::targets() +{ + Q_D(QDeclarativeAnchorAnimation); + return QDeclarativeListProperty<QDeclarativeItem>(this, d->targets); +} + +void QDeclarativeAnchorAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarativeAnchorAnimation); + PropertyUpdater *data = new PropertyUpdater; + data->interpolatorType = QMetaType::QReal; + data->interpolator = d->interpolator; + + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = false; + + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.event && action.event->typeName() == QLatin1String("AnchorChanges") + && (d->targets.isEmpty() || d->targets.contains(static_cast<QDeclarativeAnchorChanges*>(action.event)->object()))) { + data->actions << static_cast<QDeclarativeAnchorChanges*>(action.event)->additionalActions(); + } + } + + if (data->actions.count()) { + if (!d->rangeIsSet) { + d->va->setStartValue(qreal(0)); + d->va->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + d->va->setFromSourcedValue(&data->fromSourced); + } else { + delete data; + } +} + QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeanimation_p.h b/src/declarative/util/qdeclarativeanimation_p.h index af48309..eb339f6 100644 --- a/src/declarative/util/qdeclarativeanimation_p.h +++ b/src/declarative/util/qdeclarativeanimation_p.h @@ -480,6 +480,26 @@ protected: virtual QAbstractAnimation *qtAnimation(); }; +class QDeclarativeAnchorAnimationPrivate; +class QDeclarativeAnchorAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarativeAnchorAnimation) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeItem> targets READ targets) + +public: + QDeclarativeAnchorAnimation(QObject *parent=0); + virtual ~QDeclarativeAnchorAnimation(); + + QDeclarativeListProperty<QDeclarativeItem> targets(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeAbstractAnimation) @@ -495,6 +515,7 @@ QML_DECLARE_TYPE(QDeclarativeParallelAnimation) QML_DECLARE_TYPE(QDeclarativeVector3dAnimation) QML_DECLARE_TYPE(QDeclarativeRotationAnimation) QML_DECLARE_TYPE(QDeclarativeParentAnimation) +QML_DECLARE_TYPE(QDeclarativeAnchorAnimation) QT_END_HEADER diff --git a/src/declarative/util/qdeclarativeanimation_p_p.h b/src/declarative/util/qdeclarativeanimation_p_p.h index ae82a90..0460312 100644 --- a/src/declarative/util/qdeclarativeanimation_p_p.h +++ b/src/declarative/util/qdeclarativeanimation_p_p.h @@ -393,6 +393,19 @@ public: QPointF computeTransformOrigin(QDeclarativeItem::TransformOrigin origin, qreal width, qreal height) const; }; +class QDeclarativeAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeAnchorAnimation) +public: + QDeclarativeAnchorAnimationPrivate() : rangeIsSet(false), va(0), + interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {} + + bool rangeIsSet; + QDeclarativeBulkValueAnimator *va; + QVariantAnimation::Interpolator interpolator; + QList<QDeclarativeItem*> targets; +}; + QT_END_NAMESPACE #endif // QDECLARATIVEANIMATION_P_H diff --git a/src/declarative/util/qdeclarativefontloader.cpp b/src/declarative/util/qdeclarativefontloader.cpp index 8f5f537..bf98d40 100644 --- a/src/declarative/util/qdeclarativefontloader.cpp +++ b/src/declarative/util/qdeclarativefontloader.cpp @@ -61,7 +61,7 @@ class QDeclarativeFontLoaderPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QDeclarativeFontLoader) public: - QDeclarativeFontLoaderPrivate() : reply(0), status(QDeclarativeFontLoader::Null) {} + QDeclarativeFontLoaderPrivate() : reply(0), status(QDeclarativeFontLoader::Null), redirectCount(0) {} void addFontToDatabase(const QByteArray &); @@ -69,6 +69,7 @@ public: QString name; QNetworkReply *reply; QDeclarativeFontLoader::Status status; + int redirectCount; }; @@ -206,15 +207,31 @@ QDeclarativeFontLoader::Status QDeclarativeFontLoader::status() const return d->status; } +#define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16 + void QDeclarativeFontLoader::replyFinished() { Q_D(QDeclarativeFontLoader); if (d->reply) { + d->redirectCount++; + if (d->redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->reply->url().resolved(redirect.toUrl()); + d->reply->deleteLater(); + d->reply = 0; + setSource(url); + return; + } + } + d->redirectCount=0; + if (!d->reply->error()) { QByteArray ba = d->reply->readAll(); d->addFontToDatabase(ba); } else { d->status = Error; + qWarning() << "Cannot load font:" << d->reply->url(); emit statusChanged(); } d->reply->deleteLater(); diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp index b968ca5..5b0a7ea 100644 --- a/src/declarative/util/qdeclarativelistmodel.cpp +++ b/src/declarative/util/qdeclarativelistmodel.cpp @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#include "qdeclarativelistmodel_p.h" - +#include "qdeclarativelistmodel_p_p.h" +#include "qdeclarativelistmodelworkeragent_p.h" #include "qdeclarativeopenmetaobject_p.h" #include <qdeclarativecustomparser_p.h> @@ -66,8 +66,6 @@ QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListM return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); } -static void dump(ModelNode *node, int ind); - /*! \qmlclass ListModel QDeclarativeListModel \since 4.7 @@ -96,9 +94,12 @@ static void dump(ModelNode *node, int ind); } \endcode - Roles (properties) must begin with a lower-case letter. The above example defines a + Roles (properties) must begin with a lower-case letter.The above example defines a ListModel containing three elements, with the roles "name" and "cost". + Values must be simple constants - either strings (quoted), bools (true, false), numbers, + or enum values (like Text.AlignHCenter). + The defined model can be used in views such as ListView: \code Component { @@ -169,6 +170,8 @@ static void dump(ModelNode *node, int ind); } \endcode + \section2 Modifying list models + The content of a ListModel may be created and modified using the clear(), append(), and set() methods. For example: @@ -193,252 +196,146 @@ static void dump(ModelNode *node, int ind); except by first clearing the model - whatever properties are first added are then the only permitted properties in the model. - \sa {qmlmodels}{Data Models} -*/ -class ModelObject : public QObject -{ - Q_OBJECT -public: - ModelObject(); + \section2 Using threaded list models with WorkerScript - void setValue(const QByteArray &name, const QVariant &val) - { - _mo->setValue(name, val); - } + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. -private: - QDeclarativeOpenMetaObject *_mo; -}; + Here is an example that uses WorkerScript to periodically append the + current time to a list model: -struct ModelNode -{ - ModelNode(); - ~ModelNode(); + \snippet examples/declarative/listmodel-threaded/timedisplay.qml 0 - QList<QVariant> values; - QHash<QString, ModelNode *> properties; + The included file, \tt dataloader.js, looks like this: - QDeclarativeListModel *model(const QDeclarativeListModel *parent) { - if (!modelCache) { - modelCache = new QDeclarativeListModel; - QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(parent)); + \snippet examples/declarative/listmodel-threaded/dataloader.js 0 - modelCache->_root = this; - } - return modelCache; - } + The application's \tt Timer object periodically sends a message to the + worker script by calling \tt WorkerScript::sendMessage(). When this message + is received, \tt WorkerScript.onMessage() is invoked in + \tt dataloader.js, which appends the current time to the list model. - ModelObject *object(const QDeclarativeListModel *parent) { - if (!objectCache) { - objectCache = new ModelObject(); - QHash<QString, ModelNode *>::iterator it; - for (it = properties.begin(); it != properties.end(); ++it) { - objectCache->setValue(it.key().toUtf8(), parent->valueForNode(*it)); - } - } - return objectCache; - } + Note the call to sync() from the \c WorkerScript.onMessage() handler. + Without this call, the changes made to the list are not reflected in the + list model in the main thread. - void setObjectValue(const QScriptValue& valuemap); - void setListValue(const QScriptValue& valuelist); + \section3 Limitations - void setProperty(const QString& prop, const QVariant& val) { - QHash<QString, ModelNode *>::const_iterator it = properties.find(prop); - if (it != properties.end()) { - (*it)->values[0] = val; - } else { - ModelNode *n = new ModelNode; - n->values << val; - properties.insert(prop,n); - } - if (objectCache) - objectCache->setValue(prop.toUtf8(), val); - } - - QDeclarativeListModel *modelCache; - ModelObject *objectCache; - bool isArray; -}; - -QT_END_NAMESPACE + If a list model is to be accessed from a WorkerScript, it \bold cannot + contain nested list data. So, the following model cannot be used from a WorkerScript + because of the list contained in the "attributes" property: -Q_DECLARE_METATYPE(ModelNode *) - -QT_BEGIN_NAMESPACE - -void ModelNode::setObjectValue(const QScriptValue& valuemap) { - QScriptValueIterator it(valuemap); - while (it.hasNext()) { - it.next(); - ModelNode *value = new ModelNode; - QScriptValue v = it.value(); - if (v.isArray()) { - value->isArray = true; - value->setListValue(v); - } else { - value->values << v.toVariant(); + \code + ListModel { + id: fruitModel + ListElement { + name: "Apple" + cost: 2.45 + attributes: [ + ListElement { description: "Core" }, + ListElement { description: "Deciduous" } + ] } - properties.insert(it.name(),value); } -} + \endcode -void ModelNode::setListValue(const QScriptValue& valuelist) { - int size = valuelist.property(QString::fromLatin1("length")).toInt32(); - values.clear(); - for (int i = 0; i < size; ++i) { - ModelNode *value = new ModelNode; - QScriptValue v = valuelist.property(i); - if (v.isArray()) { - value->isArray = true; - value->setListValue(v); - } else if (v.isObject()) { - value->setObjectValue(v); - } else { - value->values << v.toVariant(); - } - values.append(qVariantFromValue(value)); + In addition, the WorkerScript cannot add any nested list data to the model. - } -} + \sa {qmlmodels}{Data Models}, WorkerScript +*/ -ModelObject::ModelObject() -: _mo(new QDeclarativeOpenMetaObject(this)) +QDeclarativeListModel::QDeclarativeListModel(QObject *parent) +: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0), m_isWorkerCopy(false) { } -QDeclarativeListModel::QDeclarativeListModel(QObject *parent) -: QListModelInterface(parent), _rolesOk(false), _root(0) +QDeclarativeListModel::QDeclarativeListModel(bool workerCopy, QObject *parent) +: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0), m_isWorkerCopy(workerCopy) { + if (workerCopy) + m_flat = new FlatListModel(this); + else + m_nested = new NestedListModel(this); } QDeclarativeListModel::~QDeclarativeListModel() { - delete _root; + delete m_nested; + delete m_flat; } -void QDeclarativeListModel::checkRoles() const +bool QDeclarativeListModel::flatten() { - if (_rolesOk || !_root) - return; + if (m_flat) + return true; - for (int ii = 0; ii < _root->values.count(); ++ii) { - ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(ii)); - if (node) { - foreach (const QString &role, node->properties.keys()) - addRole(role); - } + QList<int> roles = m_nested->roles(); + + QList<QHash<int, QVariant> > values; + bool hasNested = false; + for (int i=0; i<m_nested->count(); i++) { + values.append(m_nested->data(i, roles, &hasNested)); + if (hasNested) + return false; } - _rolesOk = true; + FlatListModel *flat = new FlatListModel(this); + flat->m_values = values; + + for (int i=0; i<roles.count(); i++) { + QString s = m_nested->toString(roles[i]); + flat->m_roles.insert(roles[i], s); + flat->m_strings.insert(s, roles[i]); + } + + m_flat = flat; + delete m_nested; + m_nested = 0; + return true; } -void QDeclarativeListModel::addRole(const QString &role) const +QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent() { - if (!roleStrings.contains(role)) - roleStrings << role; + if (m_agent) + return m_agent; + + if (!flatten()) { + qmlInfo(this) << "List contains nested list values and cannot be used from a worker script"; + return 0; + } + + m_agent = new QDeclarativeListModelWorkerAgent(this); + return m_agent; } QList<int> QDeclarativeListModel::roles() const { - checkRoles(); - QList<int> rv; - for (int ii = 0; ii < roleStrings.count(); ++ii) - rv << ii; - return rv; + return m_flat ? m_flat->roles() : m_nested->roles(); } QString QDeclarativeListModel::toString(int role) const { - checkRoles(); - if (role < roleStrings.count()) - return roleStrings.at(role); - else - return QString(); -} - -QVariant QDeclarativeListModel::valueForNode(ModelNode *node) const -{ - QObject *rv = 0; - - if (node->isArray) { - // List - rv = node->model(this); - } else { - if (!node->properties.isEmpty()) { - // Object - rv = node->object(this); - } else if (node->values.count() == 0) { - // Invalid - return QVariant(); - } else if (node->values.count() == 1) { - // Value - QVariant &var = node->values[0]; - ModelNode *valueNode = qvariant_cast<ModelNode *>(var); - if (valueNode) { - if (!valueNode->properties.isEmpty()) - rv = valueNode->object(this); - else - rv = valueNode->model(this); - } else { - return var; - } - } - } - - if (rv) - return QVariant::fromValue(rv); - else - return QVariant(); + return m_flat ? m_flat->toString(role) : m_nested->toString(role); } QHash<int,QVariant> QDeclarativeListModel::data(int index, const QList<int> &roles) const { - checkRoles(); - QHash<int, QVariant> rv; if (index >= count() || index < 0) - return rv; - - ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); - if (!node) - return rv; - - for (int ii = 0; ii < roles.count(); ++ii) { - const QString &roleString = roleStrings.at(roles.at(ii)); - - QHash<QString, ModelNode *>::ConstIterator iter = - node->properties.find(roleString); - if (iter != node->properties.end()) { - ModelNode *row = *iter; - rv.insert(roles.at(ii), valueForNode(row)); - } - } + return QHash<int, QVariant>(); - return rv; + return m_flat ? m_flat->data(index, roles) : m_nested->data(index, roles); } QVariant QDeclarativeListModel::data(int index, int role) const { - checkRoles(); - QVariant rv; if (index >= count() || index < 0) - return rv; - - ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); - if (!node) - return rv; - - const QString &roleString = roleStrings.at(role); - - QHash<QString, ModelNode *>::ConstIterator iter = - node->properties.find(roleString); - if (iter != node->properties.end()) { - ModelNode *row = *iter; - rv = valueForNode(row); - } + return QVariant(); - return rv; + return m_flat ? m_flat->data(index, role) : m_nested->data(index, role); } /*! @@ -447,8 +344,7 @@ QVariant QDeclarativeListModel::data(int index, int role) const */ int QDeclarativeListModel::count() const { - if (!_root) return 0; - return _root->values.count(); + return m_flat ? m_flat->count() : m_nested->count(); } /*! @@ -462,12 +358,15 @@ int QDeclarativeListModel::count() const void QDeclarativeListModel::clear() { int cleared = count(); - _rolesOk = false; - delete _root; - _root = 0; - roleStrings.clear(); - emit itemsRemoved(0,cleared); - emit countChanged(0); + if (m_flat) + m_flat->clear(); + else + m_nested->clear(); + + if (!m_isWorkerCopy) { + emit itemsRemoved(0, cleared); + emit countChanged(0); + } } /*! @@ -479,17 +378,20 @@ void QDeclarativeListModel::clear() */ void QDeclarativeListModel::remove(int index) { - if (!_root || index < 0 || index >= _root->values.count()) { + if (index < 0 || index >= count()) { qmlInfo(this) << tr("remove: index %1 out of range").arg(index); return; } - ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); - _root->values.removeAt(index); - if (node) - delete node; - emit itemsRemoved(index,1); - emit countChanged(_root->values.count()); + if (m_flat) + m_flat->remove(index); + else + m_nested->remove(index); + + if (!m_isWorkerCopy) { + emit itemsRemoved(index, 1); + emit countChanged(this->count()); + } } /*! @@ -513,20 +415,17 @@ void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap) qmlInfo(this) << tr("insert: value is not an object"); return; } - if (!_root) - _root = new ModelNode; - if (index >= _root->values.count() || index<0) { - if (index == _root->values.count()) - append(valuemap); - else - qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); return; } - ModelNode *mn = new ModelNode; - mn->setObjectValue(valuemap); - _root->values.insert(index,qVariantFromValue(mn)); - emit itemsInserted(index,1); - emit countChanged(_root->values.count()); + + bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap); + if (ok && !m_isWorkerCopy) { + emit itemsInserted(index, 1); + emit countChanged(this->count()); + } } /*! @@ -551,9 +450,10 @@ void QDeclarativeListModel::move(int from, int to, int n) qmlInfo(this) << tr("move: out of range"); return; } - int origfrom=from; // preserve actual move, so any animations are correct - int origto=to; - int orign=n; + + int origfrom = from; + int origto = to; + int orign = n; if (from > to) { // Only move forwards - flip if backwards moving int tfrom = from; @@ -562,24 +462,14 @@ void QDeclarativeListModel::move(int from, int to, int n) to = tto+n; n = tfrom-tto; } - if (n==1) { - _root->values.move(from,to); - } else { - QList<QVariant> replaced; - int i=0; - QVariantList::const_iterator it=_root->values.begin(); it += from+n; - for (; i<to-from; ++i,++it) - replaced.append(*it); - i=0; - it=_root->values.begin(); it += from; - for (; i<n; ++i,++it) - replaced.append(*it); - QVariantList::const_iterator f=replaced.begin(); - QVariantList::iterator t=_root->values.begin(); t += from; - for (; f != replaced.end(); ++f, ++t) - *t = *f; - } - emit itemsMoved(origfrom,origto,orign); + + if (m_flat) + m_flat->move(from, to, n); + else + m_nested->move(from, to, n); + + if (!m_isWorkerCopy) + emit itemsMoved(origfrom, origto, orign); } /*! @@ -600,13 +490,8 @@ void QDeclarativeListModel::append(const QScriptValue& valuemap) qmlInfo(this) << tr("append: value is not an object"); return; } - if (!_root) - _root = new ModelNode; - ModelNode *mn = new ModelNode; - mn->setObjectValue(valuemap); - _root->values << qVariantFromValue(mn); - emit itemsInserted(count()-1,1); - emit countChanged(_root->values.count()); + + insert(count(), valuemap); } /*! @@ -640,15 +525,7 @@ QScriptValue QDeclarativeListModel::get(int index) const return 0; } - ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); - if (!node) - return 0; - QDeclarativeEngine *eng = qmlEngine(this); - if (!eng) { - qWarning("Cannot call QDeclarativeListModel::get() without a QDeclarativeEngine"); - return 0; - } - return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng); + return m_flat ? m_flat->get(index) : m_nested->get(index); } /*! @@ -672,27 +549,22 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) qmlInfo(this) << tr("set: value is not an object"); return; } - if ( !_root || index > _root->values.count() || index < 0) { + if (count() == 0 || index > count() || index < 0) { qmlInfo(this) << tr("set: index %1 out of range").arg(index); return; } - if (index == _root->values.count()) + + if (index == count()) { append(valuemap); - else { - ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + } else { QList<int> roles; - node->setObjectValue(valuemap); - QScriptValueIterator it(valuemap); - while (it.hasNext()) { - it.next(); - int r = roleStrings.indexOf(it.name()); - if (r<0) { - r = roleStrings.count(); - roleStrings << it.name(); - } - roles.append(r); - } - emit itemsChanged(index,1,roles); + if (m_flat) + m_flat->set(index, valuemap, &roles); + else + m_nested->set(index, valuemap, &roles); + + if (!m_isWorkerCopy) + emit itemsChanged(index, 1, roles); } } @@ -711,22 +583,33 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) */ void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value) { - if ( !_root || index >= _root->values.count() || index < 0) { + if (count() == 0 || index >= count() || index < 0) { qmlInfo(this) << tr("set: index %1 out of range").arg(index); return; } - ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); - int r = roleStrings.indexOf(property); - if (r<0) { - r = roleStrings.count(); - roleStrings << property; - } + QList<int> roles; - roles.append(r); + if (m_flat) + m_flat->setProperty(index, property, value, &roles); + else + m_nested->setProperty(index, property, value, &roles); - if (node) - node->setProperty(property,value); - emit itemsChanged(index,1,roles); + if (!m_isWorkerCopy) + emit itemsChanged(index, 1, roles); +} + +/*! + \qmlmethod ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QDeclarativeListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; } bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data) @@ -736,7 +619,7 @@ bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParser const QVariant &value = values.at(ii); if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { - QDeclarativeCustomParserNode node = + QDeclarativeCustomParserNode node = qvariant_cast<QDeclarativeCustomParserNode>(value); { @@ -783,7 +666,7 @@ bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParser } else { - QDeclarativeParser::Variant variant = + QDeclarativeParser::Variant variant = qvariant_cast<QDeclarativeParser::Variant>(value); int ref = data.count(); @@ -798,10 +681,17 @@ bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParser d += char(variant.asBoolean()); } else if (variant.isScript()) { if (definesEmptyList(variant.asScript())) { - d[0] = 0; // QDeclarativeParser::Variant::Invalid - marks empty list + d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list } else { - error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value")); - return false; + QByteArray script = variant.asScript().toUtf8(); + int v = evaluateEnum(script); + if (v<0) { + error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value")); + return false; + } else { + d[0] = char(QDeclarativeParser::Variant::Number); + d += QByteArray::number(v); + } } } d.append('\0'); @@ -834,15 +724,15 @@ QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomPa } } - int size = sizeof(ListModelData) + - instr.count() * sizeof(ListInstruction) + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + data.count(); QByteArray rv; rv.resize(size); ListModelData *lmd = (ListModelData *)rv.data(); - lmd->dataOffset = sizeof(ListModelData) + + lmd->dataOffset = sizeof(ListModelData) + instr.count() * sizeof(ListInstruction); lmd->instrCount = instr.count(); for (int ii = 0; ii < instr.count(); ++ii) @@ -857,7 +747,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray & QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj); ModelNode *root = new ModelNode; - rv->_root = root; + rv->m_nested->_root = root; QStack<ModelNode *> nodes; nodes << root; @@ -942,7 +832,491 @@ bool QDeclarativeListModelParser::definesEmptyList(const QString &s) \sa ListModel */ -static void dump(ModelNode *node, int ind) +FlatListModel::FlatListModel(QDeclarativeListModel *base) + : m_scriptEngine(0), m_listModel(base) +{ +} + +FlatListModel::~FlatListModel() +{ +} + +QHash<int,QVariant> FlatListModel::data(int index, const QList<int> &roles) const +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash<int, QVariant> row; + for (int i=0; i<roles.count(); i++) { + int role = roles[i]; + if (m_values[index].contains(role)) + row.insert(role, m_values[index][role]); + } + return row; +} + +QVariant FlatListModel::data(int index, int role) const +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + if (m_values[index].contains(role)) + return m_values[index][role]; + return QVariant(); +} + +QList<int> FlatListModel::roles() const +{ + return m_roles.keys(); +} + +QString FlatListModel::toString(int role) const +{ + if (m_roles.contains(role)) + return m_roles[role]; + return QString(); +} + +int FlatListModel::count() const +{ + return m_values.count(); +} + +void FlatListModel::clear() +{ + m_values.clear(); +} + +void FlatListModel::remove(int index) +{ + m_values.removeAt(index); +} + +bool FlatListModel::append(const QScriptValue &value) +{ + return insert(m_values.count(), value); +} + +bool FlatListModel::insert(int index, const QScriptValue &value) +{ + Q_ASSERT(index >= 0 && index <= m_values.count()); + + QHash<int, QVariant> row; + if (!addValue(value, &row, 0)) + return false; + + m_values.insert(index, row); + return true; +} + +QScriptValue FlatListModel::get(int index) const +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel)); + + if (!scriptEngine) + return 0; + + QScriptValue rv = scriptEngine->newObject(); + + QHash<int, QVariant> row = m_values.at(index); + for (QHash<int, QVariant>::ConstIterator iter = row.begin(); iter != row.end(); ++iter) + rv.setProperty(m_roles.value(iter.key()), qScriptValueFromValue(scriptEngine, iter.value())); + + return rv; +} + +void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash<int, QVariant> row = m_values[index]; + if (addValue(value, &row, roles)) + m_values[index] = row; +} + +void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash<QString, int>::Iterator iter = m_strings.find(property); + int role; + if (iter == m_strings.end()) { + role = m_roles.count(); + m_roles.insert(role, property); + m_strings.insert(property, role); + } else { + role = iter.value(); + } + roles->append(role); + + m_values[index][role] = value; +} + +void FlatListModel::move(int from, int to, int n) +{ + if (n == 1) { + m_values.move(from, to); + } else { + QList<QHash<int, QVariant> > replaced; + int i=0; + QList<QHash<int, QVariant> >::ConstIterator it=m_values.begin(); it += from+n; + for (; i<to-from; ++i,++it) + replaced.append(*it); + i=0; + it=m_values.begin(); it += from; + for (; i<n; ++i,++it) + replaced.append(*it); + QList<QHash<int, QVariant> >::ConstIterator f=replaced.begin(); + QList<QHash<int, QVariant> >::Iterator t=m_values.begin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } +} + +bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles) +{ + QScriptValueIterator it(value); + while (it.hasNext()) { + it.next(); + if (it.value().isObject()) { + qmlInfo(m_listModel) << "Cannot add nested list values when modifying or after modification from a worker script"; + return false; + } + + QString name = it.name(); + QVariant v = it.value().toVariant(); + + QHash<QString, int>::Iterator iter = m_strings.find(name); + if (iter == m_strings.end()) { + int role = m_roles.count(); + m_roles.insert(role, name); + iter = m_strings.insert(name, role); + if (roles) + roles->append(role); + } + row->insert(*iter, v); + } + return true; +} + +NestedListModel::NestedListModel(QDeclarativeListModel *base) + : _root(0), m_listModel(base), _rolesOk(false) +{ +} + +NestedListModel::~NestedListModel() +{ + delete _root; +} + +QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const +{ + QObject *rv = 0; + if (hasNested) + *hasNested = false; + + if (node->isArray) { + // List + rv = node->model(this); + if (hasNested) + *hasNested = true; + } else { + if (!node->properties.isEmpty()) { + // Object + rv = node->object(this); + } else if (node->values.count() == 0) { + // Invalid + return QVariant(); + } else if (node->values.count() == 1) { + // Value + QVariant &var = node->values[0]; + ModelNode *valueNode = qvariant_cast<ModelNode *>(var); + if (valueNode) { + if (!valueNode->properties.isEmpty()) + rv = valueNode->object(this); + else + rv = valueNode->model(this); + } else { + return var; + } + } + } + + if (rv) { + return QVariant::fromValue(rv); + } else { + return QVariant(); + } +} + +QHash<int,QVariant> NestedListModel::data(int index, const QList<int> &roles, bool *hasNested) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QHash<int, QVariant> rv; + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return rv; + + for (int ii = 0; ii < roles.count(); ++ii) { + const QString &roleString = roleStrings.at(roles.at(ii)); + + QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv.insert(roles.at(ii), valueForNode(row, hasNested)); + } + } + + return rv; +} + +QVariant NestedListModel::data(int index, int role) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QVariant rv; + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return rv; + + const QString &roleString = roleStrings.at(role); + + QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv = valueForNode(row); + } + + return rv; +} + +int NestedListModel::count() const +{ + if (!_root) return 0; + return _root->values.count(); +} + +void NestedListModel::clear() +{ + _rolesOk = false; + roleStrings.clear(); + + delete _root; + _root = 0; +} + +void NestedListModel::remove(int index) +{ + if (!_root) + return; + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + _root->values.removeAt(index); + if (node) + delete node; +} + +bool NestedListModel::insert(int index, const QScriptValue& valuemap) +{ + if (!_root) + _root = new ModelNode; + + ModelNode *mn = new ModelNode; + mn->setObjectValue(valuemap); + _root->values.insert(index,qVariantFromValue(mn)); + return true; +} + +void NestedListModel::move(int from, int to, int n) +{ + if (n==1) { + _root->values.move(from,to); + } else { + QList<QVariant> replaced; + int i=0; + QVariantList::const_iterator it=_root->values.begin(); it += from+n; + for (; i<to-from; ++i,++it) + replaced.append(*it); + i=0; + it=_root->values.begin(); it += from; + for (; i<n; ++i,++it) + replaced.append(*it); + QVariantList::const_iterator f=replaced.begin(); + QVariantList::iterator t=_root->values.begin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } +} + +bool NestedListModel::append(const QScriptValue& valuemap) +{ + if (!_root) + _root = new ModelNode; + ModelNode *mn = new ModelNode; + mn->setObjectValue(valuemap); + _root->values << qVariantFromValue(mn); + return true; +} + +QScriptValue NestedListModel::get(int index) const +{ + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return 0; + QDeclarativeEngine *eng = qmlEngine(m_listModel); + if (!eng) { + qWarning("Cannot call QDeclarativeListModel::get() without a QDeclarativeEngine"); + return 0; + } + return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng); +} + +void NestedListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + node->setObjectValue(valuemap); + + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + int r = roleStrings.indexOf(it.name()); + if (r < 0) { + r = roleStrings.count(); + roleStrings << it.name(); + } + roles->append(r); + } +} + +void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + node->setProperty(property, value); + + int r = roleStrings.indexOf(property); + if (r < 0) { + r = roleStrings.count(); + roleStrings << property; + } + roles->append(r); +} + +void NestedListModel::checkRoles() const +{ + if (_rolesOk || !_root) + return; + + for (int i = 0; i<_root->values.count(); ++i) { + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i)); + if (node) { + foreach (const QString &role, node->properties.keys()) { + if (!roleStrings.contains(role)) + roleStrings.append(role); + } + } + } + + _rolesOk = true; +} + +QList<int> NestedListModel::roles() const +{ + checkRoles(); + QList<int> rv; + for (int ii = 0; ii < roleStrings.count(); ++ii) + rv << ii; + return rv; +} + +QString NestedListModel::toString(int role) const +{ + checkRoles(); + if (role < roleStrings.count()) + return roleStrings.at(role); + else + return QString(); +} + + +ModelNode::ModelNode() +: modelCache(0), objectCache(0), isArray(false) +{ +} + +ModelNode::~ModelNode() +{ + ModelNode *node; + + QList<ModelNode *> nodeValues = properties.values(); + for (int ii = 0; ii < nodeValues.count(); ++ii) { + node = nodeValues[ii]; + if (node) { delete node; node = 0; } + } + + for (int ii = 0; ii < values.count(); ++ii) { + node = qvariant_cast<ModelNode *>(values.at(ii)); + if (node) { delete node; node = 0; } + } + + if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; } + if (objectCache) { delete objectCache; objectCache = 0; } +} + +void ModelNode::setObjectValue(const QScriptValue& valuemap) { + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + ModelNode *value = new ModelNode; + QScriptValue v = it.value(); + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + } else { + value->values << v.toVariant(); + } + properties.insert(it.name(),value); + } +} + +void ModelNode::setListValue(const QScriptValue& valuelist) { + QScriptValueIterator it(valuelist); + values.clear(); + while (it.hasNext()) { + it.next(); + ModelNode *value = new ModelNode; + QScriptValue v = it.value(); + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + } else if (v.isObject()) { + value->setObjectValue(v); + } else { + value->values << v.toVariant(); + } + values.append(qVariantFromValue(value)); + + } +} + +void ModelNode::setProperty(const QString& prop, const QVariant& val) { + QHash<QString, ModelNode *>::const_iterator it = properties.find(prop); + if (it != properties.end()) { + (*it)->values[0] = val; + } else { + ModelNode *n = new ModelNode; + n->values << val; + properties.insert(prop,n); + } + if (objectCache) + objectCache->setValue(prop.toUtf8(), val); +} + +void ModelNode::dump(ModelNode *node, int ind) { QByteArray indentBa(ind * 4, ' '); const char *indent = indentBa.constData(); @@ -963,22 +1337,15 @@ static void dump(ModelNode *node, int ind) } } -ModelNode::ModelNode() -: modelCache(0), objectCache(0), isArray(false) +ModelObject::ModelObject() +: _mo(new QDeclarativeOpenMetaObject(this)) { } -ModelNode::~ModelNode() +void ModelObject::setValue(const QByteArray &name, const QVariant &val) { - qDeleteAll(properties); - for (int ii = 0; ii < values.count(); ++ii) { - ModelNode *node = qvariant_cast<ModelNode *>(values.at(ii)); - if (node) { delete node; node = 0; } - } - if (modelCache) { modelCache->_root = 0/* ==this */; delete modelCache; modelCache = 0; } - if (objectCache) { delete objectCache; } + _mo->setValue(name, val); } -QT_END_NAMESPACE -#include <qdeclarativelistmodel.moc> +QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativelistmodel_p.h b/src/declarative/util/qdeclarativelistmodel_p.h index 8eb6583..6a0426b 100644 --- a/src/declarative/util/qdeclarativelistmodel_p.h +++ b/src/declarative/util/qdeclarativelistmodel_p.h @@ -59,6 +59,9 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) +class FlatListModel; +class NestedListModel; +class QDeclarativeListModelWorkerAgent; struct ModelNode; class Q_DECLARATIVE_EXPORT QDeclarativeListModel : public QListModelInterface { @@ -83,20 +86,26 @@ public: Q_INVOKABLE void set(int index, const QScriptValue&); Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + QDeclarativeListModelWorkerAgent *agent(); Q_SIGNALS: void countChanged(int); private: - QVariant valueForNode(ModelNode *) const; - mutable QStringList roleStrings; friend class QDeclarativeListModelParser; + friend class QDeclarativeListModelWorkerAgent; friend struct ModelNode; - void checkRoles() const; - void addRole(const QString &) const; - mutable bool _rolesOk; - ModelNode *_root; + QDeclarativeListModel(bool workerCopy, QObject *parent=0); + bool flatten(); + bool modifyCheck(); + + QDeclarativeListModelWorkerAgent *m_agent; + NestedListModel *m_nested; + FlatListModel *m_flat; + bool m_isWorkerCopy; }; // ### FIXME diff --git a/src/declarative/util/qdeclarativelistmodel_p_p.h b/src/declarative/util/qdeclarativelistmodel_p_p.h new file mode 100644 index 0000000..8041561 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodel_p_p.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODEL_P_P_H +#define QDECLARATIVELISTMODEL_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qdeclarativelistmodel_p.h" + +#include "qdeclarative.h" +#include "qdeclarativeengine_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeOpenMetaObject; +class QScriptEngine; +class QDeclarativeListModelWorkerAgent; +struct ModelNode; + +class FlatListModel +{ +public: + FlatListModel(QDeclarativeListModel *base); + ~FlatListModel(); + + QHash<int,QVariant> data(int index, const QList<int> &roles) const; + QVariant data(int index, int role) const; + + QList<int> roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool append(const QScriptValue&); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList<int> *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles); + void move(int from, int to, int count); + +private: + friend class QDeclarativeListModelWorkerAgent; + friend class QDeclarativeListModel; + + bool addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles); + + QScriptEngine *m_scriptEngine; + QHash<int, QString> m_roles; + QHash<QString, int> m_strings; + QList<QHash<int, QVariant> > m_values; + QDeclarativeListModel *m_listModel; +}; + +class NestedListModel +{ +public: + NestedListModel(QDeclarativeListModel *base); + ~NestedListModel(); + + QHash<int,QVariant> data(int index, const QList<int> &roles, bool *hasNested = 0) const; + QVariant data(int index, int role) const; + + QList<int> roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool append(const QScriptValue&); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList<int> *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList<int> *role); + void move(int from, int to, int count); + + QVariant valueForNode(ModelNode *, bool *hasNested = 0) const; + void checkRoles() const; + + ModelNode *_root; + QDeclarativeListModel *m_listModel; + +private: + mutable QStringList roleStrings; + mutable bool _rolesOk; +}; + + +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(); + void setValue(const QByteArray &name, const QVariant &val); + +private: + QDeclarativeOpenMetaObject *_mo; +}; + +struct ModelNode +{ + ModelNode(); + ~ModelNode(); + + QList<QVariant> values; + QHash<QString, ModelNode *> properties; + + QDeclarativeListModel *model(const NestedListModel *model) { + if (!modelCache) { + modelCache = new QDeclarativeListModel; + QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel)); + modelCache->m_nested->_root = this; // ListModel defaults to nestable model + } + return modelCache; + } + + ModelObject *object(const NestedListModel *model) { + if (!objectCache) { + objectCache = new ModelObject(); + QHash<QString, ModelNode *>::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it)); + } + } + return objectCache; + } + + + void setObjectValue(const QScriptValue& valuemap); + void setListValue(const QScriptValue& valuelist); + void setProperty(const QString& prop, const QVariant& val); + static void dump(ModelNode *node, int ind); + + QDeclarativeListModel *modelCache; + ModelObject *objectCache; + bool isArray; +}; + + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ModelNode *) + +QT_END_HEADER + +#endif // QDECLARATIVELISTMODEL_P_P_H + diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp new file mode 100644 index 0000000..2951262 --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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 "qdeclarativelistmodelworkeragent_p.h" +#include "qdeclarativelistmodel_p_p.h" +#include "qdeclarativedeclarativedata_p.h" +#include "qdeclarativeengine_p.h" +#include "qdeclarativeinfo.h" + +#include <QtCore/qcoreevent.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE + + +void QDeclarativeListModelWorkerAgent::Data::clearChange() +{ + changes.clear(); +} + +void QDeclarativeListModelWorkerAgent::Data::insertChange(int index, int count) +{ + Change c = { Change::Inserted, index, count, 0 }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::removeChange(int index, int count) +{ + Change c = { Change::Removed, index, count, 0 }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::moveChange(int index, int count, int to) +{ + Change c = { Change::Moved, index, count, to }; + changes << c; +} + +void QDeclarativeListModelWorkerAgent::Data::changedChange(int index, int count) +{ + Change c = { Change::Changed, index, count, 0 }; + changes << c; +} + +QDeclarativeListModelWorkerAgent::QDeclarativeListModelWorkerAgent(QDeclarativeListModel *model) +: m_engine(0), m_ref(1), m_orig(model), m_copy(new QDeclarativeListModel(true, this)) +{ + m_copy->m_flat->m_roles = m_orig->m_flat->m_roles; + m_copy->m_flat->m_strings = m_orig->m_flat->m_strings; + m_copy->m_flat->m_values = m_orig->m_flat->m_values; +} + +QDeclarativeListModelWorkerAgent::~QDeclarativeListModelWorkerAgent() +{ +} + +void QDeclarativeListModelWorkerAgent::setScriptEngine(QScriptEngine *eng) +{ + m_engine = eng; + if (m_copy->m_flat) + m_copy->m_flat->m_scriptEngine = eng; +} + +QScriptEngine *QDeclarativeListModelWorkerAgent::scriptEngine() const +{ + return m_engine; +} + +void QDeclarativeListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QDeclarativeListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + delete this; +} + +int QDeclarativeListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QDeclarativeListModelWorkerAgent::clear() +{ + data.clearChange(); + data.removeChange(0, m_copy->count()); + m_copy->clear(); +} + +void QDeclarativeListModelWorkerAgent::remove(int index) +{ + int count = m_copy->count(); + m_copy->remove(index); + + if (m_copy->count() != count) + data.removeChange(index, 1); +} + +void QDeclarativeListModelWorkerAgent::append(const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->append(value); + + if (m_copy->count() != count) + data.insertChange(m_copy->count() - 1, 1); +} + +void QDeclarativeListModelWorkerAgent::insert(int index, const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->insert(index, value); + + if (m_copy->count() != count) + data.insertChange(index, 1); +} + +QScriptValue QDeclarativeListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QDeclarativeListModelWorkerAgent::set(int index, const QScriptValue &value) +{ + m_copy->set(index, value); + data.changedChange(index, 1); +} + +void QDeclarativeListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + m_copy->setProperty(index, property, value); + data.changedChange(index, 1); +} + +void QDeclarativeListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); + data.moveChange(from, to, count); +} + +void QDeclarativeListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + QCoreApplication::postEvent(this, s); +} + +bool QDeclarativeListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + Sync *s = static_cast<Sync *>(e); + + const QList<Change> &changes = s->data.changes; + + if (m_copy) { + bool cc = m_copy->count() != s->list->count(); + + FlatListModel *orig = m_orig->m_flat; + FlatListModel *copy = s->list->m_flat; + if (!orig || !copy) { + qWarning("QML ListModel worker: sync() failed"); + return QObject::event(e); + } + orig->m_roles = copy->m_roles; + orig->m_strings = copy->m_strings; + orig->m_values = copy->m_values; + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + emit m_orig->itemsInserted(change.index, change.count); + break; + case Change::Removed: + emit m_orig->itemsRemoved(change.index, change.count); + break; + case Change::Moved: + emit m_orig->itemsMoved(change.index, change.to, change.count); + break; + case Change::Changed: + emit m_orig->itemsMoved(change.index, change.to, change.count); + break; + } + } + + if (cc) + emit m_orig->countChanged(m_copy->count()); + } + } + + return QObject::event(e); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h new file mode 100644 index 0000000..b6a643b --- /dev/null +++ b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODELWORKERAGENT_P_H +#define QDECLARATIVELISTMODELWORKERAGENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qdeclarative.h" + +#include <QtScript/qscriptvalue.h> +#include <QtGui/qevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeListModel; + +// Currently this will leak as no-one releases it in the worker thread +class QDeclarativeListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QDeclarativeListModelWorkerAgent(QDeclarativeListModel *); + ~QDeclarativeListModelWorkerAgent(); + + void setScriptEngine(QScriptEngine *eng); + QScriptEngine *scriptEngine() const; + + void addref(); + void release(); + + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QScriptValue &); + Q_INVOKABLE void insert(int index, const QScriptValue&); + Q_INVOKABLE QScriptValue get(int index) const; + Q_INVOKABLE void set(int index, const QScriptValue &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QDeclarativeListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + VariantRef &operator=(const VariantRef &o) { + if (o.a) o.a->addref(); + if (a) a->release(); a = o.a; + return *this; + } + + QDeclarativeListModelWorkerAgent *a; + }; +protected: + virtual bool event(QEvent *); + +private: + friend class QDeclarativeWorkerScriptEnginePrivate; + QScriptEngine *m_engine; + + struct Change { + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + }; + + struct Data { + QList<Change> changes; + + void clearChange(); + void insertChange(int index, int count); + void removeChange(int index, int count); + void moveChange(int index, int count, int to); + void changedChange(int index, int count); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QDeclarativeListModel *list; + }; + + QAtomicInt m_ref; + QDeclarativeListModel *m_orig; + QDeclarativeListModel *m_copy; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeListModelWorkerAgent::VariantRef); + +QT_END_HEADER + +#endif // QDECLARATIVEWORKERSCRIPT_P_H + diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index cfb25dd..942d5f6 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -137,7 +137,7 @@ class QDeclarativeImageRequestHandler : public QObject Q_OBJECT public: QDeclarativeImageRequestHandler(QDeclarativeImageReader *read, QDeclarativeEngine *eng) - : QObject(), accessManager(0), engine(eng), reader(read) + : QObject(), accessManager(0), engine(eng), reader(read), redirectCount(0) { QCoreApplication::postEvent(this, new QEvent(QEvent::User)); } @@ -162,18 +162,24 @@ private: QNetworkAccessManager *accessManager; QDeclarativeEngine *engine; QDeclarativeImageReader *reader; + int redirectCount; + + static int replyDownloadProgress; + static int replyFinished; + static int downloadProgress; + static int thisNetworkRequestDone; }; //=========================================================================== +int QDeclarativeImageRequestHandler::replyDownloadProgress = -1; +int QDeclarativeImageRequestHandler::replyFinished = -1; +int QDeclarativeImageRequestHandler::downloadProgress = -1; +int QDeclarativeImageRequestHandler::thisNetworkRequestDone = -1; + bool QDeclarativeImageRequestHandler::event(QEvent *event) { if (event->type() == QEvent::User) { - static int replyDownloadProgress = -1; - static int replyFinished = -1; - static int downloadProgress = -1; - static int thisNetworkRequestDone = -1; - if (replyDownloadProgress == -1) { replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()"); @@ -264,10 +270,33 @@ bool QDeclarativeImageRequestHandler::event(QEvent *event) return QObject::event(event); } +#define IMAGEREQUESTHANDLER_MAX_REDIRECT_RECURSION 16 + void QDeclarativeImageRequestHandler::networkRequestDone() { QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); QDeclarativePixmapReply *job = replies.take(reply); + + redirectCount++; + if (redirectCount < IMAGEREQUESTHANDLER_MAX_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + + reply->deleteLater(); + reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress); + QMetaObject::connect(reply, replyFinished, this, thisNetworkRequestDone); + + replies.insert(reply, job); + return; + } + } + redirectCount=0; + if (job) { QImage image; QDeclarativeImageReaderEvent::ReadError error; diff --git a/src/declarative/util/qdeclarativepropertychanges.cpp b/src/declarative/util/qdeclarativepropertychanges.cpp index 9c2b1e6..8865e04 100644 --- a/src/declarative/util/qdeclarativepropertychanges.cpp +++ b/src/declarative/util/qdeclarativepropertychanges.cpp @@ -439,7 +439,8 @@ QDeclarativePropertyChanges::ActionList QDeclarativePropertyChanges::actions() if (d->isExplicit) { a.toValue = d->expressions.at(ii).second->value(); } else { - QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->expressions.at(ii).second->expression(), object(), qmlContext(this)); + QDeclarativeBinding *newBinding = + new QDeclarativeBinding(d->expressions.at(ii).second->expression(), object(), qmlContext(this)); newBinding->setTarget(prop); a.toBinding = newBinding; a.deletableToBinding = true; diff --git a/src/declarative/util/qdeclarativestate.cpp b/src/declarative/util/qdeclarativestate.cpp index 1d70d33..802ff1c 100644 --- a/src/declarative/util/qdeclarativestate.cpp +++ b/src/declarative/util/qdeclarativestate.cpp @@ -96,21 +96,12 @@ void QDeclarativeActionEvent::reverse() { } -QList<QDeclarativeAction> QDeclarativeActionEvent::extraActions() -{ - return QList<QDeclarativeAction>(); -} - bool QDeclarativeActionEvent::changesBindings() { return false; } -void QDeclarativeActionEvent::clearForwardBindings() -{ -} - -void QDeclarativeActionEvent::clearReverseBindings() +void QDeclarativeActionEvent::clearBindings() { } @@ -368,47 +359,60 @@ void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransit for (int ii = 0; ii < applyList.count(); ++ii) { QDeclarativeAction &action = applyList[ii]; - bool found = false; - - int jj; if (action.event) { if (!action.event->isReversable()) continue; - for (jj = 0; jj < d->revertList.count(); ++jj) { + bool found = false; + for (int jj = 0; jj < d->revertList.count(); ++jj) { QDeclarativeActionEvent *event = d->revertList.at(jj).event; if (event && event->typeName() == action.event->typeName()) { if (action.event->override(event)) { found = true; + + if (action.event != d->revertList.at(jj).event) { + action.event->copyOriginals(d->revertList.at(jj).event); + + QDeclarativeSimpleAction r(action); + additionalReverts << r; + d->revertList.removeAt(jj); + } else if (action.event->isRewindable()) //###why needed? + action.event->saveCurrentValues(); + break; } } } - if (!found || action.event != d->revertList.at(jj).event) + if (!found) { action.event->saveOriginals(); - else if (action.event->isRewindable()) - action.event->saveCurrentValues(); + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } } else { + bool found = false; action.fromBinding = QDeclarativePropertyPrivate::binding(action.property); - for (jj = 0; jj < d->revertList.count(); ++jj) { + for (int jj = 0; jj < d->revertList.count(); ++jj) { if (d->revertList.at(jj).property == action.property) { found = true; + if (d->revertList.at(jj).binding != action.fromBinding) { + action.deleteFromBinding(); + } break; } } - } - if (!found) { - if (!action.restore) { - action.deleteFromBinding(); - } else { - // Only need to revert the applyList action if the previous - // state doesn't have a higher priority revert already - QDeclarativeSimpleAction r(action); - additionalReverts << r; + if (!found) { + if (!action.restore) { + action.deleteFromBinding(); + } else { + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarativeSimpleAction r(action); + additionalReverts << r; + } } - } else if (d->revertList.at(jj).binding != action.fromBinding) { - action.deleteFromBinding(); } } diff --git a/src/declarative/util/qdeclarativestate_p.h b/src/declarative/util/qdeclarativestate_p.h index 0c6e7a3..ee2b7e8 100644 --- a/src/declarative/util/qdeclarativestate_p.h +++ b/src/declarative/util/qdeclarativestate_p.h @@ -92,17 +92,15 @@ public: virtual bool isReversable(); virtual void reverse(); virtual void saveOriginals() {} + virtual void copyOriginals(QDeclarativeActionEvent *) {} virtual bool isRewindable() { return isReversable(); } virtual void rewind() {} virtual void saveCurrentValues() {} - - //virtual bool hasExtraActions(); - virtual QList<QDeclarativeAction> extraActions(); + virtual void saveTargetValues() {} virtual bool changesBindings(); - virtual void clearForwardBindings(); - virtual void clearReverseBindings(); + virtual void clearBindings(); virtual bool override(QDeclarativeActionEvent*other); }; diff --git a/src/declarative/util/qdeclarativestateoperations.cpp b/src/declarative/util/qdeclarativestateoperations.cpp index 766d1bc..6f5bb66 100644 --- a/src/declarative/util/qdeclarativestateoperations.cpp +++ b/src/declarative/util/qdeclarativestateoperations.cpp @@ -403,6 +403,17 @@ void QDeclarativeParentChange::saveOriginals() d->origStackBefore = d->rewindStackBefore; } +void QDeclarativeParentChange::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QDeclarativeParentChange); + QDeclarativeParentChange *pc = static_cast<QDeclarativeParentChange*>(other); + + d->origParent = pc->d_func()->rewindParent; + d->origStackBefore = pc->d_func()->rewindStackBefore; + + saveCurrentValues(); +} + void QDeclarativeParentChange::execute() { Q_D(QDeclarativeParentChange); @@ -561,11 +572,17 @@ QString QDeclarativeStateChangeScript::typeName() const \qmlclass AnchorChanges QDeclarativeAnchorChanges \brief The AnchorChanges element allows you to change the anchors of an item in a state. - AnchorChanges will 'inject' \c x, \c y, \c width, and \c height changes into the transition, - so you can animate them as you would normally changes to these properties: + In the following example we change the top and bottom anchors of an item: + \qml + AnchorChanges { + target: content; top: window.top; bottom: window.bottom + } + \endqml + + AnchorChanges can be animated using AnchorAnimation. \qml //animate our anchor changes - NumberAnimation { targets: content; properties: "x,y,width,height" } + AnchorAnimation {} \endqml For more information on anchors see \l {anchor-layout}{Anchor Layouts}. @@ -578,10 +595,10 @@ class QDeclarativeAnchorChangesPrivate : public QObjectPrivate public: QDeclarativeAnchorChangesPrivate() : target(0) {} - QString name; QDeclarativeItem *target; QString resetString; QStringList resetList; + QDeclarativeAnchorLine left; QDeclarativeAnchorLine right; QDeclarativeAnchorLine horizontalCenter; @@ -610,6 +627,24 @@ public: qreal fromY; qreal fromWidth; qreal fromHeight; + + qreal toX; + qreal toY; + qreal toWidth; + qreal toHeight; + + qreal rewindX; + qreal rewindY; + qreal rewindWidth; + qreal rewindHeight; + + bool applyOrigLeft; + bool applyOrigRight; + bool applyOrigHCenter; + bool applyOrigTop; + bool applyOrigBottom; + bool applyOrigVCenter; + bool applyOrigBaseline; }; /*! @@ -762,6 +797,38 @@ void QDeclarativeAnchorChanges::execute() if (!d->target) return; + //incorporate any needed "reverts" + if (d->applyOrigLeft) + d->target->anchors()->setLeft(d->origLeft); + if (d->applyOrigRight) + d->target->anchors()->setRight(d->origRight); + if (d->applyOrigHCenter) + d->target->anchors()->setHorizontalCenter(d->origHCenter); + if (d->applyOrigTop) + d->target->anchors()->setTop(d->origTop); + if (d->applyOrigBottom) + d->target->anchors()->setBottom(d->origBottom); + if (d->applyOrigVCenter) + d->target->anchors()->setVerticalCenter(d->origVCenter); + if (d->applyOrigBaseline) + d->target->anchors()->setBaseline(d->origBaseline); + + //reset any anchors that have been specified + if (d->resetList.contains(QLatin1String("left"))) + d->target->anchors()->resetLeft(); + if (d->resetList.contains(QLatin1String("right"))) + d->target->anchors()->resetRight(); + if (d->resetList.contains(QLatin1String("horizontalCenter"))) + d->target->anchors()->resetHorizontalCenter(); + if (d->resetList.contains(QLatin1String("top"))) + d->target->anchors()->resetTop(); + if (d->resetList.contains(QLatin1String("bottom"))) + d->target->anchors()->resetBottom(); + if (d->resetList.contains(QLatin1String("verticalCenter"))) + d->target->anchors()->resetVerticalCenter(); + if (d->resetList.contains(QLatin1String("baseline"))) + d->target->anchors()->resetBaseline(); + //set any anchors that have been specified if (d->left.anchorLine != QDeclarativeAnchorLine::Invalid) d->target->anchors()->setLeft(d->left); @@ -790,6 +857,22 @@ void QDeclarativeAnchorChanges::reverse() if (!d->target) return; + //reset any anchors set by the state + if (d->left.anchorLine != QDeclarativeAnchorLine::Invalid) + d->target->anchors()->resetLeft(); + if (d->right.anchorLine != QDeclarativeAnchorLine::Invalid) + d->target->anchors()->resetRight(); + if (d->horizontalCenter.anchorLine != QDeclarativeAnchorLine::Invalid) + d->target->anchors()->resetHorizontalCenter(); + if (d->top.anchorLine != QDeclarativeAnchorLine::Invalid) + d->target->anchors()->resetTop(); + if (d->bottom.anchorLine != QDeclarativeAnchorLine::Invalid) + d->target->anchors()->resetBottom(); + if (d->verticalCenter.anchorLine != QDeclarativeAnchorLine::Invalid) + d->target->anchors()->resetVerticalCenter(); + if (d->baseline.anchorLine != QDeclarativeAnchorLine::Invalid) + d->target->anchors()->resetBaseline(); + //restore previous anchors if (d->origLeft.anchorLine != QDeclarativeAnchorLine::Invalid) d->target->anchors()->setLeft(d->origLeft); @@ -812,31 +895,33 @@ QString QDeclarativeAnchorChanges::typeName() const return QLatin1String("AnchorChanges"); } -QList<QDeclarativeAction> QDeclarativeAnchorChanges::extraActions() +QList<QDeclarativeAction> QDeclarativeAnchorChanges::additionalActions() { Q_D(QDeclarativeAnchorChanges); QList<QDeclarativeAction> extra; - //### try to be smarter about which ones we add. - // or short-circuit later on if they haven't actually changed. - // we shouldn't set explicit width if there wasn't one before. if (d->target) { QDeclarativeAction a; - a.fromValue = d->fromX; - a.property = QDeclarativeProperty(d->target, QLatin1String("x")); - extra << a; - - a.fromValue = d->fromY; - a.property = QDeclarativeProperty(d->target, QLatin1String("y")); - extra << a; - - a.fromValue = d->fromWidth; - a.property = QDeclarativeProperty(d->target, QLatin1String("width")); - extra << a; - - a.fromValue = d->fromHeight; - a.property = QDeclarativeProperty(d->target, QLatin1String("height")); - extra << a; + if (d->fromX != d->toX) { + a.property = QDeclarativeProperty(d->target, QLatin1String("x")); + a.toValue = d->toX; + extra << a; + } + if (d->fromY != d->toY) { + a.property = QDeclarativeProperty(d->target, QLatin1String("y")); + a.toValue = d->toY; + extra << a; + } + if (d->fromWidth != d->toWidth) { + a.property = QDeclarativeProperty(d->target, QLatin1String("width")); + a.toValue = d->toWidth; + extra << a; + } + if (d->fromHeight != d->toHeight) { + a.property = QDeclarativeProperty(d->target, QLatin1String("height")); + a.toValue = d->toHeight; + extra << a; + } } return extra; @@ -858,10 +943,52 @@ void QDeclarativeAnchorChanges::saveOriginals() d->origVCenter = d->target->anchors()->verticalCenter(); d->origBaseline = d->target->anchors()->baseline(); + d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop + = d->applyOrigBottom = d->applyOrigHCenter = d->applyOrigBaseline = false; + saveCurrentValues(); } -void QDeclarativeAnchorChanges::clearForwardBindings() +void QDeclarativeAnchorChanges::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QDeclarativeAnchorChanges); + QDeclarativeAnchorChanges *ac = static_cast<QDeclarativeAnchorChanges*>(other); + QDeclarativeAnchorChangesPrivate *acp = ac->d_func(); + + //probably also need to revert some things + d->applyOrigLeft = (acp->left.anchorLine != QDeclarativeAnchorLine::Invalid || + acp->resetList.contains(QLatin1String("left"))); + + d->applyOrigRight = (acp->right.anchorLine != QDeclarativeAnchorLine::Invalid || + acp->resetList.contains(QLatin1String("right"))); + + d->applyOrigHCenter = (acp->horizontalCenter.anchorLine != QDeclarativeAnchorLine::Invalid || + acp->resetList.contains(QLatin1String("horizontalCenter"))); + + d->applyOrigTop = (acp->top.anchorLine != QDeclarativeAnchorLine::Invalid || + acp->resetList.contains(QLatin1String("top"))); + + d->applyOrigBottom = (acp->bottom.anchorLine != QDeclarativeAnchorLine::Invalid || + acp->resetList.contains(QLatin1String("bottom"))); + + d->applyOrigVCenter = (acp->verticalCenter.anchorLine != QDeclarativeAnchorLine::Invalid || + acp->resetList.contains(QLatin1String("verticalCenter"))); + + d->applyOrigBaseline = (acp->baseline.anchorLine != QDeclarativeAnchorLine::Invalid || + acp->resetList.contains(QLatin1String("baseline"))); + + d->origLeft = ac->d_func()->origLeft; + d->origRight = ac->d_func()->origRight; + d->origHCenter = ac->d_func()->origHCenter; + d->origTop = ac->d_func()->origTop; + d->origBottom = ac->d_func()->origBottom; + d->origVCenter = ac->d_func()->origVCenter; + d->origBaseline = ac->d_func()->origBaseline; + + saveCurrentValues(); +} + +void QDeclarativeAnchorChanges::clearBindings() { Q_D(QDeclarativeAnchorChanges); d->fromX = d->target->x(); @@ -869,6 +996,22 @@ void QDeclarativeAnchorChanges::clearForwardBindings() d->fromWidth = d->target->width(); d->fromHeight = d->target->height(); + //reset any anchors with corresponding reverts + if (d->applyOrigLeft) + d->target->anchors()->resetLeft(); + if (d->applyOrigRight) + d->target->anchors()->resetRight(); + if (d->applyOrigHCenter) + d->target->anchors()->resetHorizontalCenter(); + if (d->applyOrigTop) + d->target->anchors()->resetTop(); + if (d->applyOrigBottom) + d->target->anchors()->resetBottom(); + if (d->applyOrigVCenter) + d->target->anchors()->resetVerticalCenter(); + if (d->applyOrigBaseline) + d->target->anchors()->resetBaseline(); + //reset any anchors that have been specified if (d->resetList.contains(QLatin1String("left"))) d->target->anchors()->resetLeft(); @@ -902,47 +1045,6 @@ void QDeclarativeAnchorChanges::clearForwardBindings() d->target->anchors()->resetBaseline(); } -void QDeclarativeAnchorChanges::clearReverseBindings() -{ - Q_D(QDeclarativeAnchorChanges); - d->fromX = d->target->x(); - d->fromY = d->target->y(); - d->fromWidth = d->target->width(); - d->fromHeight = d->target->height(); - - //reset any anchors that were set in the state - if (d->left.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetLeft(); - if (d->right.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetRight(); - if (d->horizontalCenter.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetHorizontalCenter(); - if (d->top.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetTop(); - if (d->bottom.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetBottom(); - if (d->verticalCenter.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetVerticalCenter(); - if (d->baseline.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetBaseline(); - - //reset any anchors that were set in the original state - if (d->origLeft.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetLeft(); - if (d->origRight.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetRight(); - if (d->origHCenter.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetHorizontalCenter(); - if (d->origTop.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetTop(); - if (d->origBottom.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetBottom(); - if (d->origVCenter.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetVerticalCenter(); - if (d->origBaseline.anchorLine != QDeclarativeAnchorLine::Invalid) - d->target->anchors()->resetBaseline(); -} - bool QDeclarativeAnchorChanges::override(QDeclarativeActionEvent*other) { if (other->typeName() != QLatin1String("AnchorChanges")) @@ -975,6 +1077,11 @@ void QDeclarativeAnchorChanges::rewind() d->target->anchors()->setVerticalCenter(d->rewindVCenter); if (d->rewindBaseline.anchorLine != QDeclarativeAnchorLine::Invalid) d->target->anchors()->setBaseline(d->rewindBaseline); + + d->target->setX(d->rewindX); + d->target->setY(d->rewindY); + d->target->setWidth(d->rewindWidth); + d->target->setHeight(d->rewindHeight); } void QDeclarativeAnchorChanges::saveCurrentValues() @@ -987,6 +1094,20 @@ void QDeclarativeAnchorChanges::saveCurrentValues() d->rewindBottom = d->target->anchors()->bottom(); d->rewindVCenter = d->target->anchors()->verticalCenter(); d->rewindBaseline = d->target->anchors()->baseline(); + + d->rewindX = d->target->x(); + d->rewindY = d->target->y(); + d->rewindWidth = d->target->width(); + d->rewindHeight = d->target->height(); +} + +void QDeclarativeAnchorChanges::saveTargetValues() +{ + Q_D(QDeclarativeAnchorChanges); + d->toX = d->target->x(); + d->toY = d->target->y(); + d->toWidth = d->target->width(); + d->toHeight = d->target->height(); } #include <qdeclarativestateoperations.moc> diff --git a/src/declarative/util/qdeclarativestateoperations_p.h b/src/declarative/util/qdeclarativestateoperations_p.h index dd4248023..66a8717 100644 --- a/src/declarative/util/qdeclarativestateoperations_p.h +++ b/src/declarative/util/qdeclarativestateoperations_p.h @@ -107,6 +107,7 @@ public: virtual ActionList actions(); virtual void saveOriginals(); + virtual void copyOriginals(QDeclarativeActionEvent*); virtual void execute(); virtual bool isReversable(); virtual void reverse(); @@ -196,13 +197,15 @@ public: virtual void reverse(); virtual QString typeName() const; virtual bool override(QDeclarativeActionEvent*other); - virtual QList<QDeclarativeAction> extraActions(); virtual bool changesBindings(); virtual void saveOriginals(); - virtual void clearForwardBindings(); - virtual void clearReverseBindings(); + virtual void copyOriginals(QDeclarativeActionEvent*); + virtual void clearBindings(); virtual void rewind(); virtual void saveCurrentValues(); + + QList<QDeclarativeAction> additionalActions(); + virtual void saveTargetValues(); }; QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativetransitionmanager.cpp b/src/declarative/util/qdeclarativetransitionmanager.cpp index e1143fa..f07fb23 100644 --- a/src/declarative/util/qdeclarativetransitionmanager.cpp +++ b/src/declarative/util/qdeclarativetransitionmanager.cpp @@ -126,10 +126,7 @@ void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> & QDeclarativePropertyPrivate::setBinding(action.property, 0); // Disable current binding if (action.event && action.event->changesBindings()) { //### assume isReversable()? d->bindingsList << action; - if (action.reverseEvent) - action.event->clearReverseBindings(); - else - action.event->clearForwardBindings(); + action.event->clearBindings(); } } @@ -144,8 +141,6 @@ void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> & if (!d->bindingsList.isEmpty()) { - //### do extra actions here? - // Apply all the property and binding changes for (int ii = 0; ii < applyList.size(); ++ii) { const QDeclarativeAction &action = applyList.at(ii); @@ -158,17 +153,18 @@ void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> & action.event->reverse(); else action.event->execute(); - applyList << action.event->extraActions(); } } // Read all the end values for binding changes for (int ii = 0; ii < applyList.size(); ++ii) { QDeclarativeAction *action = &applyList[ii]; - if (action->event) + if (action->event) { + action->event->saveTargetValues(); continue; + } const QDeclarativeProperty &prop = action->property; - if (action->toBinding || !action->toValue.isValid()) { //### is this always right (used for exta actions) + if (action->toBinding || !action->toValue.isValid()) { action->toValue = prop.read(); } } @@ -177,15 +173,9 @@ void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> & foreach(const QDeclarativeAction &action, applyList) { if (action.event) { if (action.event->isReversable()) { - if (action.reverseEvent) { //reverse the reverse - action.event->clearForwardBindings(); - action.event->rewind(); - action.event->clearReverseBindings(); - } else { - action.event->clearReverseBindings(); - action.event->rewind(); - action.event->clearForwardBindings(); - } + action.event->clearBindings(); + action.event->rewind(); + action.event->clearBindings(); } continue; } diff --git a/src/declarative/util/qdeclarativeutilmodule.cpp b/src/declarative/util/qdeclarativeutilmodule.cpp index 65bfdc1..4d4678a 100644 --- a/src/declarative/util/qdeclarativeutilmodule.cpp +++ b/src/declarative/util/qdeclarativeutilmodule.cpp @@ -75,44 +75,48 @@ void QDeclarativeUtilModule::defineModule() { - QML_REGISTER_TYPE(Qt,4,6,AnchorChanges,QDeclarativeAnchorChanges); - QML_REGISTER_TYPE(Qt,4,6,Behavior,QDeclarativeBehavior); - QML_REGISTER_TYPE(Qt,4,6,Binding,QDeclarativeBind); - QML_REGISTER_TYPE(Qt,4,6,ColorAnimation,QDeclarativeColorAnimation); - QML_REGISTER_TYPE(Qt,4,6,Connections,QDeclarativeConnections); - QML_REGISTER_TYPE(Qt,4,6,EaseFollow,QDeclarativeEaseFollow);; - QML_REGISTER_TYPE(Qt,4,6,FontLoader,QDeclarativeFontLoader); - QML_REGISTER_TYPE(Qt,4,6,ListElement,QDeclarativeListElement); - QML_REGISTER_TYPE(Qt,4,6,NumberAnimation,QDeclarativeNumberAnimation); - QML_REGISTER_TYPE(Qt,4,6,Package,QDeclarativePackage); - QML_REGISTER_TYPE(Qt,4,6,ParallelAnimation,QDeclarativeParallelAnimation); - QML_REGISTER_TYPE(Qt,4,6,ParentAction,QDeclarativeParentAction); - QML_REGISTER_TYPE(Qt,4,6,ParentAnimation,QDeclarativeParentAnimation); - QML_REGISTER_TYPE(Qt,4,6,ParentChange,QDeclarativeParentChange); - QML_REGISTER_TYPE(Qt,4,6,PauseAnimation,QDeclarativePauseAnimation); - QML_REGISTER_TYPE(Qt,4,6,PropertyAction,QDeclarativePropertyAction); - QML_REGISTER_TYPE(Qt,4,6,PropertyAnimation,QDeclarativePropertyAnimation); - QML_REGISTER_TYPE(Qt,4,6,RotationAnimation,QDeclarativeRotationAnimation); - QML_REGISTER_TYPE(Qt,4,6,ScriptAction,QDeclarativeScriptAction); - QML_REGISTER_TYPE(Qt,4,6,SequentialAnimation,QDeclarativeSequentialAnimation); - QML_REGISTER_TYPE(Qt,4,6,SpringFollow,QDeclarativeSpringFollow); - QML_REGISTER_TYPE(Qt,4,6,StateChangeScript,QDeclarativeStateChangeScript); - QML_REGISTER_TYPE(Qt,4,6,StateGroup,QDeclarativeStateGroup); - QML_REGISTER_TYPE(Qt,4,6,State,QDeclarativeState); - QML_REGISTER_TYPE(Qt,4,6,SystemPalette,QDeclarativeSystemPalette); - QML_REGISTER_TYPE(Qt,4,6,Timer,QDeclarativeTimer); - QML_REGISTER_TYPE(Qt,4,6,Transition,QDeclarativeTransition); - QML_REGISTER_TYPE(Qt,4,6,Vector3dAnimation,QDeclarativeVector3dAnimation); + qmlRegisterType<QDeclarativeAnchorAnimation>("Qt",4,6,"AnchorAnimation"); + qmlRegisterType<QDeclarativeAnchorChanges>("Qt",4,6,"AnchorChanges"); + qmlRegisterType<QDeclarativeBehavior>("Qt",4,6,"Behavior"); + qmlRegisterType<QDeclarativeBind>("Qt",4,6,"Binding"); + qmlRegisterType<QDeclarativeColorAnimation>("Qt",4,6,"ColorAnimation"); + qmlRegisterType<QDeclarativeConnections>("Qt",4,6,"Connections"); + qmlRegisterType<QDeclarativeEaseFollow>("Qt",4,6,"EaseFollow"); + qmlRegisterType<QDeclarativeFontLoader>("Qt",4,6,"FontLoader"); + qmlRegisterType<QDeclarativeListElement>("Qt",4,6,"ListElement"); + qmlRegisterType<QDeclarativeNumberAnimation>("Qt",4,6,"NumberAnimation"); + qmlRegisterType<QDeclarativePackage>("Qt",4,6,"Package"); + qmlRegisterType<QDeclarativeParallelAnimation>("Qt",4,6,"ParallelAnimation"); + qmlRegisterType<QDeclarativeParentAction>("Qt",4,6,"ParentAction"); + qmlRegisterType<QDeclarativeParentAnimation>("Qt",4,6,"ParentAnimation"); + qmlRegisterType<QDeclarativeParentChange>("Qt",4,6,"ParentChange"); + qmlRegisterType<QDeclarativePauseAnimation>("Qt",4,6,"PauseAnimation"); + qmlRegisterType<QDeclarativePropertyAction>("Qt",4,6,"PropertyAction"); + qmlRegisterType<QDeclarativePropertyAnimation>("Qt",4,6,"PropertyAnimation"); + qmlRegisterType<QDeclarativeRotationAnimation>("Qt",4,6,"RotationAnimation"); + qmlRegisterType<QDeclarativeScriptAction>("Qt",4,6,"ScriptAction"); + qmlRegisterType<QDeclarativeSequentialAnimation>("Qt",4,6,"SequentialAnimation"); + qmlRegisterType<QDeclarativeSpringFollow>("Qt",4,6,"SpringFollow"); + qmlRegisterType<QDeclarativeStateChangeScript>("Qt",4,6,"StateChangeScript"); + qmlRegisterType<QDeclarativeStateGroup>("Qt",4,6,"StateGroup"); + qmlRegisterType<QDeclarativeState>("Qt",4,6,"State"); + qmlRegisterType<QDeclarativeSystemPalette>("Qt",4,6,"SystemPalette"); + qmlRegisterType<QDeclarativeTimer>("Qt",4,6,"Timer"); + qmlRegisterType<QDeclarativeTransition>("Qt",4,6,"Transition"); + qmlRegisterType<QDeclarativeVector3dAnimation>("Qt",4,6,"Vector3dAnimation"); #ifndef QT_NO_XMLPATTERNS - QML_REGISTER_TYPE(Qt,4,6,XmlListModel,QDeclarativeXmlListModel); - QML_REGISTER_TYPE(Qt,4,6,XmlRole,QDeclarativeXmlListModelRole); + qmlRegisterType<QDeclarativeXmlListModel>("Qt",4,6,"XmlListModel"); + qmlRegisterType<QDeclarativeXmlListModelRole>("Qt",4,6,"XmlRole"); #endif - QML_REGISTER_NOCREATE_TYPE(QDeclarativeAnchors); - QML_REGISTER_NOCREATE_TYPE(QDeclarativeAbstractAnimation); - QML_REGISTER_NOCREATE_TYPE(QDeclarativeStateOperation); + qmlRegisterType<QDeclarativeAnchors>(); + qmlRegisterType<QDeclarativeAbstractAnimation>(); + qmlRegisterType<QDeclarativeStateOperation>(); - QML_REGISTER_CUSTOM_TYPE(Qt, 4,6, ListModel, QDeclarativeListModel, QDeclarativeListModelParser); - QML_REGISTER_CUSTOM_TYPE(Qt, 4,6, PropertyChanges, QDeclarativePropertyChanges, QDeclarativePropertyChangesParser); - QML_REGISTER_CUSTOM_TYPE(Qt, 4,6, Connections, QDeclarativeConnections, QDeclarativeConnectionsParser); + qmlRegisterCustomType<QDeclarativeListModel>("Qt", 4,6, "ListModel", "QDeclarativeListModel", + new QDeclarativeListModelParser); + qmlRegisterCustomType<QDeclarativePropertyChanges>("Qt", 4, 6, "PropertyChanges", "QDeclarativePropertyChanges", + new QDeclarativePropertyChangesParser); + qmlRegisterCustomType<QDeclarativeConnections>("Qt", 4, 6, "Connections", "QDeclarativeConnections", + new QDeclarativeConnectionsParser); } diff --git a/src/declarative/util/qdeclarativeview.cpp b/src/declarative/util/qdeclarativeview.cpp index 735a009..59a062a 100644 --- a/src/declarative/util/qdeclarativeview.cpp +++ b/src/declarative/util/qdeclarativeview.cpp @@ -298,6 +298,7 @@ QDeclarativeView::~QDeclarativeView() void QDeclarativeView::setSource(const QUrl& url) { d->source = url; + d->engine.setBaseUrl(url); d->execute(); } @@ -458,8 +459,8 @@ void QDeclarativeView::setRootObject(QObject *obj) QPerformanceLog::clear(); d->root = item; d->qmlRoot = item; - connect(item, SIGNAL(widthChanged()), this, SLOT(sizeChanged())); - connect(item, SIGNAL(heightChanged()), this, SLOT(sizeChanged())); + connect(item, SIGNAL(widthChanged(qreal)), this, SLOT(sizeChanged())); + connect(item, SIGNAL(heightChanged(qreal)), this, SLOT(sizeChanged())); if (d->initialSize.height() <= 0 && d->qmlRoot->width() > 0) d->initialSize.setWidth(d->qmlRoot->width()); if (d->initialSize.height() <= 0 && d->qmlRoot->height() > 0) diff --git a/src/declarative/util/qdeclarativexmllistmodel.cpp b/src/declarative/util/qdeclarativexmllistmodel.cpp index 49dbb27..01ae2ed 100644 --- a/src/declarative/util/qdeclarativexmllistmodel.cpp +++ b/src/declarative/util/qdeclarativexmllistmodel.cpp @@ -61,6 +61,9 @@ QT_BEGIN_NAMESPACE + + + typedef QPair<int, int> QDeclarativeXmlListRange; /*! @@ -111,6 +114,9 @@ class QDeclarativeXmlQuery : public QThread { Q_OBJECT public: + QDeclarativeXmlQuery(QObject *parent=0) + : QThread(parent), m_quit(false), m_restart(false), m_abort(false), m_queryId(0) { + } ~QDeclarativeXmlQuery() { m_mutex.lock(); m_quit = true; @@ -120,11 +126,6 @@ public: wait(); } - static QDeclarativeXmlQuery *instance() { - static QDeclarativeXmlQuery *query = new QDeclarativeXmlQuery; - return query; - } - void abort() { QMutexLocker locker(&m_mutex); m_abort = true; @@ -163,11 +164,6 @@ public: return m_removedItemRanges; } -private: - QDeclarativeXmlQuery(QObject *parent=0) - : QThread(parent), m_quit(false), m_restart(false), m_abort(false), m_queryId(0) { - } - Q_SIGNALS: void queryCompleted(int queryId, int size); @@ -217,8 +213,6 @@ private: QList<QDeclarativeXmlListRange> m_removedItemRanges; }; -//Q_GLOBAL_STATIC(QDeclarativeXmlQuery, QDeclarativeXmlQuery::instance()); - void QDeclarativeXmlQuery::doQueryJob() { QString r; @@ -396,7 +390,7 @@ public: QDeclarativeXmlListModelPrivate() : isComponentComplete(true), size(-1), highestRole(Qt::UserRole) , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0) - , queryId(-1), roleObjects() {} + , queryId(-1), roleObjects(), redirectCount(0) {} bool isComponentComplete; QUrl src; @@ -410,6 +404,7 @@ public: QNetworkReply *reply; QDeclarativeXmlListModel::Status status; qreal progress; + QDeclarativeXmlQuery qmlXmlQuery; int queryId; QList<QDeclarativeXmlListModelRole *> roleObjects; static void append_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, QDeclarativeXmlListModelRole *role); @@ -417,6 +412,7 @@ public: static void removeAt_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, int i); static void insert_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, int i, QDeclarativeXmlListModelRole *role); QList<QList<QVariant> > data; + int redirectCount; }; @@ -493,7 +489,8 @@ void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty<QDecla QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent) : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent) { - connect(QDeclarativeXmlQuery::instance(), SIGNAL(queryCompleted(int,int)), + Q_D(QDeclarativeXmlListModel); + connect(&d->qmlXmlQuery, SIGNAL(queryCompleted(int,int)), this, SLOT(queryCompleted(int,int))); } @@ -575,8 +572,8 @@ void QDeclarativeXmlListModel::setSource(const QUrl &src) { Q_D(QDeclarativeXmlListModel); if (d->src != src) { - reload(); d->src = src; + reload(); emit sourceChanged(); } } @@ -726,7 +723,7 @@ void QDeclarativeXmlListModel::reload() if (!d->isComponentComplete) return; - QDeclarativeXmlQuery::instance()->abort(); + d->qmlXmlQuery.abort(); d->queryId = -1; int count = d->size; @@ -758,7 +755,7 @@ void QDeclarativeXmlListModel::reload() } if (!d->xml.isEmpty()) { - d->queryId = QDeclarativeXmlQuery::instance()->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects); + d->queryId = d->qmlXmlQuery.doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects); d->progress = 1.0; d->status = Ready; emit progressChanged(d->progress); @@ -778,9 +775,25 @@ void QDeclarativeXmlListModel::reload() this, SLOT(requestProgress(qint64,qint64))); } +#define XMLLISTMODEL_MAX_REDIRECT 16 + void QDeclarativeXmlListModel::requestFinished() { Q_D(QDeclarativeXmlListModel); + + d->redirectCount++; + if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) { + QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->reply->url().resolved(redirect.toUrl()); + d->reply->deleteLater(); + d->reply = 0; + setSource(url); + return; + } + } + d->redirectCount = 0; + if (d->reply->error() != QNetworkReply::NoError) { disconnect(d->reply, 0, this, 0); d->reply->deleteLater(); @@ -789,7 +802,7 @@ void QDeclarativeXmlListModel::requestFinished() } else { d->status = Ready; QByteArray data = d->reply->readAll(); - d->queryId = QDeclarativeXmlQuery::instance()->doQuery(d->query, d->namespaces, data, &d->roleObjects); + d->queryId = d->qmlXmlQuery.doQuery(d->query, d->namespaces, data, &d->roleObjects); disconnect(d->reply, 0, this, 0); d->reply->deleteLater(); d->reply = 0; @@ -815,10 +828,10 @@ void QDeclarativeXmlListModel::queryCompleted(int id, int size) return; bool sizeChanged = size != d->size; d->size = size; - d->data = QDeclarativeXmlQuery::instance()->modelData(); + d->data = d->qmlXmlQuery.modelData(); - QList<QDeclarativeXmlListRange> removed = QDeclarativeXmlQuery::instance()->removedItemRanges(); - QList<QDeclarativeXmlListRange> inserted = QDeclarativeXmlQuery::instance()->insertedItemRanges(); + QList<QDeclarativeXmlListRange> removed = d->qmlXmlQuery.removedItemRanges(); + QList<QDeclarativeXmlListRange> inserted = d->qmlXmlQuery.insertedItemRanges(); for (int i=0; i<removed.count(); i++) emit itemsRemoved(removed[i].first, removed[i].second); diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri index 26edecc..ddf00ea 100644 --- a/src/declarative/util/util.pri +++ b/src/declarative/util/util.pri @@ -27,7 +27,8 @@ SOURCES += \ $$PWD/qdeclarativepixmapcache.cpp \ $$PWD/qdeclarativebehavior.cpp \ $$PWD/qdeclarativefontloader.cpp \ - $$PWD/qdeclarativestyledtext.cpp + $$PWD/qdeclarativestyledtext.cpp \ + $$PWD/qdeclarativelistmodelworkeragent.cpp HEADERS += \ $$PWD/qdeclarativeutilmodule_p.h\ @@ -49,6 +50,7 @@ HEADERS += \ $$PWD/qdeclarativestategroup_p.h \ $$PWD/qdeclarativetransition_p.h \ $$PWD/qdeclarativelistmodel_p.h\ + $$PWD/qdeclarativelistmodel_p_p.h\ $$PWD/qdeclarativelistaccessor_p.h \ $$PWD/qdeclarativeopenmetaobject_p.h \ $$PWD/qdeclarativenullablevalue_p_p.h \ @@ -59,7 +61,9 @@ HEADERS += \ $$PWD/qdeclarativepixmapcache_p.h \ $$PWD/qdeclarativebehavior_p.h \ $$PWD/qdeclarativefontloader_p.h \ - $$PWD/qdeclarativestyledtext_p.h + $$PWD/qdeclarativestyledtext_p.h \ + $$PWD/qdeclarativelistmodelworkeragent_p.h \ + $$PWD/qdeclarativelistmodelworkeragent_p_p.h contains(QT_CONFIG, xmlpatterns) { QT+=xmlpatterns |