From 44d8691745ee9e8f927bdcd493f26b759c77d71c Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Wed, 16 Nov 2011 17:58:19 +1000 Subject: [PATCH] Move XmlListModel to seperate plugin MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit XmlListModel is the only feature inside QtDeclarative which depends on the QtXmlPatterns module, move this item to a separate plugin can reduce the unnecessary dependency in most use cases. To use the new XmlListModel, an additional "import QtQuick.xmllistmodel 2.0" is needed. Task-number:QTBUG-22158 Change-Id: I9e19eb6cbba46cd40fb7ffcdbd741f346779a54d Reviewed-by: Yunqiao Yin --- doc/src/declarative/qdeclarativemodels.qdoc | 4 + doc/src/snippets/declarative/xmlrole.qml | 1 + examples/declarative/flickr/content/RssModel.qml | 1 + .../modelviews/visualdatamodel/slideshow.qml | 1 + .../particles/itemparticle/content/RssModel.qml | 1 + .../photoviewer/PhotoViewerCore/AlbumDelegate.qml | 1 + .../photoviewer/PhotoViewerCore/RssModel.qml | 1 + examples/declarative/rssnews/rssnews.qml | 1 + .../declarative/twitter/TwitterCore/RssModel.qml | 1 + .../declarative/twitter/TwitterCore/UserModel.qml | 1 + examples/declarative/twitter/twitter.qml | 1 + src/declarative/util/qdeclarativeutilmodule.cpp | 12 - src/declarative/util/qdeclarativexmllistmodel.cpp | 1163 -------------------- src/declarative/util/qdeclarativexmllistmodel_p.h | 213 ---- src/declarative/util/util.pri | 10 +- src/imports/imports.pro | 2 +- src/imports/xmllistmodel/plugin.cpp | 65 ++ .../xmllistmodel/qdeclarativexmllistmodel.cpp | 1160 +++++++++++++++++++ .../xmllistmodel/qdeclarativexmllistmodel_p.h | 213 ++++ src/imports/xmllistmodel/qmldir | 1 + src/imports/xmllistmodel/xmllistmodel.pro | 16 + .../qdeclarativexmllistmodel/data/get.qml | 2 +- .../qdeclarativexmllistmodel/data/model.qml | 1 + .../data/propertychanges.qml | 1 + .../qdeclarativexmllistmodel/data/recipes.qml | 1 + .../qdeclarativexmllistmodel/data/roleCrash.qml | 1 + .../qdeclarativexmllistmodel/data/roleErrors.qml | 1 + .../qdeclarativexmllistmodel/data/roleKeys.qml | 2 +- .../qdeclarativexmllistmodel/data/testtypes.qml | 2 +- .../qdeclarativexmllistmodel/data/unique.qml | 1 + .../qdeclarativexmllistmodel.pro | 3 +- .../tst_qdeclarativexmllistmodel.cpp | 286 +++--- 32 files changed, 1617 insertions(+), 1553 deletions(-) delete mode 100644 src/declarative/util/qdeclarativexmllistmodel.cpp delete mode 100644 src/declarative/util/qdeclarativexmllistmodel_p.h create mode 100644 src/imports/xmllistmodel/plugin.cpp create mode 100644 src/imports/xmllistmodel/qdeclarativexmllistmodel.cpp create mode 100644 src/imports/xmllistmodel/qdeclarativexmllistmodel_p.h create mode 100644 src/imports/xmllistmodel/qmldir create mode 100644 src/imports/xmllistmodel/xmllistmodel.pro create mode 100644 tests/auto/declarative/qdeclarativexmllistmodel/data/empty.xml diff --git a/doc/src/declarative/qdeclarativemodels.qdoc b/doc/src/declarative/qdeclarativemodels.qdoc index 61604d2..4fe7193 100644 --- a/doc/src/declarative/qdeclarativemodels.qdoc +++ b/doc/src/declarative/qdeclarativemodels.qdoc @@ -114,8 +114,12 @@ using the model. To reset the roles available in the model, call ListModel::clea XmlListModel allows construction of a model from an XML data source. The roles are specified via the \l XmlRole element. +Note: From QtQuick 2.0, XmlListModel has been move to a seperate module \l QtQuick.XmlListModel, +to use XmlListModel item, an additional "import QtQuick.XmlListModel 2.0" is needed. + The following model has three roles, \i title, \i link and \i description: \qml +import QtQuick.XmlListModel 2.0 XmlListModel { id: feedModel source: "http://rss.news.yahoo.com/rss/oceania" diff --git a/doc/src/snippets/declarative/xmlrole.qml b/doc/src/snippets/declarative/xmlrole.qml index 6709d31..4d6964a 100644 --- a/doc/src/snippets/declarative/xmlrole.qml +++ b/doc/src/snippets/declarative/xmlrole.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 Rectangle { width: 300; height: 200 diff --git a/examples/declarative/flickr/content/RssModel.qml b/examples/declarative/flickr/content/RssModel.qml index fa28aea..c581b76 100644 --- a/examples/declarative/flickr/content/RssModel.qml +++ b/examples/declarative/flickr/content/RssModel.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { property string tags : "" diff --git a/examples/declarative/modelviews/visualdatamodel/slideshow.qml b/examples/declarative/modelviews/visualdatamodel/slideshow.qml index edf1f0f..bbcf128 100644 --- a/examples/declarative/modelviews/visualdatamodel/slideshow.qml +++ b/examples/declarative/modelviews/visualdatamodel/slideshow.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 Rectangle { id: root diff --git a/examples/declarative/particles/itemparticle/content/RssModel.qml b/examples/declarative/particles/itemparticle/content/RssModel.qml index edb3cea..9049fe4 100644 --- a/examples/declarative/particles/itemparticle/content/RssModel.qml +++ b/examples/declarative/particles/itemparticle/content/RssModel.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { property string tags : "" diff --git a/examples/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml b/examples/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml index 6a5d16e..d6b5710 100644 --- a/examples/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml +++ b/examples/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 Component { id: albumDelegate diff --git a/examples/declarative/photoviewer/PhotoViewerCore/RssModel.qml b/examples/declarative/photoviewer/PhotoViewerCore/RssModel.qml index b92685c..c9f1daf 100644 --- a/examples/declarative/photoviewer/PhotoViewerCore/RssModel.qml +++ b/examples/declarative/photoviewer/PhotoViewerCore/RssModel.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { property string tags : "" diff --git a/examples/declarative/rssnews/rssnews.qml b/examples/declarative/rssnews/rssnews.qml index ddbfdd9..7ae5998 100644 --- a/examples/declarative/rssnews/rssnews.qml +++ b/examples/declarative/rssnews/rssnews.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 import "content" Rectangle { diff --git a/examples/declarative/twitter/TwitterCore/RssModel.qml b/examples/declarative/twitter/TwitterCore/RssModel.qml index 2abbedc..5a99a9a 100644 --- a/examples/declarative/twitter/TwitterCore/RssModel.qml +++ b/examples/declarative/twitter/TwitterCore/RssModel.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 Item { id: wrapper property variant model: xmlModel diff --git a/examples/declarative/twitter/TwitterCore/UserModel.qml b/examples/declarative/twitter/TwitterCore/UserModel.qml index 00eabb9..b3b7bf5 100644 --- a/examples/declarative/twitter/TwitterCore/UserModel.qml +++ b/examples/declarative/twitter/TwitterCore/UserModel.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 //This "model" gets the user information about the searched user. Mainly for the icon. diff --git a/examples/declarative/twitter/twitter.qml b/examples/declarative/twitter/twitter.qml index 05197e2..1f8b5f4 100644 --- a/examples/declarative/twitter/twitter.qml +++ b/examples/declarative/twitter/twitter.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 import "TwitterCore" 2.0 as Twitter Item { diff --git a/src/declarative/util/qdeclarativeutilmodule.cpp b/src/declarative/util/qdeclarativeutilmodule.cpp index 511ccd3..2e82364 100644 --- a/src/declarative/util/qdeclarativeutilmodule.cpp +++ b/src/declarative/util/qdeclarativeutilmodule.cpp @@ -59,9 +59,6 @@ #include "qdeclarativetransition_p.h" #include #include -#ifndef QT_NO_XMLPATTERNS -#include "qdeclarativexmllistmodel_p.h" -#endif #include #include @@ -93,15 +90,6 @@ void QDeclarativeUtilModule::defineModule() qmlRegisterType("QtQuick",2,0,"Timer"); qmlRegisterType("QtQuick",2,0,"Transition"); qmlRegisterType("QtQuick",2,0,"Vector3dAnimation"); -#ifdef QT_NO_XMLPATTERNS - qmlRegisterTypeNotAvailable("QtQuick",2,0,"XmlListModel", - qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); - qmlRegisterTypeNotAvailable("QtQuick",2,0,"XmlRole", - qApp->translate("QDeclarativeXmlListModel","Qt was built without support for xmlpatterns")); -#else - qmlRegisterType("QtQuick",2,0,"XmlListModel"); - qmlRegisterType("QtQuick",2,0,"XmlRole"); -#endif qmlRegisterType(); diff --git a/src/declarative/util/qdeclarativexmllistmodel.cpp b/src/declarative/util/qdeclarativexmllistmodel.cpp deleted file mode 100644 index d757516..0000000 --- a/src/declarative/util/qdeclarativexmllistmodel.cpp +++ /dev/null @@ -1,1163 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdeclarativexmllistmodel_p.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult) - -QT_BEGIN_NAMESPACE - - -typedef QPair QDeclarativeXmlListRange; - -#define XMLLISTMODEL_CLEAR_ID 0 - -/*! - \qmlclass XmlRole QDeclarativeXmlListModelRole - \inqmlmodule QtQuick 2 - \ingroup qml-working-with-data - \brief The XmlRole element allows you to specify a role for an XmlListModel. - - \sa {QtDeclarative} -*/ - -/*! - \qmlproperty string QtQuick2::XmlRole::name - - The name for the role. This name is used to access the model data for this role. - - For example, the following model has a role named "title", which can be accessed - from the view's delegate: - - \qml - XmlListModel { - id: xmlModel - // ... - XmlRole { - name: "title" - query: "title/string()" - } - } - \endqml - - \qml - ListView { - model: xmlModel - delegate: Text { text: title } - } - \endqml -*/ - -/*! - \qmlproperty string QtQuick2::XmlRole::query - The relative XPath expression query for this role. The query must be relative; it cannot start - with a '/'. - - For example, if there is an XML document like this: - - \quotefile doc/src/snippets/declarative/xmlrole.xml - - Here are some valid XPath expressions for XmlRole queries on this document: - - \snippet doc/src/snippets/declarative/xmlrole.qml 0 - \dots 4 - \snippet doc/src/snippets/declarative/xmlrole.qml 1 - - See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information. -*/ - -/*! - \qmlproperty bool QtQuick2::XmlRole::isKey - Defines whether this is a key role. - - Key roles are used to to determine whether a set of values should - be updated or added to the XML list model when XmlListModel::reload() - is called. - - \sa XmlListModel -*/ - -struct XmlQueryJob -{ - int queryId; - QByteArray data; - QString query; - QString namespaces; - QStringList roleQueries; - QList roleQueryErrorId; // the ptr to send back if there is an error - QStringList keyRoleQueries; - QStringList keyRoleResultsCache; - QString prefix; -}; - - -class QDeclarativeXmlQueryEngine; -class QDeclarativeXmlQueryThreadObject : public QObject -{ - Q_OBJECT -public: - QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *); - - void processJobs(); - virtual bool event(QEvent *e); - -private: - QDeclarativeXmlQueryEngine *m_queryEngine; -}; - - -class QDeclarativeXmlQueryEngine : public QThread -{ - Q_OBJECT -public: - QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng); - ~QDeclarativeXmlQueryEngine(); - - int doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache); - void abort(int id); - - void processJobs(); - - static QDeclarativeXmlQueryEngine *instance(QDeclarativeEngine *engine); - -signals: - void queryCompleted(const QDeclarativeXmlQueryResult &); - void error(void*, const QString&); - -protected: - void run(); - -private: - void processQuery(XmlQueryJob *job); - void doQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); - void doSubQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); - void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const; - void addIndexToRangeList(QList *ranges, int index) const; - - QMutex m_mutex; - QDeclarativeXmlQueryThreadObject *m_threadObject; - QList m_jobs; - QSet m_cancelledJobs; - QAtomicInt m_queryIds; - - QDeclarativeEngine *m_engine; - QObject *m_eventLoopQuitHack; - - static QHash queryEngines; - static QMutex queryEnginesMutex; -}; -QHash QDeclarativeXmlQueryEngine::queryEngines; -QMutex QDeclarativeXmlQueryEngine::queryEnginesMutex; - - -QDeclarativeXmlQueryThreadObject::QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *e) - : m_queryEngine(e) -{ -} - -void QDeclarativeXmlQueryThreadObject::processJobs() -{ - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); -} - -bool QDeclarativeXmlQueryThreadObject::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - m_queryEngine->processJobs(); - return true; - } else { - return QObject::event(e); - } -} - - - -QDeclarativeXmlQueryEngine::QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng) -: QThread(eng), m_threadObject(0), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1), m_engine(eng), m_eventLoopQuitHack(0) -{ - qRegisterMetaType("QDeclarativeXmlQueryResult"); - - m_eventLoopQuitHack = new QObject; - m_eventLoopQuitHack->moveToThread(this); - connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); - start(QThread::IdlePriority); -} - -QDeclarativeXmlQueryEngine::~QDeclarativeXmlQueryEngine() -{ - queryEnginesMutex.lock(); - queryEngines.remove(m_engine); - queryEnginesMutex.unlock(); - - m_eventLoopQuitHack->deleteLater(); - wait(); -} - -int QDeclarativeXmlQueryEngine::doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache) { - { - QMutexLocker m1(&m_mutex); - m_queryIds.ref(); - if (m_queryIds.load() <= 0) - m_queryIds.store(1); - } - - XmlQueryJob job; - job.queryId = m_queryIds.load(); - job.data = data; - job.query = QLatin1String("doc($src)") + query; - job.namespaces = namespaces; - job.keyRoleResultsCache = keyRoleResultsCache; - - for (int i=0; icount(); i++) { - if (!roleObjects->at(i)->isValid()) { - job.roleQueries << QString(); - continue; - } - job.roleQueries << roleObjects->at(i)->query(); - job.roleQueryErrorId << static_cast(roleObjects->at(i)); - if (roleObjects->at(i)->isKey()) - job.keyRoleQueries << job.roleQueries.last(); - } - - { - QMutexLocker ml(&m_mutex); - m_jobs.append(job); - if (m_threadObject) - m_threadObject->processJobs(); - } - - return job.queryId; -} - -void QDeclarativeXmlQueryEngine::abort(int id) -{ - QMutexLocker ml(&m_mutex); - if (id != -1) - m_cancelledJobs.insert(id); -} - -void QDeclarativeXmlQueryEngine::run() -{ - m_mutex.lock(); - m_threadObject = new QDeclarativeXmlQueryThreadObject(this); - m_mutex.unlock(); - - processJobs(); - exec(); - - delete m_threadObject; - m_threadObject = 0; -} - -void QDeclarativeXmlQueryEngine::processJobs() -{ - QMutexLocker locker(&m_mutex); - - while (true) { - if (m_jobs.isEmpty()) - return; - - XmlQueryJob currentJob = m_jobs.takeLast(); - while (m_cancelledJobs.remove(currentJob.queryId)) { - if (m_jobs.isEmpty()) - return; - currentJob = m_jobs.takeLast(); - } - - locker.unlock(); - processQuery(¤tJob); - locker.relock(); - } -} - -QDeclarativeXmlQueryEngine *QDeclarativeXmlQueryEngine::instance(QDeclarativeEngine *engine) -{ - queryEnginesMutex.lock(); - QDeclarativeXmlQueryEngine *queryEng = queryEngines.value(engine); - if (!queryEng) { - queryEng = new QDeclarativeXmlQueryEngine(engine); - queryEngines.insert(engine, queryEng); - } - queryEnginesMutex.unlock(); - - return queryEng; -} - -void QDeclarativeXmlQueryEngine::processQuery(XmlQueryJob *job) -{ - QDeclarativeXmlQueryResult result; - result.queryId = job->queryId; - doQueryJob(job, &result); - doSubQueryJob(job, &result); - - { - QMutexLocker ml(&m_mutex); - if (m_cancelledJobs.contains(job->queryId)) { - m_cancelledJobs.remove(job->queryId); - } else { - emit queryCompleted(result); - } - } -} - -void QDeclarativeXmlQueryEngine::doQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) -{ - Q_ASSERT(currentJob->queryId != -1); - - QString r; - QXmlQuery query; - QBuffer buffer(¤tJob->data); - buffer.open(QIODevice::ReadOnly); - query.bindVariable(QLatin1String("src"), &buffer); - query.setQuery(currentJob->namespaces + currentJob->query); - query.evaluateTo(&r); - - //always need a single root element - QByteArray xml = "\n" + r.toUtf8() + ""; - QBuffer b(&xml); - b.open(QIODevice::ReadOnly); - - QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces; - QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + - currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/'))); - - //figure out how many items we are dealing with - int count = -1; - { - QXmlResultItems result; - QXmlQuery countquery; - countquery.bindVariable(QLatin1String("inputDocument"), &b); - countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')')); - countquery.evaluateTo(&result); - QXmlItem item(result.next()); - if (item.isAtomicValue()) - count = item.toAtomicValue().toInt(); - } - - currentJob->data = xml; - currentJob->prefix = namespaces + prefix + QLatin1Char('/'); - currentResult->size = (count > 0 ? count : 0); -} - -void QDeclarativeXmlQueryEngine::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const -{ - const QStringList &keysQueries = currentJob.keyRoleQueries; - QString keysQuery; - if (keysQueries.count() == 1) - keysQuery = currentJob.prefix + keysQueries[0]; - else if (keysQueries.count() > 1) - keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")"); - - if (!keysQuery.isEmpty()) { - query->setQuery(keysQuery); - QXmlResultItems resultItems; - query->evaluateTo(&resultItems); - QXmlItem item(resultItems.next()); - while (!item.isNull()) { - values->append(item.toAtomicValue().toString()); - item = resultItems.next(); - } - } -} - -void QDeclarativeXmlQueryEngine::addIndexToRangeList(QList *ranges, int index) const { - if (ranges->isEmpty()) - ranges->append(qMakePair(index, 1)); - else if (ranges->last().first + ranges->last().second == index) - ranges->last().second += 1; - else - ranges->append(qMakePair(index, 1)); -} - -void QDeclarativeXmlQueryEngine::doSubQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) -{ - Q_ASSERT(currentJob->queryId != -1); - - QBuffer b(¤tJob->data); - b.open(QIODevice::ReadOnly); - - QXmlQuery subquery; - subquery.bindVariable(QLatin1String("inputDocument"), &b); - - QStringList keyRoleResults; - getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery); - - // See if any values of key roles have been inserted or removed. - - if (currentJob->keyRoleResultsCache.isEmpty()) { - currentResult->inserted << qMakePair(0, currentResult->size); - } else { - if (keyRoleResults != currentJob->keyRoleResultsCache) { - QStringList temp; - for (int i=0; ikeyRoleResultsCache.count(); i++) { - if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i])) - addIndexToRangeList(¤tResult->removed, i); - else - temp << currentJob->keyRoleResultsCache[i]; - } - - for (int i=0; iinserted, i); - } - } - } - } - currentResult->keyRoleResultsCache = keyRoleResults; - - // Get the new values for each role. - //### we might be able to condense even further (query for everything in one go) - const QStringList &queries = currentJob->roleQueries; - for (int i = 0; i < queries.size(); ++i) { - QList resultList; - if (!queries[i].isEmpty()) { - subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")")); - if (subquery.isValid()) { - QXmlResultItems resultItems; - subquery.evaluateTo(&resultItems); - QXmlItem item(resultItems.next()); - while (!item.isNull()) { - resultList << item.toAtomicValue(); //### we used to trim strings - item = resultItems.next(); - } - } else { - emit error(currentJob->roleQueryErrorId.at(i), queries[i]); - } - } - //### should warn here if things have gone wrong. - while (resultList.count() < currentResult->size) - resultList << QVariant(); - currentResult->data << resultList; - b.seek(0); - } - - //this method is much slower, but works better for incremental loading - /*for (int j = 0; j < m_size; ++j) { - QList resultList; - for (int i = 0; i < m_roleObjects->size(); ++i) { - QDeclarativeXmlListModelRole *role = m_roleObjects->at(i); - subquery.setQuery(m_prefix.arg(j+1) + role->query()); - if (role->isStringList()) { - QStringList data; - subquery.evaluateTo(&data); - resultList << QVariant(data); - //qDebug() << data; - } else { - QString s; - subquery.evaluateTo(&s); - if (role->isCData()) { - //un-escape - s.replace(QLatin1String("<"), QLatin1String("<")); - s.replace(QLatin1String(">"), QLatin1String(">")); - s.replace(QLatin1String("&"), QLatin1String("&")); - } - resultList << s.trimmed(); - //qDebug() << s; - } - b.seek(0); - } - m_modelData << resultList; - }*/ -} - -class QDeclarativeXmlListModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QDeclarativeXmlListModel) -public: - QDeclarativeXmlListModelPrivate() - : isComponentComplete(true), size(-1), highestRole(Qt::UserRole) - , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0) - , queryId(-1), roleObjects(), redirectCount(0) {} - - - void notifyQueryStarted(bool remoteSource) { - Q_Q(QDeclarativeXmlListModel); - progress = remoteSource ? 0.0 : 1.0; - status = QDeclarativeXmlListModel::Loading; - errorString.clear(); - emit q->progressChanged(progress); - emit q->statusChanged(status); - } - - void deleteReply() { - Q_Q(QDeclarativeXmlListModel); - if (reply) { - QObject::disconnect(reply, 0, q, 0); - reply->deleteLater(); - reply = 0; - } - } - - bool isComponentComplete; - QUrl src; - QString xml; - QString query; - QString namespaces; - int size; - QList roles; - QStringList roleNames; - int highestRole; - - QNetworkReply *reply; - QDeclarativeXmlListModel::Status status; - QString errorString; - qreal progress; - int queryId; - QStringList keyRoleResultsCache; - QList roleObjects; - - static void append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role); - static void clear_role(QDeclarativeListProperty *list); - QList > data; - int redirectCount; -}; - - -void QDeclarativeXmlListModelPrivate::append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role) -{ - QDeclarativeXmlListModel *_this = qobject_cast(list->object); - if (_this && role) { - int i = _this->d_func()->roleObjects.count(); - _this->d_func()->roleObjects.append(role); - if (_this->d_func()->roleNames.contains(role->name())) { - qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name()); - return; - } - _this->d_func()->roles.insert(i, _this->d_func()->highestRole); - _this->d_func()->roleNames.insert(i, role->name()); - ++_this->d_func()->highestRole; - } -} - -//### clear needs to invalidate any cached data (in data table) as well -// (and the model should emit the appropriate signals) -void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty *list) -{ - QDeclarativeXmlListModel *_this = static_cast(list->object); - _this->d_func()->roles.clear(); - _this->d_func()->roleNames.clear(); - _this->d_func()->roleObjects.clear(); -} - -/*! - \qmlclass XmlListModel QDeclarativeXmlListModel - \inqmlmodule QtQuick 2 - \ingroup qml-working-with-data - \brief The XmlListModel element is used to specify a read-only model using XPath expressions. - - XmlListModel is used to create a read-only model from XML data. It can be used as a data source - for view elements (such as ListView, PathView, GridView) and other elements that interact with model - data (such as \l Repeater). - - For example, if there is a XML document at http://www.mysite.com/feed.xml like this: - - \code - - - ... - - - A blog post - Sat, 07 Sep 2010 10:00:01 GMT - - - Another blog post - Sat, 07 Sep 2010 15:35:01 GMT - - - - \endcode - - A XmlListModel could create a model from this data, like this: - - \qml - import QtQuick 1.0 - - XmlListModel { - id: xmlModel - source: "http://www.mysite.com/feed.xml" - query: "/rss/channel/item" - - XmlRole { name: "title"; query: "title/string()" } - XmlRole { name: "pubDate"; query: "pubDate/string()" } - } - \endqml - - The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate - a model item for each \c in the XML document. - - The XmlRole objects define the - model item attributes. Here, each model item will have \c title and \c pubDate - attributes that match the \c title and \c pubDate values of its corresponding \c . - (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.) - - The model could be used in a ListView, like this: - - \qml - ListView { - width: 180; height: 300 - model: xmlModel - delegate: Text { text: title + ": " + pubDate } - } - \endqml - - \image qml-xmllistmodel-example.png - - The XmlListModel data is loaded asynchronously, and \l status - is set to \c XmlListModel.Ready when loading is complete. - Note this means when XmlListModel is used for a view, the view is not - populated until the model is loaded. - - - \section2 Using key XML roles - - You can define certain roles as "keys" so that when reload() is called, - the model will only add and refresh data that contains new values for - these keys. - - For example, if above role for "pubDate" was defined like this instead: - - \qml - XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true } - \endqml - - Then when reload() is called, the model will only add and reload - items with a "pubDate" value that is not already - present in the model. - - This is useful when displaying the contents of XML documents that - are incrementally updated (such as RSS feeds) to avoid repainting the - entire contents of a model in a view. - - If multiple key roles are specified, the model only adds and reload items - with a combined value of all key roles that is not already present in - the model. - - \sa {RSS News} -*/ - -QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent) - : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent) -{ -} - -QDeclarativeXmlListModel::~QDeclarativeXmlListModel() -{ -} - -/*! - \qmlproperty list QtQuick2::XmlListModel::roles - - The roles to make available for this model. -*/ -QDeclarativeListProperty QDeclarativeXmlListModel::roleObjects() -{ - Q_D(QDeclarativeXmlListModel); - QDeclarativeListProperty list(this, d->roleObjects); - list.append = &QDeclarativeXmlListModelPrivate::append_role; - list.clear = &QDeclarativeXmlListModelPrivate::clear_role; - return list; -} - -QHash QDeclarativeXmlListModel::data(int index, const QList &roles) const -{ - Q_D(const QDeclarativeXmlListModel); - QHash rv; - for (int i = 0; i < roles.size(); ++i) { - int role = roles.at(i); - int roleIndex = d->roles.indexOf(role); - rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index)); - } - return rv; -} - -QVariant QDeclarativeXmlListModel::data(int index, int role) const -{ - Q_D(const QDeclarativeXmlListModel); - int roleIndex = d->roles.indexOf(role); - return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index); -} - -/*! - \qmlproperty int QtQuick2::XmlListModel::count - The number of data entries in the model. -*/ -int QDeclarativeXmlListModel::count() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->size; -} - -QList QDeclarativeXmlListModel::roles() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->roles; -} - -QString QDeclarativeXmlListModel::toString(int role) const -{ - Q_D(const QDeclarativeXmlListModel); - int index = d->roles.indexOf(role); - if (index == -1) - return QString(); - return d->roleNames.at(index); -} - -/*! - \qmlproperty url QtQuick2::XmlListModel::source - The location of the XML data source. - - If both \c source and \l xml are set, \l xml is used. -*/ -QUrl QDeclarativeXmlListModel::source() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->src; -} - -void QDeclarativeXmlListModel::setSource(const QUrl &src) -{ - Q_D(QDeclarativeXmlListModel); - if (d->src != src) { - d->src = src; - if (d->xml.isEmpty()) // src is only used if d->xml is not set - reload(); - emit sourceChanged(); - } -} - -/*! - \qmlproperty string QtQuick2::XmlListModel::xml - This property holds the XML data for this model, if set. - - The text is assumed to be UTF-8 encoded. - - If both \l source and \c xml are set, \c xml is used. -*/ -QString QDeclarativeXmlListModel::xml() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->xml; -} - -void QDeclarativeXmlListModel::setXml(const QString &xml) -{ - Q_D(QDeclarativeXmlListModel); - if (d->xml != xml) { - d->xml = xml; - reload(); - emit xmlChanged(); - } -} - -/*! - \qmlproperty string QtQuick2::XmlListModel::query - An absolute XPath query representing the base query for creating model items - from this model's XmlRole objects. The query should start with '/' or '//'. -*/ -QString QDeclarativeXmlListModel::query() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->query; -} - -void QDeclarativeXmlListModel::setQuery(const QString &query) -{ - Q_D(QDeclarativeXmlListModel); - if (!query.startsWith(QLatin1Char('/'))) { - qmlInfo(this) << QCoreApplication::translate("QDeclarativeXmlRoleList", "An XmlListModel query must start with '/' or \"//\""); - return; - } - - if (d->query != query) { - d->query = query; - reload(); - emit queryChanged(); - } -} - -/*! - \qmlproperty string QtQuick2::XmlListModel::namespaceDeclarations - The namespace declarations to be used in the XPath queries. - - The namespaces should be declared as in XQuery. For example, if a requested document - at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom", - this can be declared as the default namespace: - - \qml - XmlListModel { - source: "http://mysite.com/feed.xml" - query: "/feed/entry" - namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';" - - XmlRole { name: "title"; query: "title/string()" } - } - \endqml -*/ -QString QDeclarativeXmlListModel::namespaceDeclarations() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->namespaces; -} - -void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarations) -{ - Q_D(QDeclarativeXmlListModel); - if (d->namespaces != declarations) { - d->namespaces = declarations; - reload(); - emit namespaceDeclarationsChanged(); - } -} - -/*! - \qmlmethod object QtQuick2::XmlListModel::get(int index) - - Returns the item at \a index in the model. - - For example, for a model like this: - - \qml - XmlListModel { - id: model - source: "http://mysite.com/feed.xml" - query: "/feed/entry" - XmlRole { name: "title"; query: "title/string()" } - } - \endqml - - This will access the \c title value for the first item in the model: - - \js - var title = model.get(0).title; - \endjs -*/ -QDeclarativeV8Handle QDeclarativeXmlListModel::get(int index) const -{ - // Must be called with a context and handle scope - Q_D(const QDeclarativeXmlListModel); - - if (index < 0 || index >= count()) - return QDeclarativeV8Handle::fromHandle(v8::Undefined()); - - QDeclarativeEngine *engine = qmlContext(this)->engine(); - QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(engine); - v8::Local rv = v8::Object::New(); - for (int ii = 0; ii < d->roleObjects.count(); ++ii) - rv->Set(v8engine->toString(d->roleObjects[ii]->name()), - v8engine->fromVariant(d->data.value(ii).value(index))); - - return QDeclarativeV8Handle::fromHandle(rv); -} - -/*! - \qmlproperty enumeration QtQuick2::XmlListModel::status - Specifies the model loading status, which can be one of the following: - - \list - \o XmlListModel.Null - No XML data has been set for this model. - \o XmlListModel.Ready - The XML data has been loaded into the model. - \o XmlListModel.Loading - The model is in the process of reading and loading XML data. - \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details - about the error. - \endlist - - \sa progress - -*/ -QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->status; -} - -/*! - \qmlproperty real QtQuick2::XmlListModel::progress - - This indicates the current progress of the downloading of the XML data - source. This value ranges from 0.0 (no data downloaded) to - 1.0 (all data downloaded). If the XML data is not from a remote source, - the progress becomes 1.0 as soon as the data is read. - - Note that when the progress is 1.0, the XML data has been downloaded, but - it is yet to be loaded into the model at this point. Use the status - property to find out when the XML data has been read and loaded into - the model. - - \sa status, source -*/ -qreal QDeclarativeXmlListModel::progress() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->progress; -} - -/*! - \qmlmethod void QtQuick2::XmlListModel::errorString() - - Returns a string description of the last error that occurred - if \l status is XmlListModel::Error. -*/ -QString QDeclarativeXmlListModel::errorString() const -{ - Q_D(const QDeclarativeXmlListModel); - return d->errorString; -} - -void QDeclarativeXmlListModel::classBegin() -{ - Q_D(QDeclarativeXmlListModel); - d->isComponentComplete = false; - - QDeclarativeXmlQueryEngine *queryEngine = QDeclarativeXmlQueryEngine::instance(qmlEngine(this)); - connect(queryEngine, SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)), - SLOT(queryCompleted(QDeclarativeXmlQueryResult))); - connect(queryEngine, SIGNAL(error(void*,QString)), - SLOT(queryError(void*,QString))); -} - -void QDeclarativeXmlListModel::componentComplete() -{ - Q_D(QDeclarativeXmlListModel); - d->isComponentComplete = true; - reload(); -} - -/*! - \qmlmethod QtQuick2::XmlListModel::reload() - - Reloads the model. - - If no key roles have been specified, all existing model - data is removed, and the model is rebuilt from scratch. - - Otherwise, items are only added if the model does not already - contain items with matching key role values. - - \sa {Using key XML roles}, XmlRole::isKey -*/ -void QDeclarativeXmlListModel::reload() -{ - Q_D(QDeclarativeXmlListModel); - - if (!d->isComponentComplete) - return; - - QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->abort(d->queryId); - d->queryId = -1; - - if (d->size < 0) - d->size = 0; - - if (d->reply) { - d->reply->abort(); - d->deleteReply(); - } - - if (!d->xml.isEmpty()) { - d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache); - d->notifyQueryStarted(false); - - } else if (d->src.isEmpty()) { - d->queryId = XMLLISTMODEL_CLEAR_ID; - d->notifyQueryStarted(false); - QTimer::singleShot(0, this, SLOT(dataCleared())); - - } else { - d->notifyQueryStarted(true); - QNetworkRequest req(d->src); - req.setRawHeader("Accept", "application/xml,*/*"); - d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req); - QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished())); - QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), - 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->deleteReply(); - setSource(url); - return; - } - } - d->redirectCount = 0; - - if (d->reply->error() != QNetworkReply::NoError) { - d->errorString = d->reply->errorString(); - d->deleteReply(); - - int count = this->count(); - d->data.clear(); - d->size = 0; - if (count > 0) { - emit itemsRemoved(0, count); - emit countChanged(); - } - - d->status = Error; - d->queryId = -1; - emit statusChanged(d->status); - } else { - QByteArray data = d->reply->readAll(); - if (data.isEmpty()) { - d->queryId = XMLLISTMODEL_CLEAR_ID; - QTimer::singleShot(0, this, SLOT(dataCleared())); - } else { - d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache); - } - d->deleteReply(); - - d->progress = 1.0; - emit progressChanged(d->progress); - } -} - -void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total) -{ - Q_D(QDeclarativeXmlListModel); - if (d->status == Loading && total > 0) { - d->progress = qreal(received)/total; - emit progressChanged(d->progress); - } -} - -void QDeclarativeXmlListModel::dataCleared() -{ - Q_D(QDeclarativeXmlListModel); - QDeclarativeXmlQueryResult r; - r.queryId = XMLLISTMODEL_CLEAR_ID; - r.size = 0; - r.removed << qMakePair(0, count()); - r.keyRoleResultsCache = d->keyRoleResultsCache; - queryCompleted(r); -} - -void QDeclarativeXmlListModel::queryError(void* object, const QString& error) -{ - // Be extra careful, object may no longer exist, it's just an ID. - Q_D(QDeclarativeXmlListModel); - for (int i=0; iroleObjects.count(); i++) { - if (d->roleObjects.at(i) == static_cast(object)) { - qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error); - return; - } - } - qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error); -} - -void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result) -{ - Q_D(QDeclarativeXmlListModel); - if (result.queryId != d->queryId) - return; - - int origCount = d->size; - bool sizeChanged = result.size != d->size; - - d->size = result.size; - d->data = result.data; - d->keyRoleResultsCache = result.keyRoleResultsCache; - if (d->src.isEmpty() && d->xml.isEmpty()) - d->status = Null; - else - d->status = Ready; - d->errorString.clear(); - d->queryId = -1; - - bool hasKeys = false; - for (int i=0; iroleObjects.count(); i++) { - if (d->roleObjects[i]->isKey()) { - hasKeys = true; - break; - } - } - if (!hasKeys) { - if (!(origCount == 0 && d->size == 0)) { - emit itemsRemoved(0, origCount); - emit itemsInserted(0, d->size); - emit countChanged(); - } - - } else { - for (int i=0; istatus); -} - -QT_END_NAMESPACE - -#include diff --git a/src/declarative/util/qdeclarativexmllistmodel_p.h b/src/declarative/util/qdeclarativexmllistmodel_p.h deleted file mode 100644 index 3d8617f..0000000 --- a/src/declarative/util/qdeclarativexmllistmodel_p.h +++ /dev/null @@ -1,213 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QDECLARATIVEXMLLISTMODEL_H -#define QDECLARATIVEXMLLISTMODEL_H - -#include -#include - -#include -#include - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QDeclarativeContext; -class QDeclarativeXmlListModelRole; -class QDeclarativeXmlListModelPrivate; - -struct QDeclarativeXmlQueryResult { - int queryId; - int size; - QList > data; - QList > inserted; - QList > removed; - QStringList keyRoleResultsCache; -}; - -class Q_AUTOTEST_EXPORT QDeclarativeXmlListModel : public QListModelInterface, public QDeclarativeParserStatus -{ - Q_OBJECT - Q_INTERFACES(QDeclarativeParserStatus) - Q_ENUMS(Status) - - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - Q_PROPERTY(QString xml READ xml WRITE setXml NOTIFY xmlChanged) - Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) - Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations NOTIFY namespaceDeclarationsChanged) - Q_PROPERTY(QDeclarativeListProperty roles READ roleObjects) - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_CLASSINFO("DefaultProperty", "roles") - -public: - QDeclarativeXmlListModel(QObject *parent = 0); - ~QDeclarativeXmlListModel(); - - virtual QHash data(int index, const QList &roles = (QList())) const; - virtual QVariant data(int index, int role) const; - virtual int count() const; - virtual QList roles() const; - virtual QString toString(int role) const; - - QDeclarativeListProperty roleObjects(); - - QUrl source() const; - void setSource(const QUrl&); - - QString xml() const; - void setXml(const QString&); - - QString query() const; - void setQuery(const QString&); - - QString namespaceDeclarations() const; - void setNamespaceDeclarations(const QString&); - - Q_INVOKABLE QDeclarativeV8Handle get(int index) const; - - enum Status { Null, Ready, Loading, Error }; - Status status() const; - qreal progress() const; - - Q_INVOKABLE QString errorString() const; - - virtual void classBegin(); - virtual void componentComplete(); - -Q_SIGNALS: - void statusChanged(QDeclarativeXmlListModel::Status); - void progressChanged(qreal progress); - void countChanged(); - void sourceChanged(); - void xmlChanged(); - void queryChanged(); - void namespaceDeclarationsChanged(); - -public Q_SLOTS: - // ### need to use/expose Expiry to guess when to call this? - // ### property to auto-call this on reasonable Expiry? - // ### LastModified/Age also useful to guess. - // ### Probably also applies to other network-requesting types. - void reload(); - -private Q_SLOTS: - void requestFinished(); - void requestProgress(qint64,qint64); - void dataCleared(); - void queryCompleted(const QDeclarativeXmlQueryResult &); - void queryError(void* object, const QString& error); - -private: - Q_DECLARE_PRIVATE(QDeclarativeXmlListModel) - Q_DISABLE_COPY(QDeclarativeXmlListModel) -}; - -class Q_AUTOTEST_EXPORT QDeclarativeXmlListModelRole : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) - Q_PROPERTY(bool isKey READ isKey WRITE setIsKey NOTIFY isKeyChanged) -public: - QDeclarativeXmlListModelRole() : m_isKey(false) {} - ~QDeclarativeXmlListModelRole() {} - - QString name() const { return m_name; } - void setName(const QString &name) { - if (name == m_name) - return; - m_name = name; - emit nameChanged(); - } - - QString query() const { return m_query; } - void setQuery(const QString &query) - { - if (query.startsWith(QLatin1Char('/'))) { - qmlInfo(this) << tr("An XmlRole query must not start with '/'"); - return; - } - if (m_query == query) - return; - m_query = query; - emit queryChanged(); - } - - bool isKey() const { return m_isKey; } - void setIsKey(bool b) { - if (m_isKey == b) - return; - m_isKey = b; - emit isKeyChanged(); - } - - bool isValid() { - return !m_name.isEmpty() && !m_query.isEmpty(); - } - -Q_SIGNALS: - void nameChanged(); - void queryChanged(); - void isKeyChanged(); - -private: - QString m_name; - QString m_query; - bool m_isKey; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QDeclarativeXmlListModel) -QML_DECLARE_TYPE(QDeclarativeXmlListModelRole) - -QT_END_HEADER - -#endif // QDECLARATIVEXMLLISTMODEL_H diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri index e476db7..3122cd2 100644 --- a/src/declarative/util/util.pri +++ b/src/declarative/util/util.pri @@ -58,12 +58,4 @@ HEADERS += \ $$PWD/qdeclarativechangeset_p.h \ $$PWD/qdeclarativelistcompositor_p.h \ $$PWD/qdeclarativepathinterpolator_p.h \ - $$PWD/qdeclarativesvgparser_p.h - -contains(QT_CONFIG, xmlpatterns) { - QT+=xmlpatterns - SOURCES += $$PWD/qdeclarativexmllistmodel.cpp - HEADERS += $$PWD/qdeclarativexmllistmodel_p.h -} else { - DEFINES += QT_NO_XMLPATTERNS -} + $$PWD/qdeclarativesvgparser_p.h \ No newline at end of file diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 9c66082..35590b2 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -2,4 +2,4 @@ TEMPLATE = subdirs SUBDIRS += qtquick1 qt47 folderlistmodel particles gestures etcprovider contains(QT_CONFIG, qmltest): SUBDIRS += testlib - +contains(QT_CONFIG, xmlpatterns) : SUBDIRS += xmllistmodel \ No newline at end of file diff --git a/src/imports/xmllistmodel/plugin.cpp b/src/imports/xmllistmodel/plugin.cpp new file mode 100644 index 0000000..4e25470 --- /dev/null +++ b/src/imports/xmllistmodel/plugin.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qdeclarativexmllistmodel_p.h" + +QT_BEGIN_NAMESPACE + +class QmlXmlListModelPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.XmlListModel")); + qmlRegisterType(uri,2,0,"XmlListModel"); + qmlRegisterType(uri,2,0,"XmlRole"); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" + +Q_EXPORT_PLUGIN2(qmlxmllistmodelplugin, QT_PREPEND_NAMESPACE(QmlXmlListModelPlugin)); diff --git a/src/imports/xmllistmodel/qdeclarativexmllistmodel.cpp b/src/imports/xmllistmodel/qdeclarativexmllistmodel.cpp new file mode 100644 index 0000000..868d9cb --- /dev/null +++ b/src/imports/xmllistmodel/qdeclarativexmllistmodel.cpp @@ -0,0 +1,1160 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativexmllistmodel_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult) + +QT_BEGIN_NAMESPACE + + +typedef QPair QDeclarativeXmlListRange; + +#define XMLLISTMODEL_CLEAR_ID 0 + +/*! + \qmlclass XmlRole QDeclarativeXmlListModelRole + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The XmlRole element allows you to specify a role for an XmlListModel. + + \sa {QtDeclarative} +*/ + +/*! + \qmlproperty string QtQuick2::XmlRole::name + + The name for the role. This name is used to access the model data for this role. + + For example, the following model has a role named "title", which can be accessed + from the view's delegate: + + \qml + XmlListModel { + id: xmlModel + // ... + XmlRole { + name: "title" + query: "title/string()" + } + } + \endqml + + \qml + ListView { + model: xmlModel + delegate: Text { text: title } + } + \endqml +*/ + +/*! + \qmlproperty string QtQuick2::XmlRole::query + The relative XPath expression query for this role. The query must be relative; it cannot start + with a '/'. + + For example, if there is an XML document like this: + + \quotefile doc/src/snippets/declarative/xmlrole.xml + Here are some valid XPath expressions for XmlRole queries on this document: + + \snippet doc/src/snippets/declarative/xmlrole.qml 0 + \dots 4 + \snippet doc/src/snippets/declarative/xmlrole.qml 1 + + See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information. +*/ + +/*! + \qmlproperty bool QtQuick2::XmlRole::isKey + Defines whether this is a key role. + Key roles are used to to determine whether a set of values should + be updated or added to the XML list model when XmlListModel::reload() + is called. + + \sa XmlListModel +*/ + +struct XmlQueryJob +{ + int queryId; + QByteArray data; + QString query; + QString namespaces; + QStringList roleQueries; + QList roleQueryErrorId; // the ptr to send back if there is an error + QStringList keyRoleQueries; + QStringList keyRoleResultsCache; + QString prefix; +}; + + +class QDeclarativeXmlQueryEngine; +class QDeclarativeXmlQueryThreadObject : public QObject +{ + Q_OBJECT +public: + QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *); + + void processJobs(); + virtual bool event(QEvent *e); + +private: + QDeclarativeXmlQueryEngine *m_queryEngine; +}; + + +class QDeclarativeXmlQueryEngine : public QThread +{ + Q_OBJECT +public: + QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng); + ~QDeclarativeXmlQueryEngine(); + + int doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache); + void abort(int id); + + void processJobs(); + + static QDeclarativeXmlQueryEngine *instance(QDeclarativeEngine *engine); + +signals: + void queryCompleted(const QDeclarativeXmlQueryResult &); + void error(void*, const QString&); + +protected: + void run(); + +private: + void processQuery(XmlQueryJob *job); + void doQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); + void doSubQueryJob(XmlQueryJob *job, QDeclarativeXmlQueryResult *currentResult); + void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const; + void addIndexToRangeList(QList *ranges, int index) const; + + QMutex m_mutex; + QDeclarativeXmlQueryThreadObject *m_threadObject; + QList m_jobs; + QSet m_cancelledJobs; + QAtomicInt m_queryIds; + + QDeclarativeEngine *m_engine; + QObject *m_eventLoopQuitHack; + + static QHash queryEngines; + static QMutex queryEnginesMutex; +}; +QHash QDeclarativeXmlQueryEngine::queryEngines; +QMutex QDeclarativeXmlQueryEngine::queryEnginesMutex; + + +QDeclarativeXmlQueryThreadObject::QDeclarativeXmlQueryThreadObject(QDeclarativeXmlQueryEngine *e) + : m_queryEngine(e) +{ +} + +void QDeclarativeXmlQueryThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarativeXmlQueryThreadObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + m_queryEngine->processJobs(); + return true; + } else { + return QObject::event(e); + } +} + + + +QDeclarativeXmlQueryEngine::QDeclarativeXmlQueryEngine(QDeclarativeEngine *eng) +: QThread(eng), m_threadObject(0), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1), m_engine(eng), m_eventLoopQuitHack(0) +{ + qRegisterMetaType("QDeclarativeXmlQueryResult"); + + m_eventLoopQuitHack = new QObject; + m_eventLoopQuitHack->moveToThread(this); + connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); +} + +QDeclarativeXmlQueryEngine::~QDeclarativeXmlQueryEngine() +{ + queryEnginesMutex.lock(); + queryEngines.remove(m_engine); + queryEnginesMutex.unlock(); + + m_eventLoopQuitHack->deleteLater(); + wait(); +} + +int QDeclarativeXmlQueryEngine::doQuery(QString query, QString namespaces, QByteArray data, QList* roleObjects, QStringList keyRoleResultsCache) { + { + QMutexLocker m1(&m_mutex); + m_queryIds.ref(); + if (m_queryIds.load() <= 0) + m_queryIds.store(1); + } + + XmlQueryJob job; + job.queryId = m_queryIds.load(); + job.data = data; + job.query = QLatin1String("doc($src)") + query; + job.namespaces = namespaces; + job.keyRoleResultsCache = keyRoleResultsCache; + + for (int i=0; icount(); i++) { + if (!roleObjects->at(i)->isValid()) { + job.roleQueries << QString(); + continue; + } + job.roleQueries << roleObjects->at(i)->query(); + job.roleQueryErrorId << static_cast(roleObjects->at(i)); + if (roleObjects->at(i)->isKey()) + job.keyRoleQueries << job.roleQueries.last(); + } + + { + QMutexLocker ml(&m_mutex); + m_jobs.append(job); + if (m_threadObject) + m_threadObject->processJobs(); + } + + return job.queryId; +} + +void QDeclarativeXmlQueryEngine::abort(int id) +{ + QMutexLocker ml(&m_mutex); + if (id != -1) + m_cancelledJobs.insert(id); +} + +void QDeclarativeXmlQueryEngine::run() +{ + m_mutex.lock(); + m_threadObject = new QDeclarativeXmlQueryThreadObject(this); + m_mutex.unlock(); + + processJobs(); + exec(); + + delete m_threadObject; + m_threadObject = 0; +} + +void QDeclarativeXmlQueryEngine::processJobs() +{ + QMutexLocker locker(&m_mutex); + + while (true) { + if (m_jobs.isEmpty()) + return; + + XmlQueryJob currentJob = m_jobs.takeLast(); + while (m_cancelledJobs.remove(currentJob.queryId)) { + if (m_jobs.isEmpty()) + return; + currentJob = m_jobs.takeLast(); + } + + locker.unlock(); + processQuery(¤tJob); + locker.relock(); + } +} + +QDeclarativeXmlQueryEngine *QDeclarativeXmlQueryEngine::instance(QDeclarativeEngine *engine) +{ + queryEnginesMutex.lock(); + QDeclarativeXmlQueryEngine *queryEng = queryEngines.value(engine); + if (!queryEng) { + queryEng = new QDeclarativeXmlQueryEngine(engine); + queryEngines.insert(engine, queryEng); + } + queryEnginesMutex.unlock(); + + return queryEng; +} + +void QDeclarativeXmlQueryEngine::processQuery(XmlQueryJob *job) +{ + QDeclarativeXmlQueryResult result; + result.queryId = job->queryId; + doQueryJob(job, &result); + doSubQueryJob(job, &result); + + { + QMutexLocker ml(&m_mutex); + if (m_cancelledJobs.contains(job->queryId)) { + m_cancelledJobs.remove(job->queryId); + } else { + emit queryCompleted(result); + } + } +} + +void QDeclarativeXmlQueryEngine::doQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QString r; + QXmlQuery query; + QBuffer buffer(¤tJob->data); + buffer.open(QIODevice::ReadOnly); + query.bindVariable(QLatin1String("src"), &buffer); + query.setQuery(currentJob->namespaces + currentJob->query); + query.evaluateTo(&r); + + //always need a single root element + QByteArray xml = "\n" + r.toUtf8() + ""; + QBuffer b(&xml); + b.open(QIODevice::ReadOnly); + + QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces; + QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + + currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/'))); + + //figure out how many items we are dealing with + int count = -1; + { + QXmlResultItems result; + QXmlQuery countquery; + countquery.bindVariable(QLatin1String("inputDocument"), &b); + countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')')); + countquery.evaluateTo(&result); + QXmlItem item(result.next()); + if (item.isAtomicValue()) + count = item.toAtomicValue().toInt(); + } + + currentJob->data = xml; + currentJob->prefix = namespaces + prefix + QLatin1Char('/'); + currentResult->size = (count > 0 ? count : 0); +} + +void QDeclarativeXmlQueryEngine::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const +{ + const QStringList &keysQueries = currentJob.keyRoleQueries; + QString keysQuery; + if (keysQueries.count() == 1) + keysQuery = currentJob.prefix + keysQueries[0]; + else if (keysQueries.count() > 1) + keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")"); + + if (!keysQuery.isEmpty()) { + query->setQuery(keysQuery); + QXmlResultItems resultItems; + query->evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + values->append(item.toAtomicValue().toString()); + item = resultItems.next(); + } + } +} + +void QDeclarativeXmlQueryEngine::addIndexToRangeList(QList *ranges, int index) const { + if (ranges->isEmpty()) + ranges->append(qMakePair(index, 1)); + else if (ranges->last().first + ranges->last().second == index) + ranges->last().second += 1; + else + ranges->append(qMakePair(index, 1)); +} + +void QDeclarativeXmlQueryEngine::doSubQueryJob(XmlQueryJob *currentJob, QDeclarativeXmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QBuffer b(¤tJob->data); + b.open(QIODevice::ReadOnly); + + QXmlQuery subquery; + subquery.bindVariable(QLatin1String("inputDocument"), &b); + + QStringList keyRoleResults; + getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery); + + // See if any values of key roles have been inserted or removed. + + if (currentJob->keyRoleResultsCache.isEmpty()) { + currentResult->inserted << qMakePair(0, currentResult->size); + } else { + if (keyRoleResults != currentJob->keyRoleResultsCache) { + QStringList temp; + for (int i=0; ikeyRoleResultsCache.count(); i++) { + if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i])) + addIndexToRangeList(¤tResult->removed, i); + else + temp << currentJob->keyRoleResultsCache[i]; + } + for (int i=0; iinserted, i); + } + } + } + } + currentResult->keyRoleResultsCache = keyRoleResults; + + // Get the new values for each role. + //### we might be able to condense even further (query for everything in one go) + const QStringList &queries = currentJob->roleQueries; + for (int i = 0; i < queries.size(); ++i) { + QList resultList; + if (!queries[i].isEmpty()) { + subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")")); + if (subquery.isValid()) { + QXmlResultItems resultItems; + subquery.evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + resultList << item.toAtomicValue(); //### we used to trim strings + item = resultItems.next(); + } + } else { + emit error(currentJob->roleQueryErrorId.at(i), queries[i]); + } + } + //### should warn here if things have gone wrong. + while (resultList.count() < currentResult->size) + resultList << QVariant(); + currentResult->data << resultList; + b.seek(0); + } + + //this method is much slower, but works better for incremental loading + /*for (int j = 0; j < m_size; ++j) { + QList resultList; + for (int i = 0; i < m_roleObjects->size(); ++i) { + QDeclarativeXmlListModelRole *role = m_roleObjects->at(i); + subquery.setQuery(m_prefix.arg(j+1) + role->query()); + if (role->isStringList()) { + QStringList data; + subquery.evaluateTo(&data); + resultList << QVariant(data); + //qDebug() << data; + } else { + QString s; + subquery.evaluateTo(&s); + if (role->isCData()) { + //un-escape + s.replace(QLatin1String("<"), QLatin1String("<")); + s.replace(QLatin1String(">"), QLatin1String(">")); + s.replace(QLatin1String("&"), QLatin1String("&")); + } + resultList << s.trimmed(); + //qDebug() << s; + } + b.seek(0); + } + m_modelData << resultList; + }*/ +} + +class QDeclarativeXmlListModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeXmlListModel) +public: + QDeclarativeXmlListModelPrivate() + : isComponentComplete(true), size(-1), highestRole(Qt::UserRole) + , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0) + , queryId(-1), roleObjects(), redirectCount(0) {} + + + void notifyQueryStarted(bool remoteSource) { + Q_Q(QDeclarativeXmlListModel); + progress = remoteSource ? 0.0 : 1.0; + status = QDeclarativeXmlListModel::Loading; + errorString.clear(); + emit q->progressChanged(progress); + emit q->statusChanged(status); + } + + void deleteReply() { + Q_Q(QDeclarativeXmlListModel); + if (reply) { + QObject::disconnect(reply, 0, q, 0); + reply->deleteLater(); + reply = 0; + } + } + + bool isComponentComplete; + QUrl src; + QString xml; + QString query; + QString namespaces; + int size; + QList roles; + QStringList roleNames; + int highestRole; + + QNetworkReply *reply; + QDeclarativeXmlListModel::Status status; + QString errorString; + qreal progress; + int queryId; + QStringList keyRoleResultsCache; + QList roleObjects; + + static void append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role); + static void clear_role(QDeclarativeListProperty *list); + QList > data; + int redirectCount; +}; + + +void QDeclarativeXmlListModelPrivate::append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role) +{ + QDeclarativeXmlListModel *_this = qobject_cast(list->object); + if (_this && role) { + int i = _this->d_func()->roleObjects.count(); + _this->d_func()->roleObjects.append(role); + if (_this->d_func()->roleNames.contains(role->name())) { + qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name()); + return; + } + _this->d_func()->roles.insert(i, _this->d_func()->highestRole); + _this->d_func()->roleNames.insert(i, role->name()); + ++_this->d_func()->highestRole; + } +} + +//### clear needs to invalidate any cached data (in data table) as well +// (and the model should emit the appropriate signals) +void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty *list) +{ + QDeclarativeXmlListModel *_this = static_cast(list->object); + _this->d_func()->roles.clear(); + _this->d_func()->roleNames.clear(); + _this->d_func()->roleObjects.clear(); +} + +/*! + \qmlclass XmlListModel QDeclarativeXmlListModel + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The XmlListModel element is used to specify a read-only model using XPath expressions. + + XmlListModel is used to create a read-only model from XML data. It can be used as a data source + for view elements (such as ListView, PathView, GridView) and other elements that interact with model + data (such as \l Repeater). + + For example, if there is a XML document at http://www.mysite.com/feed.xml like this: + + \code + + + ... + + + A blog post + Sat, 07 Sep 2010 10:00:01 GMT + + + Another blog post + Sat, 07 Sep 2010 15:35:01 GMT + + + + \endcode + + A XmlListModel could create a model from this data, like this: + + \qml + import QtQuick 1.0 + + XmlListModel { + id: xmlModel + source: "http://www.mysite.com/feed.xml" + query: "/rss/channel/item" + + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "pubDate"; query: "pubDate/string()" } + } + \endqml + + The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate + a model item for each \c in the XML document. + + The XmlRole objects define the + model item attributes. Here, each model item will have \c title and \c pubDate + attributes that match the \c title and \c pubDate values of its corresponding \c . + (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.) + + The model could be used in a ListView, like this: + + \qml + ListView { + width: 180; height: 300 + model: xmlModel + delegate: Text { text: title + ": " + pubDate } + } + \endqml + + \image qml-xmllistmodel-example.png + + The XmlListModel data is loaded asynchronously, and \l status + is set to \c XmlListModel.Ready when loading is complete. + Note this means when XmlListModel is used for a view, the view is not + populated until the model is loaded. + + + \section2 Using key XML roles + + You can define certain roles as "keys" so that when reload() is called, + the model will only add and refresh data that contains new values for + these keys. + + For example, if above role for "pubDate" was defined like this instead: + + \qml + XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true } + \endqml + + Then when reload() is called, the model will only add and reload + items with a "pubDate" value that is not already + present in the model. + + This is useful when displaying the contents of XML documents that + are incrementally updated (such as RSS feeds) to avoid repainting the + entire contents of a model in a view. + + If multiple key roles are specified, the model only adds and reload items + with a combined value of all key roles that is not already present in + the model. + + \sa {RSS News} +*/ + +QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent) + : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent) +{ +} + +QDeclarativeXmlListModel::~QDeclarativeXmlListModel() +{ +} + +/*! + \qmlproperty list QtQuick2::XmlListModel::roles + + The roles to make available for this model. +*/ +QDeclarativeListProperty QDeclarativeXmlListModel::roleObjects() +{ + Q_D(QDeclarativeXmlListModel); + QDeclarativeListProperty list(this, d->roleObjects); + list.append = &QDeclarativeXmlListModelPrivate::append_role; + list.clear = &QDeclarativeXmlListModelPrivate::clear_role; + return list; +} + +QHash QDeclarativeXmlListModel::data(int index, const QList &roles) const +{ + Q_D(const QDeclarativeXmlListModel); + QHash rv; + for (int i = 0; i < roles.size(); ++i) { + int role = roles.at(i); + int roleIndex = d->roles.indexOf(role); + rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index)); + } + return rv; +} + +QVariant QDeclarativeXmlListModel::data(int index, int role) const +{ + Q_D(const QDeclarativeXmlListModel); + int roleIndex = d->roles.indexOf(role); + return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index); +} + +/*! + \qmlproperty int QtQuick2::XmlListModel::count + The number of data entries in the model. +*/ +int QDeclarativeXmlListModel::count() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->size; +} + +QList QDeclarativeXmlListModel::roles() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->roles; +} + +QString QDeclarativeXmlListModel::toString(int role) const +{ + Q_D(const QDeclarativeXmlListModel); + int index = d->roles.indexOf(role); + if (index == -1) + return QString(); + return d->roleNames.at(index); +} + +/*! + \qmlproperty url QtQuick2::XmlListModel::source + The location of the XML data source. + + If both \c source and \l xml are set, \l xml is used. +*/ +QUrl QDeclarativeXmlListModel::source() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->src; +} + +void QDeclarativeXmlListModel::setSource(const QUrl &src) +{ + Q_D(QDeclarativeXmlListModel); + if (d->src != src) { + d->src = src; + if (d->xml.isEmpty()) // src is only used if d->xml is not set + reload(); + emit sourceChanged(); + } +} + +/*! + \qmlproperty string QtQuick2::XmlListModel::xml + This property holds the XML data for this model, if set. + + The text is assumed to be UTF-8 encoded. + + If both \l source and \c xml are set, \c xml is used. +*/ +QString QDeclarativeXmlListModel::xml() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->xml; +} + +void QDeclarativeXmlListModel::setXml(const QString &xml) +{ + Q_D(QDeclarativeXmlListModel); + if (d->xml != xml) { + d->xml = xml; + reload(); + emit xmlChanged(); + } +} + +/*! + \qmlproperty string QtQuick2::XmlListModel::query + An absolute XPath query representing the base query for creating model items + from this model's XmlRole objects. The query should start with '/' or '//'. +*/ +QString QDeclarativeXmlListModel::query() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->query; +} + +void QDeclarativeXmlListModel::setQuery(const QString &query) +{ + Q_D(QDeclarativeXmlListModel); + if (!query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << QCoreApplication::translate("QDeclarativeXmlRoleList", "An XmlListModel query must start with '/' or \"//\""); + return; + } + + if (d->query != query) { + d->query = query; + reload(); + emit queryChanged(); + } +} + +/*! + \qmlproperty string QtQuick2::XmlListModel::namespaceDeclarations + The namespace declarations to be used in the XPath queries. + + The namespaces should be declared as in XQuery. For example, if a requested document + at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom", + this can be declared as the default namespace: + + \qml + XmlListModel { + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';" + + XmlRole { name: "title"; query: "title/string()" } + } + \endqml +*/ +QString QDeclarativeXmlListModel::namespaceDeclarations() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->namespaces; +} + +void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarations) +{ + Q_D(QDeclarativeXmlListModel); + if (d->namespaces != declarations) { + d->namespaces = declarations; + reload(); + emit namespaceDeclarationsChanged(); + } +} + +/*! + \qmlmethod object QtQuick2::XmlListModel::get(int index) + + Returns the item at \a index in the model. + + For example, for a model like this: + + \qml + XmlListModel { + id: model + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + XmlRole { name: "title"; query: "title/string()" } + } + \endqml + + This will access the \c title value for the first item in the model: + + \js + var title = model.get(0).title; + \endjs +*/ +QDeclarativeV8Handle QDeclarativeXmlListModel::get(int index) const +{ + // Must be called with a context and handle scope + Q_D(const QDeclarativeXmlListModel); + + if (index < 0 || index >= count()) + return QDeclarativeV8Handle::fromHandle(v8::Undefined()); + + QDeclarativeEngine *engine = qmlContext(this)->engine(); + QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(engine); + v8::Local rv = v8::Object::New(); + for (int ii = 0; ii < d->roleObjects.count(); ++ii) + rv->Set(v8engine->toString(d->roleObjects[ii]->name()), + v8engine->fromVariant(d->data.value(ii).value(index))); + + return QDeclarativeV8Handle::fromHandle(rv); +} + +/*! + \qmlproperty enumeration QtQuick2::XmlListModel::status + Specifies the model loading status, which can be one of the following: + + \list + \o XmlListModel.Null - No XML data has been set for this model. + \o XmlListModel.Ready - The XML data has been loaded into the model. + \o XmlListModel.Loading - The model is in the process of reading and loading XML data. + \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details + about the error. + \endlist + + \sa progress + +*/ +QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->status; +} + +/*! + \qmlproperty real QtQuick2::XmlListModel::progress + + This indicates the current progress of the downloading of the XML data + source. This value ranges from 0.0 (no data downloaded) to + 1.0 (all data downloaded). If the XML data is not from a remote source, + the progress becomes 1.0 as soon as the data is read. + + Note that when the progress is 1.0, the XML data has been downloaded, but + it is yet to be loaded into the model at this point. Use the status + property to find out when the XML data has been read and loaded into + the model. + + \sa status, source +*/ +qreal QDeclarativeXmlListModel::progress() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->progress; +} + +/*! + \qmlmethod void QtQuick2::XmlListModel::errorString() + + Returns a string description of the last error that occurred + if \l status is XmlListModel::Error. +*/ +QString QDeclarativeXmlListModel::errorString() const +{ + Q_D(const QDeclarativeXmlListModel); + return d->errorString; +} + +void QDeclarativeXmlListModel::classBegin() +{ + Q_D(QDeclarativeXmlListModel); + d->isComponentComplete = false; + + QDeclarativeXmlQueryEngine *queryEngine = QDeclarativeXmlQueryEngine::instance(qmlEngine(this)); + connect(queryEngine, SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)), + SLOT(queryCompleted(QDeclarativeXmlQueryResult))); + connect(queryEngine, SIGNAL(error(void*,QString)), + SLOT(queryError(void*,QString))); +} + +void QDeclarativeXmlListModel::componentComplete() +{ + Q_D(QDeclarativeXmlListModel); + d->isComponentComplete = true; + reload(); +} + +/*! + \qmlmethod QtQuick2::XmlListModel::reload() + + Reloads the model. + + If no key roles have been specified, all existing model + data is removed, and the model is rebuilt from scratch. + + Otherwise, items are only added if the model does not already + contain items with matching key role values. + + \sa {Using key XML roles}, XmlRole::isKey +*/ +void QDeclarativeXmlListModel::reload() +{ + Q_D(QDeclarativeXmlListModel); + + if (!d->isComponentComplete) + return; + + QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->abort(d->queryId); + d->queryId = -1; + + if (d->size < 0) + d->size = 0; + + if (d->reply) { + d->reply->abort(); + d->deleteReply(); + } + + if (!d->xml.isEmpty()) { + d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache); + d->notifyQueryStarted(false); + + } else if (d->src.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + d->notifyQueryStarted(false); + QTimer::singleShot(0, this, SLOT(dataCleared())); + + } else { + d->notifyQueryStarted(true); + QNetworkRequest req(d->src); + req.setRawHeader("Accept", "application/xml,*/*"); + d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished())); + QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), + 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->deleteReply(); + setSource(url); + return; + } + } + d->redirectCount = 0; + + if (d->reply->error() != QNetworkReply::NoError) { + d->errorString = d->reply->errorString(); + d->deleteReply(); + + int count = this->count(); + d->data.clear(); + d->size = 0; + if (count > 0) { + emit itemsRemoved(0, count); + emit countChanged(); + } + + d->status = Error; + d->queryId = -1; + emit statusChanged(d->status); + } else { + QByteArray data = d->reply->readAll(); + if (data.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + QTimer::singleShot(0, this, SLOT(dataCleared())); + } else { + d->queryId = QDeclarativeXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache); + } + d->deleteReply(); + + d->progress = 1.0; + emit progressChanged(d->progress); + } +} + +void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total) +{ + Q_D(QDeclarativeXmlListModel); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QDeclarativeXmlListModel::dataCleared() +{ + Q_D(QDeclarativeXmlListModel); + QDeclarativeXmlQueryResult r; + r.queryId = XMLLISTMODEL_CLEAR_ID; + r.size = 0; + r.removed << qMakePair(0, count()); + r.keyRoleResultsCache = d->keyRoleResultsCache; + queryCompleted(r); +} + +void QDeclarativeXmlListModel::queryError(void* object, const QString& error) +{ + // Be extra careful, object may no longer exist, it's just an ID. + Q_D(QDeclarativeXmlListModel); + for (int i=0; iroleObjects.count(); i++) { + if (d->roleObjects.at(i) == static_cast(object)) { + qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error); + return; + } + } + qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error); +} + +void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result) +{ + Q_D(QDeclarativeXmlListModel); + if (result.queryId != d->queryId) + return; + + int origCount = d->size; + bool sizeChanged = result.size != d->size; + + d->size = result.size; + d->data = result.data; + d->keyRoleResultsCache = result.keyRoleResultsCache; + if (d->src.isEmpty() && d->xml.isEmpty()) + d->status = Null; + else + d->status = Ready; + d->errorString.clear(); + d->queryId = -1; + + bool hasKeys = false; + for (int i=0; iroleObjects.count(); i++) { + if (d->roleObjects[i]->isKey()) { + hasKeys = true; + break; + } + } + if (!hasKeys) { + if (!(origCount == 0 && d->size == 0)) { + emit itemsRemoved(0, origCount); + emit itemsInserted(0, d->size); + emit countChanged(); + } + + } else { + for (int i=0; istatus); +} + +QT_END_NAMESPACE + +#include \ No newline at end of file diff --git a/src/imports/xmllistmodel/qdeclarativexmllistmodel_p.h b/src/imports/xmllistmodel/qdeclarativexmllistmodel_p.h new file mode 100644 index 0000000..c12d3d9 --- /dev/null +++ b/src/imports/xmllistmodel/qdeclarativexmllistmodel_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEXMLLISTMODEL_H +#define QDECLARATIVEXMLLISTMODEL_H + +#include +#include + +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeContext; +class QDeclarativeXmlListModelRole; +class QDeclarativeXmlListModelPrivate; + +struct QDeclarativeXmlQueryResult { + int queryId; + int size; + QList > data; + QList > inserted; + QList > removed; + QStringList keyRoleResultsCache; +}; + +class Q_DECLARATIVE_EXPORT QDeclarativeXmlListModel : public QListModelInterface, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_ENUMS(Status) + + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString xml READ xml WRITE setXml NOTIFY xmlChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations NOTIFY namespaceDeclarationsChanged) + Q_PROPERTY(QDeclarativeListProperty roles READ roleObjects) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_CLASSINFO("DefaultProperty", "roles") + +public: + QDeclarativeXmlListModel(QObject *parent = 0); + ~QDeclarativeXmlListModel(); + + virtual QHash data(int index, const QList &roles = (QList())) const; + virtual QVariant data(int index, int role) const; + virtual int count() const; + virtual QList roles() const; + virtual QString toString(int role) const; + + QDeclarativeListProperty roleObjects(); + + QUrl source() const; + void setSource(const QUrl&); + + QString xml() const; + void setXml(const QString&); + + QString query() const; + void setQuery(const QString&); + + QString namespaceDeclarations() const; + void setNamespaceDeclarations(const QString&); + + Q_INVOKABLE QDeclarativeV8Handle get(int index) const; + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + Q_INVOKABLE QString errorString() const; + + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void statusChanged(QDeclarativeXmlListModel::Status); + void progressChanged(qreal progress); + void countChanged(); + void sourceChanged(); + void xmlChanged(); + void queryChanged(); + void namespaceDeclarationsChanged(); + +public Q_SLOTS: + // ### need to use/expose Expiry to guess when to call this? + // ### property to auto-call this on reasonable Expiry? + // ### LastModified/Age also useful to guess. + // ### Probably also applies to other network-requesting types. + void reload(); + +private Q_SLOTS: + void requestFinished(); + void requestProgress(qint64,qint64); + void dataCleared(); + void queryCompleted(const QDeclarativeXmlQueryResult &); + void queryError(void* object, const QString& error); + +private: + Q_DECLARE_PRIVATE(QDeclarativeXmlListModel) + Q_DISABLE_COPY(QDeclarativeXmlListModel) +}; + +class Q_AUTOTEST_EXPORT QDeclarativeXmlListModelRole : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(bool isKey READ isKey WRITE setIsKey NOTIFY isKeyChanged) +public: + QDeclarativeXmlListModelRole() : m_isKey(false) {} + ~QDeclarativeXmlListModelRole() {} + + QString name() const { return m_name; } + void setName(const QString &name) { + if (name == m_name) + return; + m_name = name; + emit nameChanged(); + } + + QString query() const { return m_query; } + void setQuery(const QString &query) + { + if (query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << tr("An XmlRole query must not start with '/'"); + return; + } + if (m_query == query) + return; + m_query = query; + emit queryChanged(); + } + + bool isKey() const { return m_isKey; } + void setIsKey(bool b) { + if (m_isKey == b) + return; + m_isKey = b; + emit isKeyChanged(); + } + + bool isValid() { + return !m_name.isEmpty() && !m_query.isEmpty(); + } + +Q_SIGNALS: + void nameChanged(); + void queryChanged(); + void isKeyChanged(); + +private: + QString m_name; + QString m_query; + bool m_isKey; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeXmlListModel) +QML_DECLARE_TYPE(QDeclarativeXmlListModelRole) + +QT_END_HEADER + +#endif // QDECLARATIVEXMLLISTMODEL_H diff --git a/src/imports/xmllistmodel/qmldir b/src/imports/xmllistmodel/qmldir new file mode 100644 index 0000000..dd39bcd --- /dev/null +++ b/src/imports/xmllistmodel/qmldir @@ -0,0 +1 @@ +plugin qmlxmllistmodelplugin \ No newline at end of file diff --git a/src/imports/xmllistmodel/xmllistmodel.pro b/src/imports/xmllistmodel/xmllistmodel.pro new file mode 100644 index 0000000..e3897e0 --- /dev/null +++ b/src/imports/xmllistmodel/xmllistmodel.pro @@ -0,0 +1,16 @@ +TARGET = qmlxmllistmodelplugin +TARGETPATH = QtQuick/XmlListModel +include(../qimportbase.pri) + +QT+= declarative xmlpatterns declarative-private v8-private core-private + +SOURCES += qdeclarativexmllistmodel.cpp plugin.cpp +HEADERS += qdeclarativexmllistmodel_p.h + +DESTDIR = $$QT.declarative.imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +INSTALLS += target qmldir \ No newline at end of file diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/empty.xml b/tests/auto/declarative/qdeclarativexmllistmodel/data/empty.xml new file mode 100644 index 0000000..e69de29 diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/get.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/get.qml index 2466c03..509da71 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/get.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/get.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { source: "model.xml" @@ -58,4 +59,3 @@ XmlListModel { postTest = true; } } - diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/model.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/model.qml index bf47f43..2df3927 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/model.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/model.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { source: "model.xml" diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/propertychanges.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/propertychanges.qml index cc05b7c..f8a97bf 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/propertychanges.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/propertychanges.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { source: "model.xml" diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/recipes.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/recipes.qml index 3834ccc..dc609e9 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/recipes.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/recipes.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { source: "recipes.xml" diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/roleCrash.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/roleCrash.qml index 50b8c0d..6a7059b 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/roleCrash.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/roleCrash.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { id: model diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/roleErrors.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/roleErrors.qml index c86a372..91664b6 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/roleErrors.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/roleErrors.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { source: "model.xml" diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/roleKeys.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/roleKeys.qml index 74eca3e..9f667d8 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/roleKeys.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/roleKeys.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { query: "/data/item" @@ -10,4 +11,3 @@ XmlListModel { nameRole.isKey = false; } } - diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/testtypes.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/testtypes.qml index 1f987e0..5ec1ffa 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/testtypes.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/testtypes.qml @@ -1,8 +1,8 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { query: "/data" XmlRole { name: "stringValue"; query: "a-string/string()" } XmlRole { name: "numberValue"; query: "a-number/number()" } } - diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/data/unique.qml b/tests/auto/declarative/qdeclarativexmllistmodel/data/unique.qml index e4ed533..322a2e4 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/data/unique.qml +++ b/tests/auto/declarative/qdeclarativexmllistmodel/data/unique.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 XmlListModel { source: "model.xml" diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro b/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro index ae55237..6ece73e 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro +++ b/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro @@ -3,11 +3,10 @@ TARGET = tst_qdeclarativexmllistmodel macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativexmllistmodel.cpp - testDataFiles.files = data testDataFiles.path = . DEPLOYMENT += testDataFiles CONFIG += parallel_test -QT += core-private gui-private v8-private declarative-private network testlib xmlpatterns +QT += core-private gui-private v8-private declarative-private network testlib xmlpatterns \ No newline at end of file diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp b/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp index 2b66e67..7a119dd 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp +++ b/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp @@ -38,12 +38,11 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include #include #include #include - +#include #include #include #include @@ -53,17 +52,17 @@ #include #include #include "../shared/util.h" +#include #include #include -#include +#include typedef QPair QDeclarativeXmlListRange; typedef QList QDeclarativeXmlModelData; Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QDeclarativeXmlModelData) -Q_DECLARE_METATYPE(QDeclarativeXmlListModel::Status) class tst_qdeclarativexmllistmodel : public QObject @@ -71,10 +70,11 @@ class tst_qdeclarativexmllistmodel : public QObject Q_OBJECT public: tst_qdeclarativexmllistmodel() {} + enum Status { Null, Ready, Loading, Error }; private slots: void initTestCase() { - qRegisterMetaType("QDeclarativeXmlListModel::Status"); + qRegisterMetaType("tst_qdeclarativexmllistmodel::Status"); } void buildModel(); @@ -104,6 +104,12 @@ private slots: void roleCrash(); private: + QString errorString(QListModelInterface* model) { + QString ret; + QMetaObject::invokeMethod(model, "errorString", Q_RETURN_ARG(QString, ret)); + return ret; + } + QString makeItemXmlAndData(const QString &data, QDeclarativeXmlModelData *modelData = 0) const { if (modelData) @@ -146,7 +152,7 @@ private: QDeclarativeEngine engine; }; - +Q_DECLARE_METATYPE(tst_qdeclarativexmllistmodel::Status) class CustomNetworkAccessManagerFactory : public QObject, public QDeclarativeNetworkAccessManagerFactory { Q_OBJECT @@ -188,18 +194,14 @@ QNetworkAccessManager *CustomNetworkAccessManagerFactory::create(QObject *parent void tst_qdeclarativexmllistmodel::buildModel() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("model.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 9); - QList roles; - roles << Qt::UserRole << Qt::UserRole + 1 << Qt::UserRole + 2 << Qt::UserRole + 3; - QHash data = model->data(3, roles); - QVERIFY(data.count() == 4); - QCOMPARE(data.value(Qt::UserRole).toString(), QLatin1String("Spot")); - QCOMPARE(data.value(Qt::UserRole+1).toString(), QLatin1String("Dog")); - QCOMPARE(data.value(Qt::UserRole+2).toInt(), 9); - QCOMPARE(data.value(Qt::UserRole+3).toString(), QLatin1String("Medium")); + QCOMPARE(model->data(3, Qt::UserRole).toString(), QLatin1String("Spot")); + QCOMPARE(model->data(3, Qt::UserRole+1).toString(), QLatin1String("Dog")); + QCOMPARE(model->data(3, Qt::UserRole+2).toInt(), 9); + QCOMPARE(model->data(3, Qt::UserRole+3).toString(), QLatin1String("Medium")); delete model; } @@ -211,10 +213,10 @@ void tst_qdeclarativexmllistmodel::testTypes() QFETCH(QVariant, expectedValue); QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("testtypes.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); - model->setXml(xml.toUtf8()); - model->reload(); + model->setProperty("xml",xml.toUtf8()); + QMetaObject::invokeMethod(model, "reload"); QTRY_COMPARE(model->count(), 1); int role = -1; @@ -271,15 +273,11 @@ void tst_qdeclarativexmllistmodel::testTypes_data() void tst_qdeclarativexmllistmodel::cdata() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("recipes.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 5); - QList roles; - roles << Qt::UserRole + 2; - QHash data = model->data(2, roles); - QVERIFY(data.count() == 1); - QVERIFY(data.value(Qt::UserRole+2).toString().startsWith(QLatin1String(""))); + QVERIFY(model->data(2, Qt::UserRole+2).toString().startsWith(QLatin1String(""))); delete model; } @@ -287,14 +285,10 @@ void tst_qdeclarativexmllistmodel::cdata() void tst_qdeclarativexmllistmodel::attributes() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("recipes.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 5); - QList roles; - roles << Qt::UserRole; - QHash data = model->data(2, roles); - QVERIFY(data.count() == 1); - QCOMPARE(data.value(Qt::UserRole).toString(), QLatin1String("Vegetable Soup")); + QCOMPARE(model->data(2, Qt::UserRole).toString(), QLatin1String("Vegetable Soup")); delete model; } @@ -302,7 +296,7 @@ void tst_qdeclarativexmllistmodel::attributes() void tst_qdeclarativexmllistmodel::roles() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("model.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 9); @@ -319,26 +313,22 @@ void tst_qdeclarativexmllistmodel::roles() void tst_qdeclarativexmllistmodel::roleErrors() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("roleErrors.qml"))); - QTest::ignoreMessage(QtWarningMsg, (QUrl::fromLocalFile(TESTDATA("roleErrors.qml")).toString() + ":6:5: QML XmlRole: An XmlRole query must not start with '/'").toUtf8().constData()); - QTest::ignoreMessage(QtWarningMsg, (QUrl::fromLocalFile(TESTDATA("roleErrors.qml")).toString() + ":9:5: QML XmlRole: invalid query: \"age/\"").toUtf8().constData()); + QTest::ignoreMessage(QtWarningMsg, (QUrl::fromLocalFile(TESTDATA("roleErrors.qml")).toString() + ":7:5: QML XmlRole: An XmlRole query must not start with '/'").toUtf8().constData()); + QTest::ignoreMessage(QtWarningMsg, (QUrl::fromLocalFile(TESTDATA("roleErrors.qml")).toString() + ":10:5: QML XmlRole: invalid query: \"age/\"").toUtf8().constData()); //### make sure we receive all expected warning messages. - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 9); - QList roles; - roles << Qt::UserRole << Qt::UserRole + 1 << Qt::UserRole + 2 << Qt::UserRole + 3; - QHash data = model->data(3, roles); - QVERIFY(data.count() == 4); //### should any of these return valid values? - QCOMPARE(data.value(Qt::UserRole), QVariant()); - QCOMPARE(data.value(Qt::UserRole+1), QVariant()); - QCOMPARE(data.value(Qt::UserRole+2), QVariant()); + QCOMPARE(model->data(3, Qt::UserRole), QVariant()); + QCOMPARE(model->data(3, Qt::UserRole+1), QVariant()); + QCOMPARE(model->data(3, Qt::UserRole+2), QVariant()); QEXPECT_FAIL("", "QTBUG-10797", Continue); - QCOMPARE(data.value(Qt::UserRole+3), QVariant()); + QCOMPARE(model->data(3, Qt::UserRole+3), QVariant()); delete model; } @@ -346,8 +336,8 @@ void tst_qdeclarativexmllistmodel::roleErrors() void tst_qdeclarativexmllistmodel::uniqueRoleNames() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("unique.qml"))); - QTest::ignoreMessage(QtWarningMsg, (QUrl::fromLocalFile(TESTDATA("unique.qml")).toString() + ":7:5: QML XmlRole: \"name\" duplicates a previous role name and will be disabled.").toUtf8().constData()); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QTest::ignoreMessage(QtWarningMsg, (QUrl::fromLocalFile(TESTDATA("unique.qml")).toString() + ":8:5: QML XmlRole: \"name\" duplicates a previous role name and will be disabled.").toUtf8().constData()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 9); @@ -364,31 +354,32 @@ void tst_qdeclarativexmllistmodel::xml() QFETCH(int, count); QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("model.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); - QSignalSpy spy(model, SIGNAL(statusChanged(QDeclarativeXmlListModel::Status))); + QListModelInterface *model = qobject_cast(component.create()); - QVERIFY(model->errorString().isEmpty()); - QCOMPARE(model->progress(), qreal(0.0)); - QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading); + QSignalSpy spy(model, SIGNAL(statusChanged(QDeclarativeXmlListModel::Status))); + QVERIFY(errorString(model).isEmpty()); + QCOMPARE(model->property("progress").toDouble(), qreal(0.0)); + QCOMPARE(model->property("status").toInt(), static_cast(Loading)); QTRY_COMPARE(spy.count(), 1); spy.clear(); - QCOMPARE(model->status(), QDeclarativeXmlListModel::Ready); - QVERIFY(model->errorString().isEmpty()); - QCOMPARE(model->progress(), qreal(1.0)); + QTest::qWait(50); + QCOMPARE(model->property("status").toInt(), static_cast(Ready)); + QVERIFY(errorString(model).isEmpty()); + QCOMPARE(model->property("progress").toDouble(), qreal(1.0)); QCOMPARE(model->count(), 9); // if xml is empty (i.e. clearing) it won't have any effect if a source is set if (xml.isEmpty()) - model->setSource(QUrl()); - model->setXml(xml); - QCOMPARE(model->progress(), qreal(1.0)); // immediately goes to 1.0 if using setXml() + model->setProperty("source",QUrl()); + model->setProperty("xml",xml); + QCOMPARE(model->property("progress").toDouble(), qreal(1.0)); // immediately goes to 1.0 if using setXml() QTRY_COMPARE(spy.count(), 1); spy.clear(); - QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading); + QCOMPARE(model->property("status").toInt(), static_cast(Loading)); QTRY_COMPARE(spy.count(), 1); spy.clear(); if (xml.isEmpty()) - QCOMPARE(model->status(), QDeclarativeXmlListModel::Null); + QCOMPARE(model->property("status").toInt(), static_cast(Null)); else - QCOMPARE(model->status(), QDeclarativeXmlListModel::Ready); - QVERIFY(model->errorString().isEmpty()); + QCOMPARE(model->property("status").toInt(), static_cast(Ready)); + QVERIFY(errorString(model).isEmpty()); QCOMPARE(model->count(), count); delete model; @@ -413,9 +404,9 @@ void tst_qdeclarativexmllistmodel::headers() qmlEng.setNetworkAccessManagerFactory(&factory); QDeclarativeComponent component(&qmlEng, QUrl::fromLocalFile(TESTDATA("model.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); - QTRY_COMPARE(model->status(), QDeclarativeXmlListModel::Ready); + QTRY_COMPARE(model->property("status").toInt(), static_cast(Ready)); QVariantMap expectedHeaders; expectedHeaders["Accept"] = "application/xml,*/*"; @@ -433,28 +424,28 @@ void tst_qdeclarativexmllistmodel::source() { QFETCH(QUrl, source); QFETCH(int, count); - QFETCH(QDeclarativeXmlListModel::Status, status); + QFETCH(Status, status); QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("model.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QSignalSpy spy(model, SIGNAL(statusChanged(QDeclarativeXmlListModel::Status))); - QVERIFY(model->errorString().isEmpty()); - QCOMPARE(model->progress(), qreal(0.0)); - QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading); + QVERIFY(errorString(model).isEmpty()); + QCOMPARE(model->property("progress").toDouble(), qreal(0.0)); + QCOMPARE(model->property("status").toInt(), static_cast(Loading)); QTRY_COMPARE(spy.count(), 1); spy.clear(); - QCOMPARE(model->status(), QDeclarativeXmlListModel::Ready); - QVERIFY(model->errorString().isEmpty()); - QCOMPARE(model->progress(), qreal(1.0)); + QCOMPARE(model->property("status").toInt(), static_cast(Ready)); + QVERIFY(errorString(model).isEmpty()); + QCOMPARE(model->property("progress").toDouble(), qreal(1.0)); QCOMPARE(model->count(), 9); - model->setSource(source); - if (model->source().isEmpty()) - QCOMPARE(model->status(), QDeclarativeXmlListModel::Null); - QCOMPARE(model->progress(), qreal(0.0)); + model->setProperty("source",source); + if (model->property("source").toString().isEmpty()) + QCOMPARE(model->property("status").toInt(), static_cast(Null)); + QCOMPARE(model->property("progress").toDouble(), qreal(0.0)); QTRY_COMPARE(spy.count(), 1); spy.clear(); - QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading); - QVERIFY(model->errorString().isEmpty()); + QCOMPARE(model->property("status").toInt(), static_cast(Loading)); + QVERIFY(errorString(model).isEmpty()); QEventLoop loop; QTimer timer; @@ -464,19 +455,19 @@ void tst_qdeclarativexmllistmodel::source() timer.start(20000); loop.exec(); - if (spy.count() == 0 && status != QDeclarativeXmlListModel::Ready) { + if (spy.count() == 0 && status != Ready) { qWarning("QDeclarativeXmlListModel invalid source test timed out"); } else { QCOMPARE(spy.count(), 1); spy.clear(); } - QCOMPARE(model->status(), status); + QCOMPARE(model->property("status").toInt(), static_cast(status)); QCOMPARE(model->count(), count); - if (status == QDeclarativeXmlListModel::Ready) - QCOMPARE(model->progress(), qreal(1.0)); + if (status == Ready) + QCOMPARE(model->property("progress").toDouble(), qreal(1.0)); - QCOMPARE(model->errorString().isEmpty(), status == QDeclarativeXmlListModel::Ready); + QCOMPARE(errorString(model).isEmpty(), status == Ready); delete model; } @@ -485,29 +476,25 @@ void tst_qdeclarativexmllistmodel::source_data() { QTest::addColumn("source"); QTest::addColumn("count"); - QTest::addColumn("status"); + QTest::addColumn("status"); - QTest::newRow("valid") << QUrl::fromLocalFile(TESTDATA("model2.xml")) << 2 << QDeclarativeXmlListModel::Ready; - QTest::newRow("invalid") << QUrl("http://blah.blah/blah.xml") << 0 << QDeclarativeXmlListModel::Error; + QTest::newRow("valid") << QUrl::fromLocalFile(TESTDATA("model2.xml")) << 2 << Ready; + QTest::newRow("invalid") << QUrl("http://blah.blah/blah.xml") << 0 << Error; // empty file QTemporaryFile *temp = new QTemporaryFile(this); if (temp->open()) - QTest::newRow("empty file") << QUrl::fromLocalFile(temp->fileName()) << 0 << QDeclarativeXmlListModel::Ready; + QTest::newRow("empty file") << QUrl::fromLocalFile(temp->fileName()) << 0 << Ready; temp->close(); } void tst_qdeclarativexmllistmodel::data() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("model.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); - QHash blank; - for (int i=0; iroles().count(); i++) - blank.insert(model->roles()[i], QVariant()); for (int i=0; i<9; i++) { - QCOMPARE(model->data(i, model->roles()), blank); for (int j=0; jroles().count(); j++) { QCOMPARE(model->data(i, j), QVariant()); } @@ -520,7 +507,7 @@ void tst_qdeclarativexmllistmodel::data() void tst_qdeclarativexmllistmodel::get() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("get.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); @@ -541,20 +528,19 @@ void tst_qdeclarativexmllistmodel::reload() // reload() is called. QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("model.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 9); QSignalSpy spyInsert(model, SIGNAL(itemsInserted(int,int))); QSignalSpy spyRemove(model, SIGNAL(itemsRemoved(int,int))); - QSignalSpy spyCount(model, SIGNAL(countChanged())); - + QSignalSpy spyCount(model, SIGNAL(countChanged())); //reload multiple times to test the xml query aborting - model->reload(); - model->reload(); + QMetaObject::invokeMethod(model, "reload"); + QMetaObject::invokeMethod(model, "reload"); QCoreApplication::processEvents(); - model->reload(); - model->reload(); + QMetaObject::invokeMethod(model, "reload"); + QMetaObject::invokeMethod(model, "reload"); QTRY_COMPARE(spyCount.count(), 1); QTRY_COMPARE(spyInsert.count(), 1); QTRY_COMPARE(spyRemove.count(), 1); @@ -582,17 +568,17 @@ void tst_qdeclarativexmllistmodel::useKeys() QFETCH(QList, removeRanges); QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("roleKeys.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); - model->setXml(oldXml); + model->setProperty("xml",oldXml); QTRY_COMPARE(model->count(), oldCount); QSignalSpy spyInsert(model, SIGNAL(itemsInserted(int,int))); QSignalSpy spyRemove(model, SIGNAL(itemsRemoved(int,int))); QSignalSpy spyCount(model, SIGNAL(countChanged())); - model->setXml(newXml); + model->setProperty("xml",newXml); if (oldCount != newData.count()) { QTRY_COMPARE(model->count(), newData.count()); @@ -733,23 +719,23 @@ void tst_qdeclarativexmllistmodel::noKeysValueChanges() // since 'sport' is not marked as a key. QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("roleKeys.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QString xml; xml = makeItemXmlAndData("name=A,age=25,sport=Football;name=B,age=35,sport=Athletics"); - model->setXml(xml); + model->setProperty("xml",xml); QTRY_COMPARE(model->count(), 2); - model->setXml(""); + model->setProperty("xml",""); QSignalSpy spyInsert(model, SIGNAL(itemsInserted(int,int))); QSignalSpy spyRemove(model, SIGNAL(itemsRemoved(int,int))); QSignalSpy spyCount(model, SIGNAL(countChanged())); xml = makeItemXmlAndData("name=A,age=25,sport=AussieRules;name=B,age=35,sport=Athletics"); - model->setXml(xml); + model->setProperty("xml",xml); // wait for the new xml data to be set, and verify no signals were emitted QTRY_VERIFY(model->data(0, model->roles()[2]).toString() != QLatin1String("Football")); @@ -771,21 +757,21 @@ void tst_qdeclarativexmllistmodel::keysChanged() // if no keys are set). QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("roleKeys.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QString xml = makeItemXmlAndData("name=A,age=25,sport=Football;name=B,age=35,sport=Athletics"); - model->setXml(xml); + model->setProperty("xml",xml); QTRY_COMPARE(model->count(), 2); - model->setXml(""); + model->setProperty("xml",""); QSignalSpy spyInsert(model, SIGNAL(itemsInserted(int,int))); QSignalSpy spyRemove(model, SIGNAL(itemsRemoved(int,int))); QSignalSpy spyCount(model, SIGNAL(countChanged())); QVERIFY(QMetaObject::invokeMethod(model, "disableNameKey")); - model->setXml(xml); + model->setProperty("xml",xml); QTRY_VERIFY(spyInsert.count() > 0 && spyRemove.count() > 0); @@ -808,11 +794,11 @@ void tst_qdeclarativexmllistmodel::threading() QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("roleKeys.qml"))); - QDeclarativeXmlListModel *m1 = qobject_cast(component.create()); + QListModelInterface *m1 = qobject_cast(component.create()); QVERIFY(m1 != 0); - QDeclarativeXmlListModel *m2 = qobject_cast(component.create()); + QListModelInterface *m2 = qobject_cast(component.create()); QVERIFY(m2 != 0); - QDeclarativeXmlListModel *m3 = qobject_cast(component.create()); + QListModelInterface *m3 = qobject_cast(component.create()); QVERIFY(m3 != 0); for (int dataCount=0; dataCountsetXml(makeItemXmlAndData(data1)); - m2->setXml(makeItemXmlAndData(data2)); - m3->setXml(makeItemXmlAndData(data3)); + m1->setProperty("xml",makeItemXmlAndData(data1)); + m2->setProperty("xml",makeItemXmlAndData(data2)); + m3->setProperty("xml",makeItemXmlAndData(data3)); QCoreApplication::processEvents(); - m2->setXml(makeItemXmlAndData(data2)); - m1->setXml(makeItemXmlAndData(data1)); - m2->setXml(makeItemXmlAndData(data2)); + m2->setProperty("xml",makeItemXmlAndData(data2)); + m1->setProperty("xml",makeItemXmlAndData(data1)); + m2->setProperty("xml",makeItemXmlAndData(data2)); QCoreApplication::processEvents(); - m3->setXml(makeItemXmlAndData(data3)); + m3->setProperty("xml",makeItemXmlAndData(data3)); QCoreApplication::processEvents(); - m2->setXml(makeItemXmlAndData(data2)); - m1->setXml(makeItemXmlAndData(data1)); - m2->setXml(makeItemXmlAndData(data2)); - m3->setXml(makeItemXmlAndData(data3)); + m2->setProperty("xml",makeItemXmlAndData(data2)); + m1->setProperty("xml",makeItemXmlAndData(data1)); + m2->setProperty("xml",makeItemXmlAndData(data2)); + m3->setProperty("xml",makeItemXmlAndData(data3)); QCoreApplication::processEvents(); - m2->setXml(makeItemXmlAndData(data2)); - m3->setXml(makeItemXmlAndData(data3)); - m3->setXml(makeItemXmlAndData(data3)); + m2->setProperty("xml",makeItemXmlAndData(data2)); + m3->setProperty("xml",makeItemXmlAndData(data3)); + m3->setProperty("xml",makeItemXmlAndData(data3)); QCoreApplication::processEvents(); QTRY_VERIFY(m1->count() == dataCount && m2->count() == dataCount && m3->count() == dataCount); @@ -880,32 +866,32 @@ void tst_qdeclarativexmllistmodel::threading_data() void tst_qdeclarativexmllistmodel::propertyChanges() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("propertychanges.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); QTRY_COMPARE(model->count(), 9); - QDeclarativeXmlListModelRole *role = model->findChild("role"); + QObject *role = model->findChild("role"); QVERIFY(role); QSignalSpy nameSpy(role, SIGNAL(nameChanged())); QSignalSpy querySpy(role, SIGNAL(queryChanged())); QSignalSpy isKeySpy(role, SIGNAL(isKeyChanged())); - role->setName("size"); - role->setQuery("size/string()"); - role->setIsKey(true); + role->setProperty("name","size"); + role->setProperty("query","size/string()"); + role->setProperty("isKey",true); - QCOMPARE(role->name(), QString("size")); - QCOMPARE(role->query(), QString("size/string()")); - QVERIFY(role->isKey()); + QCOMPARE(role->property("name").toString(), QString("size")); + QCOMPARE(role->property("query").toString(), QString("size/string()")); + QVERIFY(role->property("isKey").toBool()); QCOMPARE(nameSpy.count(),1); QCOMPARE(querySpy.count(),1); QCOMPARE(isKeySpy.count(),1); - role->setName("size"); - role->setQuery("size/string()"); - role->setIsKey(true); + role->setProperty("name","size"); + role->setProperty("query","size/string()"); + role->setProperty("isKey",true); QCOMPARE(nameSpy.count(),1); QCOMPARE(querySpy.count(),1); @@ -916,15 +902,15 @@ void tst_qdeclarativexmllistmodel::propertyChanges() QSignalSpy modelQuerySpy(model, SIGNAL(queryChanged())); QSignalSpy namespaceDeclarationsSpy(model, SIGNAL(namespaceDeclarationsChanged())); - model->setSource(QUrl("")); - model->setXml("PollyParrot12Small"); - model->setQuery("/Pets"); - model->setNamespaceDeclarations("declare namespace media=\"http://search.yahoo.com/mrss/\";"); + model->setProperty("source",QUrl("")); + model->setProperty("xml","PollyParrot12Small"); + model->setProperty("query","/Pets"); + model->setProperty("namespaceDeclarations","declare namespace media=\"http://search.yahoo.com/mrss/\";"); - QCOMPARE(model->source(), QUrl("")); - QCOMPARE(model->xml(), QString("PollyParrot12Small")); - QCOMPARE(model->query(), QString("/Pets")); - QCOMPARE(model->namespaceDeclarations(), QString("declare namespace media=\"http://search.yahoo.com/mrss/\";")); + QCOMPARE(model->property("source").toUrl(), QUrl("")); + QCOMPARE(model->property("xml").toString(), QString("PollyParrot12Small")); + QCOMPARE(model->property("query").toString(), QString("/Pets")); + QCOMPARE(model->property("namespaceDeclarations").toString(), QString("declare namespace media=\"http://search.yahoo.com/mrss/\";")); QTRY_VERIFY(model->count() == 1); @@ -933,10 +919,10 @@ void tst_qdeclarativexmllistmodel::propertyChanges() QCOMPARE(modelQuerySpy.count(),1); QCOMPARE(namespaceDeclarationsSpy.count(),1); - model->setSource(QUrl("")); - model->setXml("PollyParrot12Small"); - model->setQuery("/Pets"); - model->setNamespaceDeclarations("declare namespace media=\"http://search.yahoo.com/mrss/\";"); + model->setProperty("source",QUrl("")); + model->setProperty("xml","PollyParrot12Small"); + model->setProperty("query","/Pets"); + model->setProperty("namespaceDeclarations","declare namespace media=\"http://search.yahoo.com/mrss/\";"); QCOMPARE(sourceSpy.count(),1); QCOMPARE(xmlSpy.count(),1); @@ -951,11 +937,11 @@ void tst_qdeclarativexmllistmodel::roleCrash() { // don't crash QDeclarativeComponent component(&engine, QUrl::fromLocalFile(TESTDATA("roleCrash.qml"))); - QDeclarativeXmlListModel *model = qobject_cast(component.create()); + QListModelInterface *model = qobject_cast(component.create()); QVERIFY(model != 0); delete model; } QTEST_MAIN(tst_qdeclarativexmllistmodel) -#include "tst_qdeclarativexmllistmodel.moc" +#include "tst_qdeclarativexmllistmodel.moc" \ No newline at end of file -- 1.7.2.5