diff options
author | Thiago Macieira <thiago.macieira@nokia.com> | 2010-09-21 08:06:18 (GMT) |
---|---|---|
committer | Thiago Macieira <thiago.macieira@nokia.com> | 2010-09-21 08:06:18 (GMT) |
commit | a4d1be727573a7a87c523a2ef28074c77d16f165 (patch) | |
tree | d4f8f218c41771ca17c7ab324390485af868fa8e /src/declarative/util | |
parent | cd2fd21578a80bc5ac121c0419ee00b1799d0a60 (diff) | |
parent | 660ec910ef60513b511e2292255e53701dbb239b (diff) | |
download | Qt-a4d1be727573a7a87c523a2ef28074c77d16f165.zip Qt-a4d1be727573a7a87c523a2ef28074c77d16f165.tar.gz Qt-a4d1be727573a7a87c523a2ef28074c77d16f165.tar.bz2 |
Merge remote branch 'origin/4.7' into qt-master-from-4.7
Conflicts:
src/corelib/kernel/qobject.h
src/declarative/graphicsitems/qdeclarativeflickable.cpp
src/declarative/graphicsitems/qdeclarativeflickable_p_p.h
src/declarative/util/qdeclarativelistmodel.cpp
Diffstat (limited to 'src/declarative/util')
17 files changed, 1417 insertions, 271 deletions
diff --git a/src/declarative/util/qdeclarativeanimation.cpp b/src/declarative/util/qdeclarativeanimation.cpp index 6637282..3a96f98 100644 --- a/src/declarative/util/qdeclarativeanimation.cpp +++ b/src/declarative/util/qdeclarativeanimation.cpp @@ -2699,14 +2699,15 @@ void QDeclarativeParentAnimation::transition(QDeclarativeStateActions &actions, qreal scale = 1; qreal rotation = 0; - if (ok && transform.type() != QTransform::TxRotate) { + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { if (transform.m11() == transform.m22()) scale = transform.m11(); else { qmlInfo(this) << QDeclarativeParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); ok = false; } - } else if (ok && transform.type() == QTransform::TxRotate) { + } else if (ok && isRotate) { if (transform.m11() == transform.m22()) scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); else { diff --git a/src/declarative/util/qdeclarativefontloader.cpp b/src/declarative/util/qdeclarativefontloader.cpp index 1755855..6879494 100644 --- a/src/declarative/util/qdeclarativefontloader.cpp +++ b/src/declarative/util/qdeclarativefontloader.cpp @@ -58,23 +58,91 @@ QT_BEGIN_NAMESPACE +#define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16 + +class QDeclarativeFontObject : public QObject +{ +Q_OBJECT + +public: + QDeclarativeFontObject(int _id); + + void download(const QUrl &url, QNetworkAccessManager *manager); + +Q_SIGNALS: + void fontDownloaded(const QString&, QDeclarativeFontLoader::Status); + +private Q_SLOTS: + void replyFinished(); + +public: + int id; + +private: + QNetworkReply *reply; + int redirectCount; + + Q_DISABLE_COPY(QDeclarativeFontObject) +}; + +QDeclarativeFontObject::QDeclarativeFontObject(int _id = -1) + : QObject(0), id(_id), reply(0), redirectCount(0) {} + + +void QDeclarativeFontObject::download(const QUrl &url, QNetworkAccessManager *manager) +{ + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + reply = manager->get(req); + QObject::connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); +} + +void QDeclarativeFontObject::replyFinished() +{ + if (reply) { + redirectCount++; + if (redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkAccessManager *manager = reply->manager(); + reply->deleteLater(); + reply = 0; + download(url, manager); + return; + } + } + redirectCount = 0; + + if (!reply->error()) { + id = QFontDatabase::addApplicationFontFromData(reply->readAll()); + if (id != -1) + emit fontDownloaded(QFontDatabase::applicationFontFamilies(id).at(0), QDeclarativeFontLoader::Ready); + else + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } else { + emit fontDownloaded(QString(), QDeclarativeFontLoader::Error); + } + reply->deleteLater(); + reply = 0; + } +} + + class QDeclarativeFontLoaderPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QDeclarativeFontLoader) public: - QDeclarativeFontLoaderPrivate() : reply(0), status(QDeclarativeFontLoader::Null), redirectCount(0) {} - - void addFontToDatabase(const QByteArray &); + QDeclarativeFontLoaderPrivate() : status(QDeclarativeFontLoader::Null) {} QUrl url; QString name; - QNetworkReply *reply; QDeclarativeFontLoader::Status status; - int redirectCount; + static QHash<QUrl, QDeclarativeFontObject*> fonts; }; - +QHash<QUrl, QDeclarativeFontObject*> QDeclarativeFontLoaderPrivate::fonts; /*! \qmlclass FontLoader QDeclarativeFontLoader @@ -127,30 +195,61 @@ void QDeclarativeFontLoader::setSource(const QUrl &url) if (url == d->url) return; d->url = qmlContext(this)->resolvedUrl(url); - - d->status = Loading; - emit statusChanged(); emit sourceChanged(); + #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML - QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); - if (!lf.isEmpty()) { - int id = QFontDatabase::addApplicationFont(lf); - if (id != -1) { - d->name = QFontDatabase::applicationFontFamilies(id).at(0); - emit nameChanged(); - d->status = QDeclarativeFontLoader::Ready; + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!localFile.isEmpty()) { + if (!d->fonts.contains(d->url)) { + int id = QFontDatabase::addApplicationFont(localFile); + if (id != -1) { + updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready); + QDeclarativeFontObject *fo = new QDeclarativeFontObject(id); + d->fonts[d->url] = fo; + } else { + updateFontInfo(QString(), Error); + } } else { - d->status = QDeclarativeFontLoader::Error; - qmlInfo(this) << "Cannot load font: \"" << url.toString() << "\""; + updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready); } - emit statusChanged(); } else #endif { - QNetworkRequest req(d->url); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - d->reply = qmlEngine(this)->networkAccessManager()->get(req); - QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(replyFinished())); + if (!d->fonts.contains(d->url)) { + QDeclarativeFontObject *fo = new QDeclarativeFontObject; + d->fonts[d->url] = fo; + fo->download(d->url, qmlEngine(this)->networkAccessManager()); + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString, QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString, QDeclarativeFontLoader::Status))); + } else { + QDeclarativeFontObject *fo = d->fonts[d->url]; + if (fo->id == -1) { + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString, QDeclarativeFontLoader::Status)), + this, SLOT(updateFontInfo(QString, QDeclarativeFontLoader::Status))); + } + else + updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready); + } + } +} + +void QDeclarativeFontLoader::updateFontInfo(const QString& name, QDeclarativeFontLoader::Status status) +{ + Q_D(QDeclarativeFontLoader); + + if (name != d->name) { + d->name = name; + emit nameChanged(); + } + if (status != d->status) { + if (status == Error) + qmlInfo(this) << "Cannot load font: \"" << d->url.toString() << "\""; + d->status = status; + emit statusChanged(); } } @@ -177,7 +276,7 @@ QString QDeclarativeFontLoader::name() const void QDeclarativeFontLoader::setName(const QString &name) { Q_D(QDeclarativeFontLoader); - if (d->name == name ) + if (d->name == name) return; d->name = name; emit nameChanged(); @@ -223,52 +322,6 @@ 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; - qmlInfo(this) << "Cannot load font: \"" << d->reply->url().toString() << "\""; - emit statusChanged(); - } - d->reply->deleteLater(); - d->reply = 0; - } -} - -void QDeclarativeFontLoaderPrivate::addFontToDatabase(const QByteArray &ba) -{ - Q_Q(QDeclarativeFontLoader); - - int id = QFontDatabase::addApplicationFontFromData(ba); - if (id != -1) { - name = QFontDatabase::applicationFontFamilies(id).at(0); - emit q->nameChanged(); - status = QDeclarativeFontLoader::Ready; - } else { - status = QDeclarativeFontLoader::Error; - qmlInfo(q) << "Cannot load font: \"" << url.toString() << "\""; - } - emit q->statusChanged(); -} - QT_END_NAMESPACE + +#include <qdeclarativefontloader.moc> diff --git a/src/declarative/util/qdeclarativefontloader_p.h b/src/declarative/util/qdeclarativefontloader_p.h index 0344d99..bebd5a0 100644 --- a/src/declarative/util/qdeclarativefontloader_p.h +++ b/src/declarative/util/qdeclarativefontloader_p.h @@ -79,7 +79,7 @@ public: Status status() const; private Q_SLOTS: - void replyFinished(); + void updateFontInfo(const QString&, QDeclarativeFontLoader::Status); Q_SIGNALS: void sourceChanged(); diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp index c7d5024..5ce95e9 100644 --- a/src/declarative/util/qdeclarativelistmodel.cpp +++ b/src/declarative/util/qdeclarativelistmodel.cpp @@ -58,6 +58,28 @@ Q_DECLARE_METATYPE(QListModelInterface *) QT_BEGIN_NAMESPACE +template<typename T> +void qdeclarativelistmodel_move(int from, int to, int n, T *items) +{ + if (n == 1) { + items->move(from, to); + } else { + T replaced; + int i=0; + typename T::ConstIterator it=items->begin(); it += from+n; + for (; i<to-from; ++i,++it) + replaced.append(*it); + i=0; + it=items->begin(); it += from; + for (; i<n; ++i,++it) + replaced.append(*it); + typename T::ConstIterator f=replaced.begin(); + typename T::Iterator t=items->begin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } +} + QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const { return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); @@ -69,49 +91,67 @@ QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListM \since 4.7 \brief The ListModel element defines a free-form list data source. - The ListModel is a simple hierarchy of elements containing data roles. The contents can - be defined dynamically, or explicitly in QML: + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. - For example: + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. - \snippet doc/src/snippets/declarative/listmodel.qml 0 + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. - Roles (properties) must begin with a lower-case letter. The above example defines a - ListModel containing three elements, with the roles "name" and "cost". + \section1 Example Usage - Values must be simple constants - either strings (quoted and optionally within a call to QT_TR_NOOP), - bools (true, false), numbers, or enum values (like Text.AlignHCenter). + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \beginfloatright + \inlineimage listmodel.png + \endfloat + + \snippet doc/src/snippets/declarative/listmodel.qml 0 - The defined model can be used in views such as ListView: + \clearfloat + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: \snippet doc/src/snippets/declarative/listmodel-simple.qml 0 \dots 8 \snippet doc/src/snippets/declarative/listmodel-simple.qml 1 - \image listmodel.png - It is possible for roles to contain list data. In the example below we create a list of fruit attributes: + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: \snippet doc/src/snippets/declarative/listmodel-nested.qml model - The delegate below displays all the fruit attributes: + The delegate displays all the fruit attributes: - \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate - \image listmodel-nested.png + \beginfloatright + \inlineimage listmodel-nested.png + \endfloat + \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate - \section2 Modifying list models + \clearfloat + \section1 Modifying List Models The content of a ListModel may be created and modified using the clear(), append(), set() and setProperty() methods. For example: - - \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate - Note that when creating content dynamically the set of available properties cannot be changed - once set. Whatever properties are first added to the model are the - only permitted properties in the model. + \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. - \section2 Using threaded list models with WorkerScript + \section1 Using Threaded List Models with WorkerScript ListModel can be used together with WorkerScript access a list model from multiple threads. This is useful if list modifications are @@ -127,19 +167,19 @@ QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListM \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 -working-with-data - worker script by calling \l WorkerScript::sendMessage(). When this message - is received, \l {WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in - \tt dataloader.js, which appends the current time to the list model. + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. - Note the call to sync() from the \l {WorkerScript::onMessage}{WorkerScript.onMessage()} handler. - You must call sync() or else the changes made to the list from the external + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + handler. You must call sync() or else the changes made to the list from the external thread will not be reflected in the list model in the main thread. - \section3 Limitations + \section1 Restrictions - If a list model is to be accessed from a WorkerScript, it \bold cannot - contain list data. So, the following model cannot be used from a WorkerScript + If a list model is to be accessed from a WorkerScript, it cannot + contain list-type data. So, the following model cannot be used from a WorkerScript because of the list contained in the "attributes" property: \code @@ -156,7 +196,7 @@ working-with-data } \endcode - In addition, the WorkerScript cannot add any list data to the model. + In addition, the WorkerScript cannot add list-type data to the model. \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative */ @@ -177,17 +217,25 @@ working-with-data */ QDeclarativeListModel::QDeclarativeListModel(QObject *parent) -: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0), m_isWorkerCopy(false) +: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0) { } -QDeclarativeListModel::QDeclarativeListModel(bool workerCopy, QObject *parent) -: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0), m_isWorkerCopy(workerCopy) +QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent) +: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0) { - if (workerCopy) - m_flat = new FlatListModel(this); - else - m_nested = new NestedListModel(this); + m_flat = new FlatListModel(this); + m_flat->m_parentAgent = parent; + + if (orig->m_flat) { + m_flat->m_roles = orig->m_flat->m_roles; + m_flat->m_strings = orig->m_flat->m_strings; + m_flat->m_values = orig->m_flat->m_values; + + m_flat->m_nodeData.reserve(m_flat->m_values.count()); + for (int i=0; i<m_flat->m_values.count(); i++) + m_flat->m_nodeData << 0; + } } QDeclarativeListModel::~QDeclarativeListModel() @@ -223,19 +271,28 @@ bool QDeclarativeListModel::flatten() flat->m_strings.insert(s, roles[i]); } + flat->m_nodeData.reserve(flat->m_values.count()); + for (int i=0; i<flat->m_values.count(); i++) + flat->m_nodeData << 0; + m_flat = flat; delete m_nested; m_nested = 0; return true; } +bool QDeclarativeListModel::inWorkerThread() const +{ + return m_flat && m_flat->m_parentAgent; +} + QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent() { if (m_agent) return m_agent; if (!flatten()) { - qmlInfo(this) << "List contains nested list values and cannot be used from a worker script"; + qmlInfo(this) << "List contains list-type data and cannot be used from a worker script"; return 0; } @@ -293,12 +350,43 @@ void QDeclarativeListModel::clear() else m_nested->clear(); - if (!m_isWorkerCopy) { + if (!inWorkerThread()) { emit itemsRemoved(0, cleared); emit countChanged(); } } +QDeclarativeListModel *ModelNode::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 + + for (int i=0; i<values.count(); ++i) { + ModelNode *subNode = qvariant_cast<ModelNode *>(values.at(i)); + if (subNode) + subNode->m_model = modelCache->m_nested; + } + } + return modelCache; +} + +ModelObject *ModelNode::object(const NestedListModel *model) +{ + if (!objectCache) { + objectCache = new ModelObject(this, + const_cast<NestedListModel*>(model), + QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel))); + QHash<QString, ModelNode *>::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it)); + } + objectCache->setNodeUpdatesEnabled(true); + } + return objectCache; +} + /*! \qmlmethod ListModel::remove(int index) @@ -318,7 +406,7 @@ void QDeclarativeListModel::remove(int index) else m_nested->remove(index); - if (!m_isWorkerCopy) { + if (!inWorkerThread()) { emit itemsRemoved(index, 1); emit countChanged(); } @@ -352,7 +440,7 @@ void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap) } bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap); - if (ok && !m_isWorkerCopy) { + if (ok && !inWorkerThread()) { emit itemsInserted(index, 1); emit countChanged(); } @@ -376,7 +464,7 @@ void QDeclarativeListModel::move(int from, int to, int n) { if (n==0 || from==to) return; - if (from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0) { + if (!canMove(from, to, n)) { qmlInfo(this) << tr("move: out of range"); return; } @@ -398,7 +486,7 @@ void QDeclarativeListModel::move(int from, int to, int n) else m_nested->move(from, to, n); - if (!m_isWorkerCopy) + if (!inWorkerThread()) emit itemsMoved(origfrom, origto, orign); } @@ -427,11 +515,15 @@ void QDeclarativeListModel::append(const QScriptValue& valuemap) /*! \qmlmethod object ListModel::get(int index) - Returns the item at \a index in the list model. + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: \code - fruitModel.append({"cost": 5.95, "name":"Jackfruit"}) - fruitModel.get(0).cost + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } \endcode The \a index must be an element in the list. @@ -446,6 +538,9 @@ void QDeclarativeListModel::append(const QScriptValue& valuemap) fruitModel.get(0).attributes.get(1).value; // == "green" \endcode + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + \sa append() */ QScriptValue QDeclarativeListModel::get(int index) const @@ -489,7 +584,7 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) else m_nested->set(index, valuemap, &roles); - if (!m_isWorkerCopy) + if (!inWorkerThread()) emit itemsChanged(index, 1, roles); } } @@ -520,7 +615,7 @@ void QDeclarativeListModel::setProperty(int index, const QString& property, cons else m_nested->setProperty(index, property, value, &roles); - if (!m_isWorkerCopy) + if (!inWorkerThread()) emit itemsChanged(index, 1, roles); } @@ -687,7 +782,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray & { QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj); - ModelNode *root = new ModelNode; + ModelNode *root = new ModelNode(rv->m_nested); rv->m_nested->_root = root; QStack<ModelNode *> nodes; nodes << root; @@ -704,7 +799,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray & case ListInstruction::Push: { ModelNode *n = nodes.top(); - ModelNode *n2 = new ModelNode; + ModelNode *n2 = new ModelNode(rv->m_nested); n->values << QVariant::fromValue(n2); nodes.push(n2); if (processingSet) @@ -743,7 +838,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray & case ListInstruction::Set: { ModelNode *n = nodes.top(); - ModelNode *n2 = new ModelNode; + ModelNode *n2 = new ModelNode(rv->m_nested); n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2); nodes.push(n2); processingSet = true; @@ -751,6 +846,13 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray & break; } } + + ModelNode *rootNode = rv->m_nested->_root; + for (int i=0; i<rootNode->values.count(); ++i) { + ModelNode *node = qvariant_cast<ModelNode *>(rootNode->values[i]); + node->listIndex = i; + node->updateListIndexes(); + } } bool QDeclarativeListModelParser::definesEmptyList(const QString &s) @@ -765,22 +867,57 @@ bool QDeclarativeListModelParser::definesEmptyList(const QString &s) return false; } + /*! \qmlclass ListElement QDeclarativeListElement \ingroup qml-working-with-data \since 4.7 \brief The ListElement element defines a data item in a ListModel. + List elements are defined inside ListModel definitions, and represent items in a + list that will be displayed using ListView or \l Repeater items. + + List elements are defined like other QML elements except that they contain + a collection of \e role definitions instead of properties. Using the same + syntax as property definitions, roles both define how the data is accessed + and include the data itself. + + The names used for roles must begin with a lower-case letter and should be + common to all elements in a given model. Values must be simple constants; either + strings (quoted and optionally within a call to QT_TR_NOOP), boolean values + (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter). + + \section1 Referencing Roles + + The role names are used by delegates to obtain data from list elements. + Each role name is accessible in the delegate's scope, and refers to the + corresponding role in the current element. Where a role name would be + ambiguous to use, it can be accessed via the \l{ListView::}{model} + property (e.g., \c{model.cost} instead of \c{cost}). + + \section1 Example Usage + + The following model defines a series of list elements, each of which + contain "name" and "cost" roles and their associated values. + + \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml model + + The delegate obtains the name and cost for each element by simply referring + to \c name and \c cost: + + \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view + \sa ListModel */ FlatListModel::FlatListModel(QDeclarativeListModel *base) - : m_scriptEngine(0), m_listModel(base) + : m_scriptEngine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0) { } FlatListModel::~FlatListModel() { + qDeleteAll(m_nodeData); } QHash<int,QVariant> FlatListModel::data(int index, const QList<int> &roles) const @@ -824,11 +961,15 @@ int FlatListModel::count() const void FlatListModel::clear() { m_values.clear(); + + qDeleteAll(m_nodeData); + m_nodeData.clear(); } void FlatListModel::remove(int index) { m_values.removeAt(index); + removedNode(index); } bool FlatListModel::append(const QScriptValue &value) @@ -845,6 +986,8 @@ bool FlatListModel::insert(int index, const QScriptValue &value) return false; m_values.insert(index, row); + insertedNode(index); + return true; } @@ -858,13 +1001,17 @@ QScriptValue FlatListModel::get(int index) const if (index < 0 || index >= m_values.count()) return scriptEngine->undefinedValue(); - QScriptValue rv = scriptEngine->newObject(); + FlatListModel *that = const_cast<FlatListModel*>(this); + if (!m_scriptClass) + that->m_scriptClass = new FlatListScriptClass(that, scriptEngine); - 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()), scriptEngine->toScriptValue(iter.value())); + FlatNodeData *data = m_nodeData.value(index); + if (!data) { + data = new FlatNodeData(index); + that->m_nodeData.replace(index, data); + } - return rv; + return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data)); } void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles) @@ -896,23 +1043,8 @@ void FlatListModel::setProperty(int index, const QString& property, const QVaria 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; - } + qdeclarativelistmodel_move<QList<QHash<int, QVariant> > >(from, to, n, &m_values); + moveNodes(from, to, n); } bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles) @@ -922,7 +1054,7 @@ bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *ro it.next(); QScriptValue value = it.value(); if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { - qmlInfo(m_listModel) << "Cannot add nested list values when modifying or after modification from a worker script"; + qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; return false; } @@ -942,6 +1074,139 @@ bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *ro return true; } +void FlatListModel::insertedNode(int index) +{ + if (index >= 0 && index <= m_values.count()) { + m_nodeData.insert(index, 0); + + for (int i=index + 1; i<m_nodeData.count(); i++) { + if (m_nodeData[i]) + m_nodeData[i]->index = i; + } + } +} + +void FlatListModel::removedNode(int index) +{ + if (index >= 0 && index < m_nodeData.count()) { + delete m_nodeData.takeAt(index); + + for (int i=index; i<m_nodeData.count(); i++) { + if (m_nodeData[i]) + m_nodeData[i]->index = i; + } + } +} + +void FlatListModel::moveNodes(int from, int to, int n) +{ + if (!m_listModel->canMove(from, to, n)) + return; + + qdeclarativelistmodel_move<QList<FlatNodeData *> >(from, to, n, &m_nodeData); + + for (int i=from; i<from + (to-from); i++) { + if (m_nodeData[i]) + m_nodeData[i]->index = i; + } +} + + + +FlatNodeData::~FlatNodeData() +{ + for (QSet<FlatNodeObjectData *>::Iterator iter = objects.begin(); iter != objects.end(); ++iter) { + FlatNodeObjectData *data = *iter; + data->nodeData = 0; + } +} + +void FlatNodeData::addData(FlatNodeObjectData *data) +{ + objects.insert(data); +} + +void FlatNodeData::removeData(FlatNodeObjectData *data) +{ + objects.remove(data); +} + + +FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng) + : QScriptDeclarativeClass(seng), + m_model(model) +{ +} + +QScriptDeclarativeClass::Value FlatListScriptClass::property(Object *obj, const Identifier &name) +{ + FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj); + if (!objData->nodeData) // item at this index has been deleted + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); + + int index = objData->nodeData->index; + QString propName = toString(name); + int role = m_model->m_strings.value(propName, -1); + + if (role >= 0 && index >=0 ) { + const QHash<int, QVariant> &row = m_model->m_values[index]; + QScriptValue sv = engine()->toScriptValue<QVariant>(row[role]); + return QScriptDeclarativeClass::Value(engine(), sv); + } + + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); +} + +void FlatListScriptClass::setProperty(Object *obj, const Identifier &name, const QScriptValue &value) +{ + if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return; + } + + FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj); + if (!objData->nodeData) // item at this index has been deleted + return; + + int index = objData->nodeData->index; + QString propName = toString(name); + + int role = m_model->m_strings.value(propName, -1); + if (role >= 0 && index >= 0) { + QHash<int, QVariant> &row = m_model->m_values[index]; + row[role] = value.toVariant(); + + if (m_model->m_parentAgent) { + // This is the list in the worker thread, so tell the agent to + // emit itemsChanged() later + m_model->m_parentAgent->changedData(index, 1); + } else { + // This is the list in the main thread, so emit itemsChanged() + QList<int> roles; + roles << role; + emit m_model->m_listModel->itemsChanged(index, 1, roles); + } + } +} + +QScriptClass::QueryFlags FlatListScriptClass::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags) +{ + return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess); +} + +bool FlatListScriptClass::compare(Object *obj1, Object *obj2) +{ + FlatNodeObjectData *data1 = static_cast<FlatNodeObjectData*>(obj1); + FlatNodeObjectData *data2 = static_cast<FlatNodeObjectData*>(obj2); + + if (!data1->nodeData || !data2->nodeData) + return false; + + return data1->nodeData->index == data2->nodeData->index; +} + + + NestedListModel::NestedListModel(QDeclarativeListModel *base) : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false) { @@ -1067,11 +1332,12 @@ void NestedListModel::remove(int index) bool NestedListModel::insert(int index, const QScriptValue& valuemap) { if (!_root) { - _root = new ModelNode; + _root = new ModelNode(this); m_ownsRoot = true; } - ModelNode *mn = new ModelNode; + ModelNode *mn = new ModelNode(this); + mn->listIndex = index; mn->setObjectValue(valuemap); _root->values.insert(index,QVariant::fromValue(mn)); return true; @@ -1079,34 +1345,19 @@ bool NestedListModel::insert(int index, const QScriptValue& valuemap) 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; - } + if (!_root) + return; + qdeclarativelistmodel_move<QVariantList>(from, to, n, &_root->values); } bool NestedListModel::append(const QScriptValue& valuemap) { if (!_root) { - _root = new ModelNode; + _root = new ModelNode(this); m_ownsRoot = true; } - ModelNode *mn = new ModelNode; - mn->setObjectValue(valuemap); - _root->values << QVariant::fromValue(mn); + + insert(count(), valuemap); return true; } @@ -1201,8 +1452,8 @@ QString NestedListModel::toString(int role) const } -ModelNode::ModelNode() -: modelCache(0), objectCache(0), isArray(false) +ModelNode::ModelNode(NestedListModel *model) +: modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1) { } @@ -1226,18 +1477,18 @@ void ModelNode::clear() properties.clear(); } -void ModelNode::setObjectValue(const QScriptValue& valuemap) { +void ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache) { QScriptValueIterator it(valuemap); while (it.hasNext()) { it.next(); - ModelNode *value = new ModelNode; + ModelNode *value = new ModelNode(m_model); QScriptValue v = it.value(); if (v.isArray()) { value->isArray = true; value->setListValue(v); } else { value->values << v.toVariant(); - if (objectCache) + if (writeToCache && objectCache) objectCache->setValue(it.name().toUtf8(), value->values.last()); } if (properties.contains(it.name())) @@ -1250,14 +1501,16 @@ void ModelNode::setListValue(const QScriptValue& valuelist) { values.clear(); int size = valuelist.property(QLatin1String("length")).toInt32(); for (int i=0; i<size; i++) { - ModelNode *value = new ModelNode; + ModelNode *value = new ModelNode(m_model); QScriptValue v = valuelist.property(i); if (v.isArray()) { value->isArray = true; value->setListValue(v); } else if (v.isObject()) { + value->listIndex = i; value->setObjectValue(v); } else { + value->listIndex = i; value->values << v.toVariant(); } values.append(QVariant::fromValue(value)); @@ -1269,7 +1522,7 @@ void ModelNode::setProperty(const QString& prop, const QVariant& val) { if (it != properties.end()) { (*it)->values[0] = val; } else { - ModelNode *n = new ModelNode; + ModelNode *n = new ModelNode(m_model); n->values << val; properties.insert(prop,n); } @@ -1277,6 +1530,40 @@ void ModelNode::setProperty(const QString& prop, const QVariant& val) { objectCache->setValue(prop.toUtf8(), val); } +void ModelNode::updateListIndexes() +{ + for (QHash<QString, ModelNode *>::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) { + ModelNode *node = iter.value(); + if (node->isArray) { + for (int i=0; i<node->values.count(); ++i) { + ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(i)); + if (subNode) + subNode->listIndex = i; + } + } + node->updateListIndexes(); + } +} + +/* + Need to call this to emit itemsChanged() for modifications outside of set() + and setProperty(), i.e. if an item returned from get() is modified +*/ +void ModelNode::changedProperty(const QString &name) const +{ + if (listIndex < 0) + return; + + m_model->checkRoles(); + QList<int> roles; + int role = m_model->roleStrings.indexOf(name); + if (role < 0) + roles = m_model->roles(); + else + roles << role; + emit m_model->m_listModel->itemsChanged(listIndex, 1, roles); +} + void ModelNode::dump(ModelNode *node, int ind) { QByteArray indentBa(ind * 4, ' '); @@ -1298,16 +1585,47 @@ void ModelNode::dump(ModelNode *node, int ind) } } -ModelObject::ModelObject() -: _mo(new QDeclarativeOpenMetaObject(this)) +ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng) + : m_model(model), + m_node(node), + m_meta(new ModelNodeMetaObject(seng, this)) { } void ModelObject::setValue(const QByteArray &name, const QVariant &val) { - _mo->setValue(name, val); + m_meta->setValue(name, val); setProperty(name.constData(), val); } +void ModelObject::setNodeUpdatesEnabled(bool enable) +{ + m_meta->m_enabled = enable; +} + + +ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object) + : QDeclarativeOpenMetaObject(object), + m_enabled(false), + m_seng(seng), + m_obj(object) +{ +} + +void ModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QString propName = QString::fromUtf8(name(index)); + QVariant value = operator[](index); + + QScriptValue sv = m_seng->newObject(); + sv.setProperty(propName, m_seng->newVariant(value)); + m_obj->m_node->setObjectValue(sv, false); + + m_obj->m_node->changedProperty(propName); +} + QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativelistmodel_p.h b/src/declarative/util/qdeclarativelistmodel_p.h index 6aff9c6..fe42ef6 100644 --- a/src/declarative/util/qdeclarativelistmodel_p.h +++ b/src/declarative/util/qdeclarativelistmodel_p.h @@ -63,6 +63,7 @@ class FlatListModel; class NestedListModel; class QDeclarativeListModelWorkerAgent; struct ModelNode; +class FlatListScriptClass; class Q_DECLARATIVE_EXPORT QDeclarativeListModel : public QListModelInterface { Q_OBJECT @@ -96,16 +97,21 @@ Q_SIGNALS: private: friend class QDeclarativeListModelParser; friend class QDeclarativeListModelWorkerAgent; + friend class FlatListModel; + friend class FlatListScriptClass; friend struct ModelNode; - QDeclarativeListModel(bool workerCopy, QObject *parent=0); + // Constructs a flat list model for a worker agent + QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent); + bool flatten(); - bool modifyCheck(); + bool inWorkerThread() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } 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 index 8231414..d2d40ee 100644 --- a/src/declarative/util/qdeclarativelistmodel_p_p.h +++ b/src/declarative/util/qdeclarativelistmodel_p_p.h @@ -54,9 +54,11 @@ // #include "private/qdeclarativelistmodel_p.h" - -#include "qdeclarative.h" #include "private/qdeclarativeengine_p.h" +#include "private/qdeclarativeopenmetaobject_p.h" +#include "qdeclarative.h" + +#include <private/qscriptdeclarativeclass_p.h> QT_BEGIN_HEADER @@ -68,6 +70,8 @@ class QDeclarativeOpenMetaObject; class QScriptEngine; class QDeclarativeListModelWorkerAgent; struct ModelNode; +class FlatListScriptClass; +class FlatNodeData; class FlatListModel { @@ -94,16 +98,82 @@ public: private: friend class QDeclarativeListModelWorkerAgent; friend class QDeclarativeListModel; + friend class FlatListScriptClass; + friend class FlatNodeData; bool addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles); + void insertedNode(int index); + void removedNode(int index); + void moveNodes(int from, int to, int n); QScriptEngine *m_scriptEngine; QHash<int, QString> m_roles; QHash<QString, int> m_strings; QList<QHash<int, QVariant> > m_values; QDeclarativeListModel *m_listModel; + + FlatListScriptClass *m_scriptClass; + QList<FlatNodeData *> m_nodeData; + QDeclarativeListModelWorkerAgent *m_parentAgent; +}; + + +/* + Created when get() is called on a FlatListModel. This allows changes to the + object returned by get() to be tracked, and passed onto the model. +*/ +class FlatListScriptClass : public QScriptDeclarativeClass +{ +public: + FlatListScriptClass(FlatListModel *model, QScriptEngine *seng); + + Value property(Object *, const Identifier &); + void setProperty(Object *, const Identifier &name, const QScriptValue &); + QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags flags); + bool compare(Object *, Object *); + +private: + FlatListModel *m_model; +}; + +/* + FlatNodeData and FlatNodeObjectData allow objects returned by get() to still + point to the correct list index if move(), insert() or remove() are called. +*/ +struct FlatNodeObjectData; +class FlatNodeData +{ +public: + FlatNodeData(int i) + : index(i) {} + + ~FlatNodeData(); + + void addData(FlatNodeObjectData *data); + void removeData(FlatNodeObjectData *data); + + int index; + +private: + QSet<FlatNodeObjectData*> objects; +}; + +struct FlatNodeObjectData : public QScriptDeclarativeClass::Object +{ + FlatNodeObjectData(FlatNodeData *data) : nodeData(data) { + nodeData->addData(this); + } + + ~FlatNodeObjectData() { + if (nodeData) + nodeData->removeData(this); + } + + FlatNodeData *nodeData; }; + + class NestedListModel { public: @@ -134,25 +204,50 @@ public: QDeclarativeListModel *m_listModel; private: + friend struct ModelNode; mutable QStringList roleStrings; mutable bool _rolesOk; }; +class ModelNodeMetaObject; class ModelObject : public QObject { Q_OBJECT public: - ModelObject(); + ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng); void setValue(const QByteArray &name, const QVariant &val); + void setNodeUpdatesEnabled(bool enable); + + NestedListModel *m_model; + ModelNode *m_node; private: - QDeclarativeOpenMetaObject *_mo; + ModelNodeMetaObject *m_meta; }; +class ModelNodeMetaObject : public QDeclarativeOpenMetaObject +{ +public: + ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + QScriptEngine *m_seng; + ModelObject *m_obj; +}; + + +/* + A ModelNode is created for each item in a NestedListModel. +*/ struct ModelNode { - ModelNode(); + ModelNode(NestedListModel *model); ~ModelNode(); QList<QVariant> values; @@ -160,35 +255,22 @@ struct ModelNode void clear(); - 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; - } + QDeclarativeListModel *model(const NestedListModel *model); + ModelObject *object(const NestedListModel *model); - - void setObjectValue(const QScriptValue& valuemap); + void setObjectValue(const QScriptValue& valuemap, bool writeToCache = true); void setListValue(const QScriptValue& valuelist); void setProperty(const QString& prop, const QVariant& val); + void changedProperty(const QString &name) const; + void updateListIndexes(); static void dump(ModelNode *node, int ind); QDeclarativeListModel *modelCache; ModelObject *objectCache; bool isArray; + + NestedListModel *m_model; + int listIndex; // only used for top-level nodes within a list }; diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp index d9df169..6804d4a 100644 --- a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp +++ b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp @@ -83,11 +83,11 @@ void QDeclarativeListModelWorkerAgent::Data::changedChange(int index, int count) } QDeclarativeListModelWorkerAgent::QDeclarativeListModelWorkerAgent(QDeclarativeListModel *model) -: m_engine(0), m_ref(1), m_orig(model), m_copy(new QDeclarativeListModel(true, this)) + : m_engine(0), + m_ref(1), + m_orig(model), + m_copy(new QDeclarativeListModel(model, 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() @@ -194,6 +194,11 @@ void QDeclarativeListModelWorkerAgent::sync() mutex.unlock(); } +void QDeclarativeListModelWorkerAgent::changedData(int index, int count) +{ + data.changedChange(index, count); +} + bool QDeclarativeListModelWorkerAgent::event(QEvent *e) { if (e->type() == QEvent::User) { @@ -216,6 +221,24 @@ bool QDeclarativeListModelWorkerAgent::event(QEvent *e) orig->m_strings = copy->m_strings; orig->m_values = copy->m_values; + // update the orig->m_nodeData list + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + orig->insertedNode(change.index); + break; + case Change::Removed: + orig->removedNode(change.index); + break; + case Change::Moved: + orig->moveNodes(change.index, change.to, change.count); + break; + case Change::Changed: + break; + } + } + syncDone.wakeAll(); locker.unlock(); @@ -232,7 +255,7 @@ bool QDeclarativeListModelWorkerAgent::event(QEvent *e) emit m_orig->itemsMoved(change.index, change.to, change.count); break; case Change::Changed: - emit m_orig->itemsChanged(change.index, change.to, orig->m_roles.keys()); + emit m_orig->itemsChanged(change.index, change.count, orig->m_roles.keys()); break; } } diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h index 01da374..10c3bca 100644 --- a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h +++ b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h @@ -67,6 +67,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QDeclarativeListModel; +class FlatListScriptClass; class QDeclarativeListModelWorkerAgent : public QObject { @@ -115,6 +116,7 @@ protected: private: friend class QDeclarativeWorkerScriptEnginePrivate; + friend class FlatListScriptClass; QScriptEngine *m_engine; struct Change { @@ -141,6 +143,8 @@ private: QDeclarativeListModel *list; }; + void changedData(int index, int count); + QAtomicInt m_ref; QDeclarativeListModel *m_orig; QDeclarativeListModel *m_copy; diff --git a/src/declarative/util/qdeclarativepixmapcache_p.h b/src/declarative/util/qdeclarativepixmapcache_p.h index b4d88bd..2e83cc4 100644 --- a/src/declarative/util/qdeclarativepixmapcache_p.h +++ b/src/declarative/util/qdeclarativepixmapcache_p.h @@ -98,7 +98,7 @@ public: bool connectDownloadProgress(QObject *, int); private: - Q_DISABLE_COPY(QDeclarativePixmap); + Q_DISABLE_COPY(QDeclarativePixmap) QDeclarativePixmapData *d; }; diff --git a/src/declarative/util/qdeclarativepropertychanges.cpp b/src/declarative/util/qdeclarativepropertychanges.cpp index e897458..8d01b80 100644 --- a/src/declarative/util/qdeclarativepropertychanges.cpp +++ b/src/declarative/util/qdeclarativepropertychanges.cpp @@ -52,6 +52,7 @@ #include <qdeclarativeguard_p.h> #include <qdeclarativeproperty_p.h> #include <qdeclarativecontext_p.h> +#include <qdeclarativestate_p_p.h> #include <QtCore/qdebug.h> @@ -200,14 +201,14 @@ public: }; -class QDeclarativePropertyChangesPrivate : public QObjectPrivate +class QDeclarativePropertyChangesPrivate : public QDeclarativeStateOperationPrivate { Q_DECLARE_PUBLIC(QDeclarativePropertyChanges) public: - QDeclarativePropertyChangesPrivate() : object(0), decoded(true), restore(true), + QDeclarativePropertyChangesPrivate() : decoded(true), restore(true), isExplicit(false) {} - QObject *object; + QDeclarativeGuard<QObject> object; QByteArray data; bool decoded : 1; @@ -497,4 +498,272 @@ void QDeclarativePropertyChanges::setIsExplicit(bool e) d->isExplicit = e; } +bool QDeclarativePropertyChanges::containsValue(const QByteArray &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QByteArray, QVariant> PropertyEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsExpression(const QByteArray &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QByteArray, QDeclarativeExpression *> ExpressionEntry; + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.first == name) { + return true; + } + } + + return false; +} + +bool QDeclarativePropertyChanges::containsProperty(const QByteArray &name) const +{ + return containsValue(name) || containsExpression(name); +} + +void QDeclarativePropertyChanges::changeValue(const QByteArray &name, const QVariant &value) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair<QByteArray, QVariant> PropertyEntry; + typedef QPair<QByteArray, QDeclarativeExpression *> ExpressionEntry; + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.first == name) { + expressionIterator.remove(); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + d->property(name).write(value); + } + + d->properties.append(PropertyEntry(name, value)); + return; + } + } + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + entry.second = value; + if (state() && state()->isStateActive()) + d->property(name).write(value); + return; + } + } + + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = QString::fromUtf8(name); + action.toValue = value; + + propertyIterator.insert(PropertyEntry(name, value)); + if (state() && state()->isStateActive()) { + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + d->property(name).write(value); + } +} + +void QDeclarativePropertyChanges::changeExpression(const QByteArray &name, const QString &expression) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair<QByteArray, QVariant> PropertyEntry; + typedef QPair<QByteArray, QDeclarativeExpression *> ExpressionEntry; + + bool hadValue = false; + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + hadValue = true; + break; + } + } + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.first == name) { + entry.second->setExpression(expression); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + return; + } + } + + QDeclarativeExpression *newExpression = new QDeclarativeExpression(qmlContext(this), d->object, expression); + expressionIterator.insert(ExpressionEntry(name, newExpression)); + + if (state() && state()->isStateActive()) { + if (hadValue) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + state()->changeBindingInRevertList(object(), name, oldBinding); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } else { + QDeclarativeAction action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = QString::fromUtf8(name); + + + if (d->isExplicit) { + action.toValue = newExpression->evaluate(); + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(newExpression->expression(), object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + action.toBinding = newBinding; + action.deletableToBinding = true; + + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + + QDeclarativePropertyPrivate::setBinding(action.property, newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + } + } + // what about the signal handler? +} + +QVariant QDeclarativePropertyChanges::property(const QByteArray &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QByteArray, QVariant> PropertyEntry; + typedef QPair<QByteArray, QDeclarativeExpression *> ExpressionEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.first == name) { + return QVariant(entry.second->expression()); + } + } + + return QVariant(); +} + +void QDeclarativePropertyChanges::removeProperty(const QByteArray &name) +{ + Q_D(QDeclarativePropertyChanges); + typedef QPair<QByteArray, QVariant> PropertyEntry; + typedef QPair<QByteArray, QDeclarativeExpression *> ExpressionEntry; + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.first == name) { + expressionIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } +} + +QVariant QDeclarativePropertyChanges::value(const QByteArray &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QByteArray, QVariant> PropertyEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + return QVariant(); +} + +QString QDeclarativePropertyChanges::expression(const QByteArray &name) const +{ + Q_D(const QDeclarativePropertyChanges); + typedef QPair<QByteArray, QDeclarativeExpression *> ExpressionEntry; + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.first == name) { + return entry.second->expression(); + } + } + + return QString(); +} + +void QDeclarativePropertyChanges::detachFromState() +{ + if (state()) + state()->removeAllEntriesFromRevertList(object()); +} + +void QDeclarativePropertyChanges::attachToState() +{ + if (state()) + state()->addEntriesToRevertList(actions()); +} + QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepropertychanges_p.h b/src/declarative/util/qdeclarativepropertychanges_p.h index 8578086..199928f 100644 --- a/src/declarative/util/qdeclarativepropertychanges_p.h +++ b/src/declarative/util/qdeclarativepropertychanges_p.h @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QDeclarativePropertyChangesPrivate; -class Q_AUTOTEST_EXPORT QDeclarativePropertyChanges : public QDeclarativeStateOperation +class Q_DECLARATIVE_EXPORT QDeclarativePropertyChanges : public QDeclarativeStateOperation { Q_OBJECT Q_DECLARE_PRIVATE(QDeclarativePropertyChanges) @@ -74,6 +74,20 @@ public: void setIsExplicit(bool); virtual ActionList actions(); + + bool containsProperty(const QByteArray &name) const; + bool containsValue(const QByteArray &name) const; + bool containsExpression(const QByteArray &name) const; + void changeValue(const QByteArray &name, const QVariant &value); + void changeExpression(const QByteArray &name, const QString &expression); + void removeProperty(const QByteArray &name); + QVariant value(const QByteArray &name) const; + QString expression(const QByteArray &name) const; + + void detachFromState(); + void attachToState(); + + QVariant property(const QByteArray &name) const; }; class QDeclarativePropertyChangesParser : public QDeclarativeCustomParser diff --git a/src/declarative/util/qdeclarativestate.cpp b/src/declarative/util/qdeclarativestate.cpp index 1ed7923..0f5413e 100644 --- a/src/declarative/util/qdeclarativestate.cpp +++ b/src/declarative/util/qdeclarativestate.cpp @@ -304,7 +304,7 @@ void QDeclarativeStatePrivate::complete() for (int ii = 0; ii < reverting.count(); ++ii) { for (int jj = 0; jj < revertList.count(); ++jj) { - if (revertList.at(jj).property == reverting.at(ii)) { + if (revertList.at(jj).property() == reverting.at(ii)) { revertList.removeAt(jj); break; } @@ -370,6 +370,192 @@ void QDeclarativeAction::deleteFromBinding() } } +bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QByteArray &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name) + return true; + } + } + + return false; +} + +bool QDeclarativeState::changeValueInRevertList(QObject *target, const QByteArray &name, const QVariant &revertValue) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name) { + simpleAction.setValue(revertValue); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QByteArray &name, QDeclarativeAbstractBinding *binding) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name) { + if (simpleAction.binding()) + simpleAction.binding()->destroy(); + + simpleAction.setBinding(binding); + return true; + } + } + } + + return false; +} + +bool QDeclarativeState::removeEntryFromRevertList(QObject *target, const QByteArray &name) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target && simpleAction.property().name().toUtf8() == name) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + return true; + } + } + } + + return false; +} + +void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action) +{ + Q_D(QDeclarativeState); + + QDeclarativeSimpleAction simpleAction(action); + + d->revertList.append(simpleAction); +} + +void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target) +{ + Q_D(QDeclarativeState); + + if (isStateActive()) { + QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + } + } + } +} + +void QDeclarativeState::addEntriesToRevertList(const QList<QDeclarativeAction> &actionList) +{ + Q_D(QDeclarativeState); + if (isStateActive()) { + QList<QDeclarativeSimpleAction> simpleActionList; + + QListIterator<QDeclarativeAction> actionListIterator(actionList); + while(actionListIterator.hasNext()) { + const QDeclarativeAction &action = actionListIterator.next(); + QDeclarativeSimpleAction simpleAction(action); + action.property.write(action.toValue); + if (action.toBinding) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding, QDeclarativePropertyPrivate::DontRemoveBinding); + } + + simpleActionList.append(simpleAction); + } + + d->revertList.append(simpleActionList); + } +} + +QVariant QDeclarativeState::valueInRevertList(QObject *target, const QByteArray &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name) + return simpleAction.value(); + } + } + + return QVariant(); +} + +QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QByteArray &name) const +{ + Q_D(const QDeclarativeState); + + if (isStateActive()) { + QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarativeSimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name) + return simpleAction.binding(); + } + } + + return 0; +} + +bool QDeclarativeState::isStateActive() const +{ + return stateGroup() && stateGroup()->state() == name(); +} + void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransition *trans, QDeclarativeState *revert) { Q_D(QDeclarativeState); @@ -403,13 +589,13 @@ void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransit continue; bool found = false; for (int jj = 0; jj < d->revertList.count(); ++jj) { - QDeclarativeActionEvent *event = d->revertList.at(jj).event; + 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->needsCopy()) { - action.event->copyOriginals(d->revertList.at(jj).event); + if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) { + action.event->copyOriginals(d->revertList.at(jj).event()); QDeclarativeSimpleAction r(action); additionalReverts << r; @@ -434,9 +620,9 @@ void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransit action.fromBinding = QDeclarativePropertyPrivate::binding(action.property); for (int jj = 0; jj < d->revertList.count(); ++jj) { - if (d->revertList.at(jj).property == action.property) { + if (d->revertList.at(jj).property() == action.property) { found = true; - if (d->revertList.at(jj).binding != action.fromBinding) { + if (d->revertList.at(jj).binding() != action.fromBinding) { action.deleteFromBinding(); } break; @@ -445,7 +631,7 @@ void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransit if (!found) { if (!action.restore) { - action.deleteFromBinding(); + action.deleteFromBinding();; } else { // Only need to revert the applyList action if the previous // state doesn't have a higher priority revert already @@ -460,8 +646,8 @@ void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransit // into this state need to be translated into apply actions for (int ii = 0; ii < d->revertList.count(); ++ii) { bool found = false; - if (d->revertList.at(ii).event) { - QDeclarativeActionEvent *event = d->revertList.at(ii).event; + if (d->revertList.at(ii).event()) { + QDeclarativeActionEvent *event = d->revertList.at(ii).event(); if (!event->isReversable()) continue; for (int jj = 0; !found && jj < applyList.count(); ++jj) { @@ -474,31 +660,31 @@ void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransit } else { for (int jj = 0; !found && jj < applyList.count(); ++jj) { const QDeclarativeAction &action = applyList.at(jj); - if (action.property == d->revertList.at(ii).property) + if (action.property == d->revertList.at(ii).property()) found = true; } } if (!found) { - QVariant cur = d->revertList.at(ii).property.read(); + QVariant cur = d->revertList.at(ii).property().read(); QDeclarativeAbstractBinding *delBinding = - QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property, 0); + QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0); if (delBinding) delBinding->destroy(); QDeclarativeAction a; - a.property = d->revertList.at(ii).property; + a.property = d->revertList.at(ii).property(); a.fromValue = cur; - a.toValue = d->revertList.at(ii).value; - a.toBinding = d->revertList.at(ii).binding; - a.specifiedObject = d->revertList.at(ii).specifiedObject; - a.specifiedProperty = d->revertList.at(ii).specifiedProperty; - a.event = d->revertList.at(ii).event; - a.reverseEvent = d->revertList.at(ii).reverseEvent; + a.toValue = d->revertList.at(ii).value(); + a.toBinding = d->revertList.at(ii).binding(); + a.specifiedObject = d->revertList.at(ii).specifiedObject(); + a.specifiedProperty = d->revertList.at(ii).specifiedProperty(); + a.event = d->revertList.at(ii).event(); + a.reverseEvent = d->revertList.at(ii).reverseEvent(); if (a.event && a.event->isRewindable()) a.event->saveCurrentValues(); applyList << a; // Store these special reverts in the reverting list - d->reverting << d->revertList.at(ii).property; + d->reverting << d->revertList.at(ii).property(); } } // All the local reverts now become part of the ongoing revertList @@ -526,4 +712,16 @@ QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions() return ActionList(); } +QDeclarativeState *QDeclarativeStateOperation::state() const +{ + Q_D(const QDeclarativeStateOperation); + return d->m_state; +} + +void QDeclarativeStateOperation::setState(QDeclarativeState *state) +{ + Q_D(QDeclarativeStateOperation); + d->m_state = state; +} + QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativestate_p.h b/src/declarative/util/qdeclarativestate_p.h index 2e2ce7b..a0ab11b 100644 --- a/src/declarative/util/qdeclarativestate_p.h +++ b/src/declarative/util/qdeclarativestate_p.h @@ -111,6 +111,8 @@ public: //### rename to QDeclarativeStateChange? class QDeclarativeStateGroup; +class QDeclarativeState; +class QDeclarativeStateOperationPrivate; class Q_DECLARATIVE_EXPORT QDeclarativeStateOperation : public QObject { Q_OBJECT @@ -121,8 +123,15 @@ public: virtual ActionList actions(); + QDeclarativeState *state() const; + void setState(QDeclarativeState *state); + protected: QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent = 0); + +private: + Q_DECLARE_PRIVATE(QDeclarativeStateOperation) + Q_DISABLE_COPY(QDeclarativeStateOperation) }; typedef QDeclarativeStateOperation::ActionList QDeclarativeStateActions; @@ -169,6 +178,18 @@ public: QDeclarativeStateGroup *stateGroup() const; void setStateGroup(QDeclarativeStateGroup *); + bool containsPropertyInRevertList(QObject *target, const QByteArray &name) const; + bool changeValueInRevertList(QObject *target, const QByteArray &name, const QVariant &revertValue); + bool changeBindingInRevertList(QObject *target, const QByteArray &name, QDeclarativeAbstractBinding *binding); + bool removeEntryFromRevertList(QObject *target, const QByteArray &name); + void addEntryToRevertList(const QDeclarativeAction &action); + void removeAllEntriesFromRevertList(QObject *target); + void addEntriesToRevertList(const QList<QDeclarativeAction> &actions); + QVariant valueInRevertList(QObject *target, const QByteArray &name) const; + QDeclarativeAbstractBinding *bindingInRevertList(QObject *target, const QByteArray &name) const; + + bool isStateActive() const; + Q_SIGNALS: void completed(); diff --git a/src/declarative/util/qdeclarativestate_p_p.h b/src/declarative/util/qdeclarativestate_p_p.h index 2ef9bb0..4fd8f21 100644 --- a/src/declarative/util/qdeclarativestate_p_p.h +++ b/src/declarative/util/qdeclarativestate_p_p.h @@ -61,6 +61,8 @@ #include <private/qdeclarativeproperty_p.h> #include <private/qdeclarativeguard_p.h> +#include <private/qdeclarativebinding_p.h> + #include <private/qobject_p.h> QT_BEGIN_NAMESPACE @@ -69,30 +71,123 @@ class QDeclarativeSimpleAction { public: enum State { StartState, EndState }; - QDeclarativeSimpleAction(const QDeclarativeAction &a, State state = StartState) + QDeclarativeSimpleAction(const QDeclarativeAction &a, State state = StartState) { - property = a.property; - specifiedObject = a.specifiedObject; - specifiedProperty = a.specifiedProperty; - event = a.event; + m_property = a.property; + m_specifiedObject = a.specifiedObject; + m_specifiedProperty = a.specifiedProperty; + m_event = a.event; if (state == StartState) { - value = a.fromValue; - binding = QDeclarativePropertyPrivate::binding(property); - reverseEvent = true; + m_value = a.fromValue; + if (QDeclarativePropertyPrivate::binding(m_property)) { + m_binding = QDeclarativeAbstractBinding::getPointer(QDeclarativePropertyPrivate::binding(m_property)); + } + m_reverseEvent = true; } else { - value = a.toValue; - binding = a.toBinding; - reverseEvent = false; + m_value = a.toValue; + m_binding = QDeclarativeAbstractBinding::getPointer(a.toBinding); + m_reverseEvent = false; } } - QDeclarativeProperty property; - QVariant value; - QDeclarativeAbstractBinding *binding; - QObject *specifiedObject; - QString specifiedProperty; - QDeclarativeActionEvent *event; - bool reverseEvent; + ~QDeclarativeSimpleAction() + { + } + + QDeclarativeSimpleAction(const QDeclarativeSimpleAction &other) + : m_property(other.m_property), + m_value(other.m_value), + m_binding(QDeclarativeAbstractBinding::getPointer(other.binding())), + m_specifiedObject(other.m_specifiedObject), + m_specifiedProperty(other.m_specifiedProperty), + m_event(other.m_event), + m_reverseEvent(other.m_reverseEvent) + { + } + + QDeclarativeSimpleAction &operator =(const QDeclarativeSimpleAction &other) + { + m_property = other.m_property; + m_value = other.m_value; + m_binding = QDeclarativeAbstractBinding::getPointer(other.binding()); + m_specifiedObject = other.m_specifiedObject; + m_specifiedProperty = other.m_specifiedProperty; + m_event = other.m_event; + m_reverseEvent = other.m_reverseEvent; + + return *this; + } + + void setProperty(const QDeclarativeProperty &property) + { + m_property = property; + } + + const QDeclarativeProperty &property() const + { + return m_property; + } + + void setValue(const QVariant &value) + { + m_value = value; + } + + const QVariant &value() const + { + return m_value; + } + + void setBinding(QDeclarativeAbstractBinding *binding) + { + m_binding = QDeclarativeAbstractBinding::getPointer(binding); + } + + QDeclarativeAbstractBinding *binding() const + { + return m_binding.data(); + } + + QObject *specifiedObject() const + { + return m_specifiedObject; + } + + const QString &specifiedProperty() const + { + return m_specifiedProperty; + } + + QDeclarativeActionEvent *event() const + { + return m_event; + } + + bool reverseEvent() const + { + return m_reverseEvent; + } + +private: + QDeclarativeProperty m_property; + QVariant m_value; + QDeclarativeAbstractBinding::Pointer m_binding; + QObject *m_specifiedObject; + QString m_specifiedProperty; + QDeclarativeActionEvent *m_event; + bool m_reverseEvent; +}; + +class QDeclarativeStateOperationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeStateOperation) + +public: + + QDeclarativeStateOperationPrivate() + : m_state(0) {} + + QDeclarativeState *m_state; }; class QDeclarativeStatePrivate : public QObjectPrivate @@ -122,10 +217,14 @@ public: static void operations_append(QDeclarativeListProperty<QDeclarativeStateOperation> *prop, QDeclarativeStateOperation *op) { QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + op->setState(qobject_cast<QDeclarativeState*>(prop->object)); list->append(OperationGuard(op, list)); } static void operations_clear(QDeclarativeListProperty<QDeclarativeStateOperation> *prop) { QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + QMutableListIterator<OperationGuard> listIterator(*list); + while(listIterator.hasNext()) + listIterator.next()->setState(0); list->clear(); } static int operations_count(QDeclarativeListProperty<QDeclarativeStateOperation> *prop) { diff --git a/src/declarative/util/qdeclarativestateoperations.cpp b/src/declarative/util/qdeclarativestateoperations.cpp index 845b3da..8cb813c 100644 --- a/src/declarative/util/qdeclarativestateoperations.cpp +++ b/src/declarative/util/qdeclarativestateoperations.cpp @@ -52,6 +52,7 @@ #include "private/qdeclarativecontext_p.h" #include "private/qdeclarativeproperty_p.h" #include "private/qdeclarativebinding_p.h" +#include "private/qdeclarativestate_p_p.h" #include <QtCore/qdebug.h> #include <QtGui/qgraphicsitem.h> @@ -61,7 +62,7 @@ QT_BEGIN_NAMESPACE -class QDeclarativeParentChangePrivate : public QObjectPrivate +class QDeclarativeParentChangePrivate : public QDeclarativeStateOperationPrivate { Q_DECLARE_PUBLIC(QDeclarativeParentChange) public: @@ -98,14 +99,15 @@ void QDeclarativeParentChangePrivate::doChange(QDeclarativeItem *targetParent, Q qreal scale = 1; qreal rotation = 0; - if (ok && transform.type() != QTransform::TxRotate) { + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { if (transform.m11() == transform.m22()) scale = transform.m11(); else { qmlInfo(q) << QDeclarativeParentChange::tr("Unable to preserve appearance under non-uniform scale"); ok = false; } - } else if (ok && transform.type() == QTransform::TxRotate) { + } else if (ok && isRotate) { if (transform.m11() == transform.m22()) scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); else { @@ -579,7 +581,7 @@ void QDeclarativeParentChange::rewind() d->doChange(d->rewindParent, d->rewindStackBefore); } -class QDeclarativeStateChangeScriptPrivate : public QObjectPrivate +class QDeclarativeStateChangeScriptPrivate : public QDeclarativeStateOperationPrivate { public: QDeclarativeStateChangeScriptPrivate() {} @@ -964,7 +966,7 @@ void QDeclarativeAnchorSet::resetCenterIn() } -class QDeclarativeAnchorChangesPrivate : public QObjectPrivate +class QDeclarativeAnchorChangesPrivate : public QDeclarativeStateOperationPrivate { public: QDeclarativeAnchorChangesPrivate() @@ -1029,6 +1031,11 @@ public: bool applyOrigVCenter; bool applyOrigBaseline; + QDeclarativeNullableValue<qreal> origWidth; + QDeclarativeNullableValue<qreal> origHeight; + qreal origX; + qreal origY; + QList<QDeclarativeAbstractBinding*> oldBindings; QDeclarativeProperty leftProp; @@ -1320,6 +1327,42 @@ void QDeclarativeAnchorChanges::reverse(Reason reason) QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); if (d->origBaselineBinding) QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + + //restore any absolute geometry changed by the state's anchors + QDeclarativeAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::Vertical_Mask; + QDeclarativeAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarativeAnchors::Vertical_Mask; + QDeclarativeAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarativeAnchors::Horizontal_Mask; + QDeclarativeAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarativeAnchors::Horizontal_Mask; + + bool stateSetWidth = (stateHAnchors && + stateHAnchors != QDeclarativeAnchors::LeftAnchor && + stateHAnchors != QDeclarativeAnchors::RightAnchor && + stateHAnchors != QDeclarativeAnchors::HCenterAnchor); + bool origSetWidth = (origHAnchors && + origHAnchors != QDeclarativeAnchors::LeftAnchor && + origHAnchors != QDeclarativeAnchors::RightAnchor && + origHAnchors != QDeclarativeAnchors::HCenterAnchor); + if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) + d->target->setWidth(d->origWidth.value); + + bool stateSetHeight = (stateVAnchors && + stateVAnchors != QDeclarativeAnchors::TopAnchor && + stateVAnchors != QDeclarativeAnchors::BottomAnchor && + stateVAnchors != QDeclarativeAnchors::VCenterAnchor && + stateVAnchors != QDeclarativeAnchors::BaselineAnchor); + bool origSetHeight = (origVAnchors && + origVAnchors != QDeclarativeAnchors::TopAnchor && + origVAnchors != QDeclarativeAnchors::BottomAnchor && + origVAnchors != QDeclarativeAnchors::VCenterAnchor && + origVAnchors != QDeclarativeAnchors::BaselineAnchor); + if (d->origHeight.isValid() && stateSetHeight && !origSetHeight) + d->target->setHeight(d->origHeight.value); + + if (stateHAnchors && !origHAnchors) + d->target->setX(d->origX); + + if (stateVAnchors && !origVAnchors) + d->target->setY(d->origY); } QString QDeclarativeAnchorChanges::typeName() const @@ -1382,6 +1425,14 @@ void QDeclarativeAnchorChanges::saveOriginals() d->origVCenterBinding = QDeclarativePropertyPrivate::binding(d->vCenterProp); d->origBaselineBinding = QDeclarativePropertyPrivate::binding(d->baselineProp); + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::get(d->target); + if (targetPrivate->widthValid) + d->origWidth = d->target->width(); + if (targetPrivate->heightValid) + d->origHeight = d->target->height(); + d->origX = d->target->x(); + d->origY = d->target->y(); + d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop = d->applyOrigBottom = d->applyOrigVCenter = d->applyOrigBaseline = false; @@ -1414,6 +1465,11 @@ void QDeclarativeAnchorChanges::copyOriginals(QDeclarativeActionEvent *other) d->origVCenterBinding = acp->origVCenterBinding; d->origBaselineBinding = acp->origBaselineBinding; + d->origWidth = acp->origWidth; + d->origHeight = acp->origHeight; + d->origX = acp->origX; + d->origY = acp->origY; + d->oldBindings.clear(); d->oldBindings << acp->leftBinding << acp->rightBinding << acp->hCenterBinding << acp->topBinding << acp->bottomBinding << acp->baselineBinding; diff --git a/src/declarative/util/qdeclarativetransitionmanager.cpp b/src/declarative/util/qdeclarativetransitionmanager.cpp index d82c4bb..89b0044 100644 --- a/src/declarative/util/qdeclarativetransitionmanager.cpp +++ b/src/declarative/util/qdeclarativetransitionmanager.cpp @@ -86,8 +86,8 @@ void QDeclarativeTransitionManager::complete() d->applyBindings(); for (int ii = 0; ii < d->completeList.count(); ++ii) { - const QDeclarativeProperty &prop = d->completeList.at(ii).property; - prop.write(d->completeList.at(ii).value); + const QDeclarativeProperty &prop = d->completeList.at(ii).property(); + prop.write(d->completeList.at(ii).value()); } d->completeList.clear(); diff --git a/src/declarative/util/qdeclarativexmllistmodel.cpp b/src/declarative/util/qdeclarativexmllistmodel.cpp index 905ef1e..870a523 100644 --- a/src/declarative/util/qdeclarativexmllistmodel.cpp +++ b/src/declarative/util/qdeclarativexmllistmodel.cpp @@ -209,8 +209,9 @@ Q_SIGNALS: protected: void run() { + m_mutex.lock(); + while (!m_quit) { - m_mutex.lock(); if (!m_jobs.isEmpty()) m_currentJob = m_jobs.dequeue(); m_mutex.unlock(); @@ -230,12 +231,13 @@ protected: m_mutex.lock(); if (m_currentJob.queryId != -1 && m_abortQueryId != m_currentJob.queryId) emit queryCompleted(r); - if (m_jobs.isEmpty()) + if (m_jobs.isEmpty() && !m_quit) m_condition.wait(&m_mutex); m_currentJob.queryId = -1; m_abortQueryId = -1; - m_mutex.unlock(); } + + m_mutex.unlock(); } private: |