1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativexmllistmodel_p.h"
44 #include <QtDeclarative/qdeclarativecontext.h>
45 #include <QtDeclarative/private/qdeclarativeengine_p.h>
48 #include <QStringList>
50 #include <QApplication>
53 #include <QXmlResultItems>
54 #include <QXmlNodeModelIndex>
56 #include <QNetworkRequest>
57 #include <QNetworkReply>
61 #include <private/qobject_p.h>
63 Q_DECLARE_METATYPE(QDeclarative1XmlQueryResult)
70 typedef QPair<int, int> QDeclarative1XmlListRange;
72 #define XMLLISTMODEL_CLEAR_ID 0
75 \qmlclass XmlRole QDeclarative1XmlListModelRole
76 \inqmlmodule QtQuick 1
77 \ingroup qml-working-with-data
79 \brief The XmlRole element allows you to specify a role for an XmlListModel.
85 \qmlproperty string QtQuick1::XmlRole::name
87 The name for the role. This name is used to access the model data for this role.
89 For example, the following model has a role named "title", which can be accessed
90 from the view's delegate:
98 query: "title/string()"
106 delegate: Text { text: title }
112 \qmlproperty string QtQuick1::XmlRole::query
113 The relative XPath expression query for this role. The query must be relative; it cannot start
116 For example, if there is an XML document like this:
118 \quotefile doc/src/snippets/qtquick1/xmlrole.xml
120 Here are some valid XPath expressions for XmlRole queries on this document:
122 \snippet doc/src/snippets/qtquick1/xmlrole.qml 0
124 \snippet doc/src/snippets/qtquick1/xmlrole.qml 1
126 See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information.
130 \qmlproperty bool QtQuick1::XmlRole::isKey
131 Defines whether this is a key role.
133 Key roles are used to to determine whether a set of values should
134 be updated or added to the XML list model when XmlListModel::reload()
146 QStringList roleQueries;
147 QList<void*> roleQueryErrorId; // the ptr to send back if there is an error
148 QStringList keyRoleQueries;
149 QStringList keyRoleResultsCache;
153 class QDeclarative1XmlQuery : public QObject
157 QDeclarative1XmlQuery(QObject *parent=0)
158 : QObject(parent), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1) {
159 qRegisterMetaType<QDeclarative1XmlQueryResult>("QDeclarative1XmlQueryResult");
160 moveToThread(&m_thread);
161 m_thread.start(QThread::IdlePriority);
164 ~QDeclarative1XmlQuery() {
165 if(m_thread.isRunning()) {
172 QMutexLocker ml(&m_mutex);
178 int doQuery(QString query, QString namespaces, QByteArray data, QList<QDeclarative1XmlListModelRole *>* roleObjects, QStringList keyRoleResultsCache) {
180 QMutexLocker m1(&m_mutex);
182 if (m_queryIds.load() <= 0)
187 job.queryId = m_queryIds.load();
189 job.query = QLatin1String("doc($src)") + query;
190 job.namespaces = namespaces;
191 job.keyRoleResultsCache = keyRoleResultsCache;
193 for (int i=0; i<roleObjects->count(); i++) {
194 if (!roleObjects->at(i)->isValid()) {
195 job.roleQueries << QString();
198 job.roleQueries << roleObjects->at(i)->query();
199 job.roleQueryErrorId << static_cast<void*>(roleObjects->at(i));
200 if (roleObjects->at(i)->isKey())
201 job.keyRoleQueries << job.roleQueries.last();
205 QMutexLocker ml(&m_mutex);
206 m_jobs.insert(m_queryIds.load(), job);
209 QMetaObject::invokeMethod(this, "processQuery", Qt::QueuedConnection, Q_ARG(int, job.queryId));
214 void processQuery(int queryId) {
218 QMutexLocker ml(&m_mutex);
219 if (!m_jobs.contains(queryId))
221 job = m_jobs.value(queryId);
224 QDeclarative1XmlQueryResult result;
225 result.queryId = job.queryId;
226 doQueryJob(&job, &result);
227 doSubQueryJob(&job, &result);
230 QMutexLocker ml(&m_mutex);
231 if (m_jobs.contains(queryId)) {
232 emit queryCompleted(result);
233 m_jobs.remove(queryId);
239 void queryCompleted(const QDeclarative1XmlQueryResult &);
240 void error(void*, const QString&);
246 void doQueryJob(XmlQueryJob *job, QDeclarative1XmlQueryResult *currentResult);
247 void doSubQueryJob(XmlQueryJob *job, QDeclarative1XmlQueryResult *currentResult);
248 void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const;
249 void addIndexToRangeList(QList<QDeclarative1XmlListRange> *ranges, int index) const;
254 QMap<int, XmlQueryJob> m_jobs;
255 QAtomicInt m_queryIds;
258 Q_GLOBAL_STATIC(QDeclarative1XmlQuery, globalXmlQuery)
260 void QDeclarative1XmlQuery::doQueryJob(XmlQueryJob *currentJob, QDeclarative1XmlQueryResult *currentResult)
262 Q_ASSERT(currentJob->queryId != -1);
266 QBuffer buffer(¤tJob->data);
267 buffer.open(QIODevice::ReadOnly);
268 query.bindVariable(QLatin1String("src"), &buffer);
269 query.setQuery(currentJob->namespaces + currentJob->query);
270 query.evaluateTo(&r);
272 //always need a single root element
273 QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>";
275 b.open(QIODevice::ReadOnly);
277 QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces;
278 QString prefix = QLatin1String("doc($inputDocument)/dummy:items") +
279 currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/')));
281 //figure out how many items we are dealing with
284 QXmlResultItems result;
285 QXmlQuery countquery;
286 countquery.bindVariable(QLatin1String("inputDocument"), &b);
287 countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')'));
288 countquery.evaluateTo(&result);
289 QXmlItem item(result.next());
290 if (item.isAtomicValue())
291 count = item.toAtomicValue().toInt();
294 currentJob->data = xml;
295 currentJob->prefix = namespaces + prefix + QLatin1Char('/');
296 currentResult->size = (count > 0 ? count : 0);
299 void QDeclarative1XmlQuery::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const
301 const QStringList &keysQueries = currentJob.keyRoleQueries;
303 if (keysQueries.count() == 1)
304 keysQuery = currentJob.prefix + keysQueries[0];
305 else if (keysQueries.count() > 1)
306 keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")");
308 if (!keysQuery.isEmpty()) {
309 query->setQuery(keysQuery);
310 QXmlResultItems resultItems;
311 query->evaluateTo(&resultItems);
312 QXmlItem item(resultItems.next());
313 while (!item.isNull()) {
314 values->append(item.toAtomicValue().toString());
315 item = resultItems.next();
320 void QDeclarative1XmlQuery::addIndexToRangeList(QList<QDeclarative1XmlListRange> *ranges, int index) const {
321 if (ranges->isEmpty())
322 ranges->append(qMakePair(index, 1));
323 else if (ranges->last().first + ranges->last().second == index)
324 ranges->last().second += 1;
326 ranges->append(qMakePair(index, 1));
329 void QDeclarative1XmlQuery::doSubQueryJob(XmlQueryJob *currentJob, QDeclarative1XmlQueryResult *currentResult)
331 Q_ASSERT(currentJob->queryId != -1);
333 QBuffer b(¤tJob->data);
334 b.open(QIODevice::ReadOnly);
337 subquery.bindVariable(QLatin1String("inputDocument"), &b);
339 QStringList keyRoleResults;
340 getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery);
342 // See if any values of key roles have been inserted or removed.
344 if (currentJob->keyRoleResultsCache.isEmpty()) {
345 currentResult->inserted << qMakePair(0, currentResult->size);
347 if (keyRoleResults != currentJob->keyRoleResultsCache) {
349 for (int i=0; i<currentJob->keyRoleResultsCache.count(); i++) {
350 if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i]))
351 addIndexToRangeList(¤tResult->removed, i);
353 temp << currentJob->keyRoleResultsCache[i];
356 for (int i=0; i<keyRoleResults.count(); i++) {
357 if (temp.count() == i || keyRoleResults[i] != temp[i]) {
358 temp.insert(i, keyRoleResults[i]);
359 addIndexToRangeList(¤tResult->inserted, i);
364 currentResult->keyRoleResultsCache = keyRoleResults;
366 // Get the new values for each role.
367 //### we might be able to condense even further (query for everything in one go)
368 const QStringList &queries = currentJob->roleQueries;
369 for (int i = 0; i < queries.size(); ++i) {
370 QList<QVariant> resultList;
371 if (!queries[i].isEmpty()) {
372 subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")"));
373 if (subquery.isValid()) {
374 QXmlResultItems resultItems;
375 subquery.evaluateTo(&resultItems);
376 QXmlItem item(resultItems.next());
377 while (!item.isNull()) {
378 resultList << item.toAtomicValue(); //### we used to trim strings
379 item = resultItems.next();
382 emit error(currentJob->roleQueryErrorId.at(i), queries[i]);
385 //### should warn here if things have gone wrong.
386 while (resultList.count() < currentResult->size)
387 resultList << QVariant();
388 currentResult->data << resultList;
392 //this method is much slower, but works better for incremental loading
393 /*for (int j = 0; j < m_size; ++j) {
394 QList<QVariant> resultList;
395 for (int i = 0; i < m_roleObjects->size(); ++i) {
396 QDeclarative1XmlListModelRole *role = m_roleObjects->at(i);
397 subquery.setQuery(m_prefix.arg(j+1) + role->query());
398 if (role->isStringList()) {
400 subquery.evaluateTo(&data);
401 resultList << QVariant(data);
405 subquery.evaluateTo(&s);
406 if (role->isCData()) {
408 s.replace(QLatin1String("<"), QLatin1String("<"));
409 s.replace(QLatin1String(">"), QLatin1String(">"));
410 s.replace(QLatin1String("&"), QLatin1String("&"));
412 resultList << s.trimmed();
417 m_modelData << resultList;
421 class QDeclarative1XmlListModelPrivate : public QObjectPrivate
423 Q_DECLARE_PUBLIC(QDeclarative1XmlListModel)
425 QDeclarative1XmlListModelPrivate()
426 : isComponentComplete(true), size(-1), highestRole(Qt::UserRole)
427 , reply(0), status(QDeclarative1XmlListModel::Null), progress(0.0)
428 , queryId(-1), roleObjects(), redirectCount(0) {}
431 void notifyQueryStarted(bool remoteSource) {
432 Q_Q(QDeclarative1XmlListModel);
433 progress = remoteSource ? 0.0 : 1.0;
434 status = QDeclarative1XmlListModel::Loading;
436 emit q->progressChanged(progress);
437 emit q->statusChanged(status);
440 bool isComponentComplete;
447 QStringList roleNames;
449 QNetworkReply *reply;
450 QDeclarative1XmlListModel::Status status;
454 QStringList keyRoleResultsCache;
455 QList<QDeclarative1XmlListModelRole *> roleObjects;
456 static void append_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list, QDeclarative1XmlListModelRole *role);
457 static void clear_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list);
458 QList<QList<QVariant> > data;
463 void QDeclarative1XmlListModelPrivate::append_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list, QDeclarative1XmlListModelRole *role)
465 QDeclarative1XmlListModel *_this = qobject_cast<QDeclarative1XmlListModel *>(list->object);
467 int i = _this->d_func()->roleObjects.count();
468 _this->d_func()->roleObjects.append(role);
469 if (_this->d_func()->roleNames.contains(role->name())) {
470 qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name());
473 _this->d_func()->roles.insert(i, _this->d_func()->highestRole);
474 _this->d_func()->roleNames.insert(i, role->name());
475 ++_this->d_func()->highestRole;
479 //### clear needs to invalidate any cached data (in data table) as well
480 // (and the model should emit the appropriate signals)
481 void QDeclarative1XmlListModelPrivate::clear_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list)
483 QDeclarative1XmlListModel *_this = static_cast<QDeclarative1XmlListModel *>(list->object);
484 _this->d_func()->roles.clear();
485 _this->d_func()->roleNames.clear();
486 _this->d_func()->roleObjects.clear();
490 \qmlclass XmlListModel QDeclarative1XmlListModel
491 \inqmlmodule QtQuick 1
492 \ingroup qml-working-with-data
494 \brief The XmlListModel element is used to specify a read-only model using XPath expressions.
496 XmlListModel is used to create a read-only model from XML data. It can be used as a data source
497 for view elements (such as ListView, PathView, GridView) and other elements that interact with model
498 data (such as \l Repeater).
500 For example, if there is a XML document at http://www.mysite.com/feed.xml like this:
503 <?xml version="1.0" encoding="utf-8"?>
508 <title>A blog post</title>
509 <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
512 <title>Another blog post</title>
513 <pubDate>Sat, 07 Sep 2010 15:35:01 GMT</pubDate>
519 A XmlListModel could create a model from this data, like this:
526 source: "http://www.mysite.com/feed.xml"
527 query: "/rss/channel/item"
529 XmlRole { name: "title"; query: "title/string()" }
530 XmlRole { name: "pubDate"; query: "pubDate/string()" }
534 The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate
535 a model item for each \c <item> in the XML document.
537 The XmlRole objects define the
538 model item attributes. Here, each model item will have \c title and \c pubDate
539 attributes that match the \c title and \c pubDate values of its corresponding \c <item>.
540 (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.)
542 The model could be used in a ListView, like this:
546 width: 180; height: 300
548 delegate: Text { text: title + ": " + pubDate }
552 \image qml-xmllistmodel-example.png
554 The XmlListModel data is loaded asynchronously, and \l status
555 is set to \c XmlListModel.Ready when loading is complete.
556 Note this means when XmlListModel is used for a view, the view is not
557 populated until the model is loaded.
560 \section2 Using key XML roles
562 You can define certain roles as "keys" so that when reload() is called,
563 the model will only add and refresh data that contains new values for
566 For example, if above role for "pubDate" was defined like this instead:
569 XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true }
572 Then when reload() is called, the model will only add and reload
573 items with a "pubDate" value that is not already
574 present in the model.
576 This is useful when displaying the contents of XML documents that
577 are incrementally updated (such as RSS feeds) to avoid repainting the
578 entire contents of a model in a view.
580 If multiple key roles are specified, the model only adds and reload items
581 with a combined value of all key roles that is not already present in
587 QDeclarative1XmlListModel::QDeclarative1XmlListModel(QObject *parent)
588 : QListModelInterface(*(new QDeclarative1XmlListModelPrivate), parent)
590 connect(globalXmlQuery(), SIGNAL(queryCompleted(QDeclarative1XmlQueryResult)),
591 this, SLOT(queryCompleted(QDeclarative1XmlQueryResult)));
592 connect(globalXmlQuery(), SIGNAL(error(void*,QString)),
593 this, SLOT(queryError(void*,QString)));
596 QDeclarative1XmlListModel::~QDeclarative1XmlListModel()
601 \qmlproperty list<XmlRole> QtQuick1::XmlListModel::roles
603 The roles to make available for this model.
605 QDeclarativeListProperty<QDeclarative1XmlListModelRole> QDeclarative1XmlListModel::roleObjects()
607 Q_D(QDeclarative1XmlListModel);
608 QDeclarativeListProperty<QDeclarative1XmlListModelRole> list(this, d->roleObjects);
609 list.append = &QDeclarative1XmlListModelPrivate::append_role;
610 list.clear = &QDeclarative1XmlListModelPrivate::clear_role;
614 QHash<int,QVariant> QDeclarative1XmlListModel::data(int index, const QList<int> &roles) const
616 Q_D(const QDeclarative1XmlListModel);
617 QHash<int, QVariant> rv;
618 for (int i = 0; i < roles.size(); ++i) {
619 int role = roles.at(i);
620 int roleIndex = d->roles.indexOf(role);
621 rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index));
626 QVariant QDeclarative1XmlListModel::data(int index, int role) const
628 Q_D(const QDeclarative1XmlListModel);
629 int roleIndex = d->roles.indexOf(role);
630 return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index);
634 \qmlproperty int QtQuick1::XmlListModel::count
635 The number of data entries in the model.
637 int QDeclarative1XmlListModel::count() const
639 Q_D(const QDeclarative1XmlListModel);
643 QList<int> QDeclarative1XmlListModel::roles() const
645 Q_D(const QDeclarative1XmlListModel);
649 QString QDeclarative1XmlListModel::toString(int role) const
651 Q_D(const QDeclarative1XmlListModel);
652 int index = d->roles.indexOf(role);
655 return d->roleNames.at(index);
659 \qmlproperty url QtQuick1::XmlListModel::source
660 The location of the XML data source.
662 If both \c source and \l xml are set, \l xml is used.
664 QUrl QDeclarative1XmlListModel::source() const
666 Q_D(const QDeclarative1XmlListModel);
670 void QDeclarative1XmlListModel::setSource(const QUrl &src)
672 Q_D(QDeclarative1XmlListModel);
675 if (d->xml.isEmpty()) // src is only used if d->xml is not set
677 emit sourceChanged();
682 \qmlproperty string QtQuick1::XmlListModel::xml
683 This property holds the XML data for this model, if set.
685 The text is assumed to be UTF-8 encoded.
687 If both \l source and \c xml are set, \c xml is used.
689 QString QDeclarative1XmlListModel::xml() const
691 Q_D(const QDeclarative1XmlListModel);
695 void QDeclarative1XmlListModel::setXml(const QString &xml)
697 Q_D(QDeclarative1XmlListModel);
706 \qmlproperty string QtQuick1::XmlListModel::query
707 An absolute XPath query representing the base query for creating model items
708 from this model's XmlRole objects. The query should start with '/' or '//'.
710 QString QDeclarative1XmlListModel::query() const
712 Q_D(const QDeclarative1XmlListModel);
716 void QDeclarative1XmlListModel::setQuery(const QString &query)
718 Q_D(QDeclarative1XmlListModel);
719 if (!query.startsWith(QLatin1Char('/'))) {
720 qmlInfo(this) << QCoreApplication::translate("QDeclarative1XmlRoleList", "An XmlListModel query must start with '/' or \"//\"");
724 if (d->query != query) {
732 \qmlproperty string QtQuick1::XmlListModel::namespaceDeclarations
733 The namespace declarations to be used in the XPath queries.
735 The namespaces should be declared as in XQuery. For example, if a requested document
736 at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom",
737 this can be declared as the default namespace:
741 source: "http://mysite.com/feed.xml"
743 namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';"
745 XmlRole { name: "title"; query: "title/string()" }
749 QString QDeclarative1XmlListModel::namespaceDeclarations() const
751 Q_D(const QDeclarative1XmlListModel);
752 return d->namespaces;
755 void QDeclarative1XmlListModel::setNamespaceDeclarations(const QString &declarations)
757 Q_D(QDeclarative1XmlListModel);
758 if (d->namespaces != declarations) {
759 d->namespaces = declarations;
761 emit namespaceDeclarationsChanged();
766 \qmlmethod object QtQuick1::XmlListModel::get(int index)
768 Returns the item at \a index in the model.
770 For example, for a model like this:
775 source: "http://mysite.com/feed.xml"
777 XmlRole { name: "title"; query: "title/string()" }
781 This will access the \c title value for the first item in the model:
784 var title = model.get(0).title;
787 QDeclarativeV8Handle QDeclarative1XmlListModel::get(int index) const
789 // Must be called with a context and handle scope
790 Q_D(const QDeclarative1XmlListModel);
792 if (index < 0 || index >= count())
793 return QDeclarativeV8Handle::fromHandle(v8::Undefined());
795 QDeclarativeEngine *engine = qmlContext(this)->engine();
796 QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(engine);
797 v8::Local<v8::Object> rv = v8::Object::New();
798 for (int ii = 0; ii < d->roleObjects.count(); ++ii)
799 rv->Set(v8engine->toString(d->roleObjects[ii]->name()),
800 v8engine->fromVariant(d->data.value(ii).value(index)));
802 return QDeclarativeV8Handle::fromHandle(rv);
806 \qmlproperty enumeration QtQuick1::XmlListModel::status
807 Specifies the model loading status, which can be one of the following:
810 \o XmlListModel.Null - No XML data has been set for this model.
811 \o XmlListModel.Ready - The XML data has been loaded into the model.
812 \o XmlListModel.Loading - The model is in the process of reading and loading XML data.
813 \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details
820 QDeclarative1XmlListModel::Status QDeclarative1XmlListModel::status() const
822 Q_D(const QDeclarative1XmlListModel);
827 \qmlproperty real QtQuick1::XmlListModel::progress
829 This indicates the current progress of the downloading of the XML data
830 source. This value ranges from 0.0 (no data downloaded) to
831 1.0 (all data downloaded). If the XML data is not from a remote source,
832 the progress becomes 1.0 as soon as the data is read.
834 Note that when the progress is 1.0, the XML data has been downloaded, but
835 it is yet to be loaded into the model at this point. Use the status
836 property to find out when the XML data has been read and loaded into
841 qreal QDeclarative1XmlListModel::progress() const
843 Q_D(const QDeclarative1XmlListModel);
848 \qmlmethod void QtQuick1::XmlListModel::errorString()
850 Returns a string description of the last error that occurred
851 if \l status is XmlListModel::Error.
853 QString QDeclarative1XmlListModel::errorString() const
855 Q_D(const QDeclarative1XmlListModel);
856 return d->errorString;
859 void QDeclarative1XmlListModel::classBegin()
861 Q_D(QDeclarative1XmlListModel);
862 d->isComponentComplete = false;
865 void QDeclarative1XmlListModel::componentComplete()
867 Q_D(QDeclarative1XmlListModel);
868 d->isComponentComplete = true;
873 \qmlmethod QtQuick1::XmlListModel::reload()
877 If no key roles have been specified, all existing model
878 data is removed, and the model is rebuilt from scratch.
880 Otherwise, items are only added if the model does not already
881 contain items with matching key role values.
883 \sa {Using key XML roles}, XmlRole::isKey
885 void QDeclarative1XmlListModel::reload()
887 Q_D(QDeclarative1XmlListModel);
889 if (!d->isComponentComplete)
892 globalXmlQuery()->abort(d->queryId);
901 // abort will generally have already done this (and more)
902 d->reply->deleteLater();
907 if (!d->xml.isEmpty()) {
908 d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache);
909 d->notifyQueryStarted(false);
911 } else if (d->src.isEmpty()) {
912 d->queryId = XMLLISTMODEL_CLEAR_ID;
913 d->notifyQueryStarted(false);
914 QTimer::singleShot(0, this, SLOT(dataCleared()));
917 d->notifyQueryStarted(true);
918 QNetworkRequest req(d->src);
919 req.setRawHeader("Accept", "application/xml,*/*");
920 d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req);
921 QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished()));
922 QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
923 this, SLOT(requestProgress(qint64,qint64)));
927 #define XMLLISTMODEL_MAX_REDIRECT 16
929 void QDeclarative1XmlListModel::requestFinished()
931 Q_D(QDeclarative1XmlListModel);
934 if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) {
935 QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
936 if (redirect.isValid()) {
937 QUrl url = d->reply->url().resolved(redirect.toUrl());
938 d->reply->deleteLater();
944 d->redirectCount = 0;
946 if (d->reply->error() != QNetworkReply::NoError) {
947 d->errorString = d->reply->errorString();
948 disconnect(d->reply, 0, this, 0);
949 d->reply->deleteLater();
952 int count = this->count();
956 emit itemsRemoved(0, count);
962 emit statusChanged(d->status);
964 QByteArray data = d->reply->readAll();
965 if (data.isEmpty()) {
966 d->queryId = XMLLISTMODEL_CLEAR_ID;
967 QTimer::singleShot(0, this, SLOT(dataCleared()));
969 d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
971 disconnect(d->reply, 0, this, 0);
972 d->reply->deleteLater();
976 emit progressChanged(d->progress);
980 void QDeclarative1XmlListModel::requestProgress(qint64 received, qint64 total)
982 Q_D(QDeclarative1XmlListModel);
983 if (d->status == Loading && total > 0) {
984 d->progress = qreal(received)/total;
985 emit progressChanged(d->progress);
989 void QDeclarative1XmlListModel::dataCleared()
991 Q_D(QDeclarative1XmlListModel);
992 QDeclarative1XmlQueryResult r;
993 r.queryId = XMLLISTMODEL_CLEAR_ID;
995 r.removed << qMakePair(0, count());
996 r.keyRoleResultsCache = d->keyRoleResultsCache;
1000 void QDeclarative1XmlListModel::queryError(void* object, const QString& error)
1002 // Be extra careful, object may no longer exist, it's just an ID.
1003 Q_D(QDeclarative1XmlListModel);
1004 for (int i=0; i<d->roleObjects.count(); i++) {
1005 if (d->roleObjects.at(i) == static_cast<QDeclarative1XmlListModelRole*>(object)) {
1006 qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error);
1010 qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error);
1013 void QDeclarative1XmlListModel::queryCompleted(const QDeclarative1XmlQueryResult &result)
1015 Q_D(QDeclarative1XmlListModel);
1016 if (result.queryId != d->queryId)
1019 int origCount = d->size;
1020 bool sizeChanged = result.size != d->size;
1022 d->size = result.size;
1023 d->data = result.data;
1024 d->keyRoleResultsCache = result.keyRoleResultsCache;
1026 d->errorString.clear();
1029 bool hasKeys = false;
1030 for (int i=0; i<d->roleObjects.count(); i++) {
1031 if (d->roleObjects[i]->isKey()) {
1037 if (!(origCount == 0 && d->size == 0)) {
1038 emit itemsRemoved(0, origCount);
1039 emit itemsInserted(0, d->size);
1040 emit countChanged();
1044 for (int i=0; i<result.removed.count(); i++)
1045 emit itemsRemoved(result.removed[i].first, result.removed[i].second);
1046 for (int i=0; i<result.inserted.count(); i++)
1047 emit itemsInserted(result.inserted[i].first, result.inserted[i].second);
1050 emit countChanged();
1053 emit statusChanged(d->status);
1060 #include <qdeclarativexmllistmodel.moc>