summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/declarative/util/qdeclarativexmllistmodel.cpp147
-rw-r--r--src/declarative/util/qdeclarativexmllistmodel_p.h3
-rw-r--r--tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp97
3 files changed, 183 insertions, 64 deletions
diff --git a/src/declarative/util/qdeclarativexmllistmodel.cpp b/src/declarative/util/qdeclarativexmllistmodel.cpp
index 3e08854..efc614b 100644
--- a/src/declarative/util/qdeclarativexmllistmodel.cpp
+++ b/src/declarative/util/qdeclarativexmllistmodel.cpp
@@ -57,6 +57,7 @@
#include <QBuffer>
#include <QNetworkRequest>
#include <QNetworkReply>
+#include <QTimer>
#include <private/qobject_p.h>
@@ -67,6 +68,8 @@ QT_BEGIN_NAMESPACE
typedef QPair<int, int> QDeclarativeXmlListRange;
+#define XMLLISTMODEL_CLEAR_ID 0
+
/*!
\qmlclass XmlRole QDeclarativeXmlListModelRole
\since 4.7
@@ -127,9 +130,10 @@ class QDeclarativeXmlQuery : public QThread
Q_OBJECT
public:
QDeclarativeXmlQuery(QObject *parent=0)
- : QThread(parent), m_quit(false), m_abortQueryId(-1), m_queryIds(0) {
+ : QThread(parent), m_quit(false), m_abortQueryId(-1), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1) {
qRegisterMetaType<QDeclarativeXmlQueryResult>("QDeclarativeXmlQueryResult");
}
+
~QDeclarativeXmlQuery() {
m_mutex.lock();
m_quit = true;
@@ -586,7 +590,8 @@ void QDeclarativeXmlListModel::setSource(const QUrl &src)
Q_D(QDeclarativeXmlListModel);
if (d->src != src) {
d->src = src;
- reload();
+ if (d->xml.isEmpty()) // src is only used if d->xml is not set
+ reload();
emit sourceChanged();
}
}
@@ -663,23 +668,13 @@ void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarati
/*!
\qmlproperty enum XmlListModel::status
+ Specifies the model loading status, which can be one of the following:
- This property holds the status of data source loading. It can be one of:
\list
- \o Null - no data source has been set
- \o Ready - the data source has been loaded
- \o Loading - the data source is currently being loaded
- \o Error - an error occurred while loading the data source
- \endlist
-
- Note that a change in the status property does not cause anything to happen
- (although it reflects what has happened to the XmlListModel internally). If you wish
- to react to the change in status you need to do it yourself, for example in one
- of the following ways:
- \list
- \o Create a state, so that a state change occurs, e.g. State{name: 'loaded'; when: xmlListModel.status = XmlListModel.Ready;}
- \o Do something inside the onStatusChanged signal handler, e.g. XmlListModel{id: xmlListModel; onStatusChanged: if(xmlListModel.status == XmlListModel.Ready) console.log('Loaded');}
- \o Bind to the status variable somewhere, e.g. Text{text: if(xmlListModel.status!=XmlListModel.Ready){'Not Loaded';}else{'Loaded';}}
+ \o Null - No XML data has been set for this model.
+ \o Ready - The XML data has been loaded into the model.
+ \o Loading - The model is in the process of reading and loading XML data.
+ \o Error - An error occurred while the model was loading.
\endlist
\sa progress
@@ -694,10 +689,17 @@ QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const
/*!
\qmlproperty real XmlListModel::progress
- This property holds the progress of data source loading, from 0.0 (nothing loaded)
- to 1.0 (finished).
+ 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
+ \sa status, source
*/
qreal QDeclarativeXmlListModel::progress() const
{
@@ -741,27 +743,8 @@ void QDeclarativeXmlListModel::reload()
globalXmlQuery()->abort(d->queryId);
d->queryId = -1;
- int count = d->size;
- if (count < 0)
- d->size = 0;
- bool hasKeys = false;
- for (int i=0; i<d->roleObjects.count(); i++) {
- if (d->roleObjects[i]->isKey()) {
- hasKeys = true;
- break;
- }
- }
- if (!hasKeys) {
- d->data.clear();
+ if (d->size < 0)
d->size = 0;
- if (count > 0) {
- emit itemsRemoved(0, count);
- emit countChanged();
- }
- }
-
- if (d->src.isEmpty() && d->xml.isEmpty())
- return;
if (d->reply) {
d->reply->abort();
@@ -772,12 +755,22 @@ void QDeclarativeXmlListModel::reload()
if (!d->xml.isEmpty()) {
d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache);
d->progress = 1.0;
- d->status = Ready;
+ d->status = Loading;
emit progressChanged(d->progress);
emit statusChanged(d->status);
return;
}
+ if (d->src.isEmpty()) {
+ d->queryId = XMLLISTMODEL_CLEAR_ID;
+ d->progress = 1.0;
+ d->status = Loading;
+ emit progressChanged(d->progress);
+ emit statusChanged(d->status);
+ QTimer::singleShot(0, this, SLOT(dataCleared()));
+ return;
+ }
+
d->progress = 0.0;
d->status = Loading;
emit progressChanged(d->progress);
@@ -813,18 +806,33 @@ void QDeclarativeXmlListModel::requestFinished()
disconnect(d->reply, 0, this, 0);
d->reply->deleteLater();
d->reply = 0;
+
+ 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 {
- d->status = Ready;
QByteArray data = d->reply->readAll();
- d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
+ if (data.isEmpty()) {
+ d->queryId = XMLLISTMODEL_CLEAR_ID;
+ QTimer::singleShot(0, this, SLOT(dataCleared()));
+ } else {
+ d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
+ }
disconnect(d->reply, 0, this, 0);
d->reply->deleteLater();
d->reply = 0;
+
+ d->progress = 1.0;
+ emit progressChanged(d->progress);
}
- d->progress = 1.0;
- emit progressChanged(d->progress);
- emit statusChanged(d->status);
}
void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total)
@@ -836,23 +844,58 @@ void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total)
}
}
+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::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;
+ d->status = Ready;
+ d->queryId = -1;
- for (int i=0; i<result.removed.count(); i++)
- emit itemsRemoved(result.removed[i].first, result.removed[i].second);
- for (int i=0; i<result.inserted.count(); i++)
- emit itemsInserted(result.inserted[i].first, result.inserted[i].second);
+ bool hasKeys = false;
+ for (int i=0; i<d->roleObjects.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; i<result.removed.count(); i++)
+ emit itemsRemoved(result.removed[i].first, result.removed[i].second);
+ for (int i=0; i<result.inserted.count(); i++)
+ emit itemsInserted(result.inserted[i].first, result.inserted[i].second);
- if (sizeChanged)
- emit countChanged();
+ if (sizeChanged)
+ emit countChanged();
+ }
+
+ emit statusChanged(d->status);
}
QT_END_NAMESPACE
diff --git a/src/declarative/util/qdeclarativexmllistmodel_p.h b/src/declarative/util/qdeclarativexmllistmodel_p.h
index 7bb0f0e..7b85476 100644
--- a/src/declarative/util/qdeclarativexmllistmodel_p.h
+++ b/src/declarative/util/qdeclarativexmllistmodel_p.h
@@ -117,7 +117,7 @@ public:
virtual void componentComplete();
Q_SIGNALS:
- void statusChanged(Status);
+ void statusChanged(QDeclarativeXmlListModel::Status);
void progressChanged(qreal progress);
void countChanged();
void sourceChanged();
@@ -135,6 +135,7 @@ public Q_SLOTS:
private Q_SLOTS:
void requestFinished();
void requestProgress(qint64,qint64);
+ void dataCleared();
void queryCompleted(const QDeclarativeXmlQueryResult &);
private:
diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp b/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp
index e3aa5cc..74da79e 100644
--- a/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp
+++ b/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp
@@ -41,6 +41,8 @@
#include <qtest.h>
#include <QtTest/qsignalspy.h>
#include <QtCore/qtimer.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qtemporaryfile.h>
#ifdef QTEST_XMLPATTERNS
#include <QtDeclarative/qdeclarativeengine.h>
@@ -53,6 +55,7 @@ typedef QList<QVariantList> QDeclarativeXmlModelData;
Q_DECLARE_METATYPE(QList<QDeclarativeXmlListRange>)
Q_DECLARE_METATYPE(QDeclarativeXmlModelData)
+Q_DECLARE_METATYPE(QDeclarativeXmlListModel::Status)
class tst_qdeclarativexmllistmodel : public QObject
@@ -62,6 +65,10 @@ public:
tst_qdeclarativexmllistmodel() {}
private slots:
+ void initTestCase() {
+ qRegisterMetaType<QDeclarativeXmlListModel::Status>("QDeclarativeXmlListModel::Status");
+ }
+
void buildModel();
void missingFields();
void cdata();
@@ -69,7 +76,10 @@ private slots:
void roles();
void roleErrors();
void uniqueRoleNames();
- void status();
+ void xml();
+ void xml_data();
+ void source();
+ void source_data();
void data();
void reload();
void useKeys();
@@ -183,7 +193,6 @@ void tst_qdeclarativexmllistmodel::attributes()
QDeclarativeXmlListModel *model = qobject_cast<QDeclarativeXmlListModel*>(component.create());
QVERIFY(model != 0);
QTRY_COMPARE(model->count(), 5);
-
QList<int> roles;
roles << Qt::UserRole;
QHash<int, QVariant> data = model->data(2, roles);
@@ -249,27 +258,93 @@ void tst_qdeclarativexmllistmodel::uniqueRoleNames()
delete model;
}
-void tst_qdeclarativexmllistmodel::status()
+
+void tst_qdeclarativexmllistmodel::xml()
{
- QDeclarativeXmlListModel *model;
- model = new QDeclarativeXmlListModel;
- QCOMPARE(model->status(), QDeclarativeXmlListModel::Null);
+ QFETCH(QString, xml);
+ QFETCH(int, count);
- model->setXml("<data></data>");
+ QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/model.qml"));
+ QDeclarativeXmlListModel *model = qobject_cast<QDeclarativeXmlListModel*>(component.create());
+ QSignalSpy spy(model, SIGNAL(statusChanged(QDeclarativeXmlListModel::Status)));
+
+ QCOMPARE(model->progress(), qreal(0.0));
+ QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading);
+ QTRY_COMPARE(spy.count(), 1); spy.clear();
QCOMPARE(model->status(), QDeclarativeXmlListModel::Ready);
+ QCOMPARE(model->progress(), 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()
+ QTRY_COMPARE(spy.count(), 1); spy.clear();
+ QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading);
+ QTRY_COMPARE(spy.count(), 1); spy.clear();
+ QCOMPARE(model->status(), QDeclarativeXmlListModel::Ready);
+ QCOMPARE(model->count(), count);
+
delete model;
+}
+
+void tst_qdeclarativexmllistmodel::xml_data()
+{
+ QTest::addColumn<QString>("xml");
+ QTest::addColumn<int>("count");
+
+ QTest::newRow("xml with no items") << "<Pets></Pets>" << 0;
+ QTest::newRow("empty xml") << "" << 0;
+ QTest::newRow("one item") << "<Pets><Pet><name>Hobbes</name><type>Tiger</type><age>7</age><size>Large</size></Pet></Pets>" << 1;
+}
+
+void tst_qdeclarativexmllistmodel::source()
+{
+ QFETCH(QUrl, source);
+ QFETCH(int, count);
+ QFETCH(QDeclarativeXmlListModel::Status, status);
QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/model.qml"));
- model = qobject_cast<QDeclarativeXmlListModel*>(component.create());
- QVERIFY(model != 0);
- QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading);
+ QDeclarativeXmlListModel *model = qobject_cast<QDeclarativeXmlListModel*>(component.create());
+ QSignalSpy spy(model, SIGNAL(statusChanged(QDeclarativeXmlListModel::Status)));
- QTRY_COMPARE(model->count(), 9);
+ QCOMPARE(model->progress(), qreal(0.0));
+ QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading);
+ QTRY_COMPARE(spy.count(), 1); spy.clear();
QCOMPARE(model->status(), QDeclarativeXmlListModel::Ready);
+ QCOMPARE(model->progress(), qreal(1.0));
+ QCOMPARE(model->count(), 9);
+
+ model->setSource(source);
+ QCOMPARE(model->progress(), qreal(0.0));
+ QTRY_COMPARE(spy.count(), 1); spy.clear();
+ QCOMPARE(model->status(), QDeclarativeXmlListModel::Loading);
+ QTRY_COMPARE(spy.count(), 1); spy.clear();
+ QCOMPARE(model->status(), status);
+ QCOMPARE(model->count(), count);
+ if (status == QDeclarativeXmlListModel::Ready)
+ QCOMPARE(model->progress(), qreal(1.0));
delete model;
}
+void tst_qdeclarativexmllistmodel::source_data()
+{
+ QTest::addColumn<QUrl>("source");
+ QTest::addColumn<int>("count");
+ QTest::addColumn<QDeclarativeXmlListModel::Status>("status");
+
+ QTest::newRow("valid") << QUrl::fromLocalFile(SRCDIR "/data/model2.xml") << 2 << QDeclarativeXmlListModel::Ready;
+ QTest::newRow("invalid") << QUrl("http://blah.blah/blah.xml") << 0 << QDeclarativeXmlListModel::Error;
+
+ // empty file
+ QTemporaryFile *temp = new QTemporaryFile(this);
+ if (temp->open())
+ QTest::newRow("empty file") << QUrl::fromLocalFile(temp->fileName()) << 0 << QDeclarativeXmlListModel::Ready;
+ temp->close();
+}
+
void tst_qdeclarativexmllistmodel::data()
{
QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/model.qml"));