1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qqmlxmllistmodel_p.h"
44 #include <qqmlcontext.h>
45 #include <private/qqmlengine_p.h>
48 #include <QStringList>
52 #include <QXmlResultItems>
53 #include <QXmlNodeModelIndex>
55 #include <QNetworkRequest>
56 #include <QNetworkReply>
60 #include <private/qabstractitemmodel_p.h>
62 Q_DECLARE_METATYPE(QQuickXmlQueryResult)
67 typedef QPair<int, int> QQuickXmlListRange;
69 #define XMLLISTMODEL_CLEAR_ID 0
72 \qmlmodule QtQuick.XmlListModel 2
73 \title QML Module QtQuick.XmlListModel 2.0
74 \brief Contains types for creating models from XML data
76 This QML module contains types for creating models from XML data.
78 To use the types in this module, import the module with the following line:
81 import QtQuick.XmlListModel 2.0
86 \qmlclass XmlRole QQuickXmlListModelRole
87 \inqmlmodule QtQuick.XmlListModel 2
88 \brief For specifying a role to an XmlListModel
89 \ingroup qtquick-models
95 \qmlproperty string QtQuick.XmlListModel2::XmlRole::name
97 The name for the role. This name is used to access the model data for this role.
99 For example, the following model has a role named "title", which can be accessed
100 from the view's delegate:
108 query: "title/string()"
116 delegate: Text { text: title }
122 \qmlproperty string QtQuick.XmlListModel2::XmlRole::query
123 The relative XPath expression query for this role. The query must be relative; it cannot start
126 For example, if there is an XML document like this:
128 \quotefile qml/xmlrole.xml
129 Here are some valid XPath expressions for XmlRole queries on this document:
131 \snippet qml/xmlrole.qml 0
133 \snippet qml/xmlrole.qml 1
135 See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information.
139 \qmlproperty bool QtQuick.XmlListModel2::XmlRole::isKey
140 Defines whether this is a key role.
141 Key roles are used to to determine whether a set of values should
142 be updated or added to the XML list model when XmlListModel::reload()
154 QStringList roleQueries;
155 QList<void*> roleQueryErrorId; // the ptr to send back if there is an error
156 QStringList keyRoleQueries;
157 QStringList keyRoleResultsCache;
162 class QQuickXmlQueryEngine;
163 class QQuickXmlQueryThreadObject : public QObject
167 QQuickXmlQueryThreadObject(QQuickXmlQueryEngine *);
170 virtual bool event(QEvent *e);
173 QQuickXmlQueryEngine *m_queryEngine;
177 class QQuickXmlQueryEngine : public QThread
181 QQuickXmlQueryEngine(QQmlEngine *eng);
182 ~QQuickXmlQueryEngine();
184 int doQuery(QString query, QString namespaces, QByteArray data, QList<QQuickXmlListModelRole *>* roleObjects, QStringList keyRoleResultsCache);
189 static QQuickXmlQueryEngine *instance(QQmlEngine *engine);
192 void queryCompleted(const QQuickXmlQueryResult &);
193 void error(void*, const QString&);
199 void processQuery(XmlQueryJob *job);
200 void doQueryJob(XmlQueryJob *job, QQuickXmlQueryResult *currentResult);
201 void doSubQueryJob(XmlQueryJob *job, QQuickXmlQueryResult *currentResult);
202 void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const;
203 void addIndexToRangeList(QList<QQuickXmlListRange> *ranges, int index) const;
206 QQuickXmlQueryThreadObject *m_threadObject;
207 QList<XmlQueryJob> m_jobs;
208 QSet<int> m_cancelledJobs;
209 QAtomicInt m_queryIds;
211 QQmlEngine *m_engine;
212 QObject *m_eventLoopQuitHack;
214 static QHash<QQmlEngine *,QQuickXmlQueryEngine*> queryEngines;
215 static QMutex queryEnginesMutex;
217 QHash<QQmlEngine *,QQuickXmlQueryEngine*> QQuickXmlQueryEngine::queryEngines;
218 QMutex QQuickXmlQueryEngine::queryEnginesMutex;
221 QQuickXmlQueryThreadObject::QQuickXmlQueryThreadObject(QQuickXmlQueryEngine *e)
226 void QQuickXmlQueryThreadObject::processJobs()
228 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
231 bool QQuickXmlQueryThreadObject::event(QEvent *e)
233 if (e->type() == QEvent::User) {
234 m_queryEngine->processJobs();
237 return QObject::event(e);
243 QQuickXmlQueryEngine::QQuickXmlQueryEngine(QQmlEngine *eng)
244 : QThread(eng), m_threadObject(0), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1), m_engine(eng), m_eventLoopQuitHack(0)
246 qRegisterMetaType<QQuickXmlQueryResult>("QQuickXmlQueryResult");
248 m_eventLoopQuitHack = new QObject;
249 m_eventLoopQuitHack->moveToThread(this);
250 connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
251 start(QThread::IdlePriority);
254 QQuickXmlQueryEngine::~QQuickXmlQueryEngine()
256 queryEnginesMutex.lock();
257 queryEngines.remove(m_engine);
258 queryEnginesMutex.unlock();
260 m_eventLoopQuitHack->deleteLater();
264 int QQuickXmlQueryEngine::doQuery(QString query, QString namespaces, QByteArray data, QList<QQuickXmlListModelRole *>* roleObjects, QStringList keyRoleResultsCache) {
266 QMutexLocker m1(&m_mutex);
268 if (m_queryIds.load() <= 0)
273 job.queryId = m_queryIds.load();
275 job.query = QLatin1String("doc($src)") + query;
276 job.namespaces = namespaces;
277 job.keyRoleResultsCache = keyRoleResultsCache;
279 for (int i=0; i<roleObjects->count(); i++) {
280 if (!roleObjects->at(i)->isValid()) {
281 job.roleQueries << QString();
284 job.roleQueries << roleObjects->at(i)->query();
285 job.roleQueryErrorId << static_cast<void*>(roleObjects->at(i));
286 if (roleObjects->at(i)->isKey())
287 job.keyRoleQueries << job.roleQueries.last();
291 QMutexLocker ml(&m_mutex);
294 m_threadObject->processJobs();
300 void QQuickXmlQueryEngine::abort(int id)
302 QMutexLocker ml(&m_mutex);
304 m_cancelledJobs.insert(id);
307 void QQuickXmlQueryEngine::run()
310 m_threadObject = new QQuickXmlQueryThreadObject(this);
316 delete m_threadObject;
320 void QQuickXmlQueryEngine::processJobs()
322 QMutexLocker locker(&m_mutex);
325 if (m_jobs.isEmpty())
328 XmlQueryJob currentJob = m_jobs.takeLast();
329 while (m_cancelledJobs.remove(currentJob.queryId)) {
330 if (m_jobs.isEmpty())
332 currentJob = m_jobs.takeLast();
336 processQuery(¤tJob);
341 QQuickXmlQueryEngine *QQuickXmlQueryEngine::instance(QQmlEngine *engine)
343 queryEnginesMutex.lock();
344 QQuickXmlQueryEngine *queryEng = queryEngines.value(engine);
346 queryEng = new QQuickXmlQueryEngine(engine);
347 queryEngines.insert(engine, queryEng);
349 queryEnginesMutex.unlock();
354 void QQuickXmlQueryEngine::processQuery(XmlQueryJob *job)
356 QQuickXmlQueryResult result;
357 result.queryId = job->queryId;
358 doQueryJob(job, &result);
359 doSubQueryJob(job, &result);
362 QMutexLocker ml(&m_mutex);
363 if (m_cancelledJobs.contains(job->queryId)) {
364 m_cancelledJobs.remove(job->queryId);
366 emit queryCompleted(result);
371 void QQuickXmlQueryEngine::doQueryJob(XmlQueryJob *currentJob, QQuickXmlQueryResult *currentResult)
373 Q_ASSERT(currentJob->queryId != -1);
377 QBuffer buffer(¤tJob->data);
378 buffer.open(QIODevice::ReadOnly);
379 query.bindVariable(QLatin1String("src"), &buffer);
380 query.setQuery(currentJob->namespaces + currentJob->query);
381 query.evaluateTo(&r);
383 //always need a single root element
384 QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>";
386 b.open(QIODevice::ReadOnly);
388 QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces;
389 QString prefix = QLatin1String("doc($inputDocument)/dummy:items") +
390 currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/')));
392 //figure out how many items we are dealing with
395 QXmlResultItems result;
396 QXmlQuery countquery;
397 countquery.bindVariable(QLatin1String("inputDocument"), &b);
398 countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')'));
399 countquery.evaluateTo(&result);
400 QXmlItem item(result.next());
401 if (item.isAtomicValue())
402 count = item.toAtomicValue().toInt();
405 currentJob->data = xml;
406 currentJob->prefix = namespaces + prefix + QLatin1Char('/');
407 currentResult->size = (count > 0 ? count : 0);
410 void QQuickXmlQueryEngine::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const
412 const QStringList &keysQueries = currentJob.keyRoleQueries;
414 if (keysQueries.count() == 1)
415 keysQuery = currentJob.prefix + keysQueries[0];
416 else if (keysQueries.count() > 1)
417 keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")");
419 if (!keysQuery.isEmpty()) {
420 query->setQuery(keysQuery);
421 QXmlResultItems resultItems;
422 query->evaluateTo(&resultItems);
423 QXmlItem item(resultItems.next());
424 while (!item.isNull()) {
425 values->append(item.toAtomicValue().toString());
426 item = resultItems.next();
431 void QQuickXmlQueryEngine::addIndexToRangeList(QList<QQuickXmlListRange> *ranges, int index) const {
432 if (ranges->isEmpty())
433 ranges->append(qMakePair(index, 1));
434 else if (ranges->last().first + ranges->last().second == index)
435 ranges->last().second += 1;
437 ranges->append(qMakePair(index, 1));
440 void QQuickXmlQueryEngine::doSubQueryJob(XmlQueryJob *currentJob, QQuickXmlQueryResult *currentResult)
442 Q_ASSERT(currentJob->queryId != -1);
444 QBuffer b(¤tJob->data);
445 b.open(QIODevice::ReadOnly);
448 subquery.bindVariable(QLatin1String("inputDocument"), &b);
450 QStringList keyRoleResults;
451 getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery);
453 // See if any values of key roles have been inserted or removed.
455 if (currentJob->keyRoleResultsCache.isEmpty()) {
456 currentResult->inserted << qMakePair(0, currentResult->size);
458 if (keyRoleResults != currentJob->keyRoleResultsCache) {
460 for (int i=0; i<currentJob->keyRoleResultsCache.count(); i++) {
461 if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i]))
462 addIndexToRangeList(¤tResult->removed, i);
464 temp << currentJob->keyRoleResultsCache[i];
466 for (int i=0; i<keyRoleResults.count(); i++) {
467 if (temp.count() == i || keyRoleResults[i] != temp[i]) {
468 temp.insert(i, keyRoleResults[i]);
469 addIndexToRangeList(¤tResult->inserted, i);
474 currentResult->keyRoleResultsCache = keyRoleResults;
476 // Get the new values for each role.
477 //### we might be able to condense even further (query for everything in one go)
478 const QStringList &queries = currentJob->roleQueries;
479 for (int i = 0; i < queries.size(); ++i) {
480 QList<QVariant> resultList;
481 if (!queries[i].isEmpty()) {
482 subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")"));
483 if (subquery.isValid()) {
484 QXmlResultItems resultItems;
485 subquery.evaluateTo(&resultItems);
486 QXmlItem item(resultItems.next());
487 while (!item.isNull()) {
488 resultList << item.toAtomicValue(); //### we used to trim strings
489 item = resultItems.next();
492 emit error(currentJob->roleQueryErrorId.at(i), queries[i]);
495 //### should warn here if things have gone wrong.
496 while (resultList.count() < currentResult->size)
497 resultList << QVariant();
498 currentResult->data << resultList;
502 //this method is much slower, but works better for incremental loading
503 /*for (int j = 0; j < m_size; ++j) {
504 QList<QVariant> resultList;
505 for (int i = 0; i < m_roleObjects->size(); ++i) {
506 QQuickXmlListModelRole *role = m_roleObjects->at(i);
507 subquery.setQuery(m_prefix.arg(j+1) + role->query());
508 if (role->isStringList()) {
510 subquery.evaluateTo(&data);
511 resultList << QVariant(data);
515 subquery.evaluateTo(&s);
516 if (role->isCData()) {
518 s.replace(QLatin1String("<"), QLatin1String("<"));
519 s.replace(QLatin1String(">"), QLatin1String(">"));
520 s.replace(QLatin1String("&"), QLatin1String("&"));
522 resultList << s.trimmed();
527 m_modelData << resultList;
531 class QQuickXmlListModelPrivate : public QAbstractItemModelPrivate
533 Q_DECLARE_PUBLIC(QQuickXmlListModel)
535 QQuickXmlListModelPrivate()
536 : isComponentComplete(true), size(-1), highestRole(Qt::UserRole)
537 , reply(0), status(QQuickXmlListModel::Null), progress(0.0)
538 , queryId(-1), roleObjects(), redirectCount(0) {}
541 void notifyQueryStarted(bool remoteSource) {
542 Q_Q(QQuickXmlListModel);
543 progress = remoteSource ? 0.0 : 1.0;
544 status = QQuickXmlListModel::Loading;
546 emit q->progressChanged(progress);
547 emit q->statusChanged(status);
551 Q_Q(QQuickXmlListModel);
553 QObject::disconnect(reply, 0, q, 0);
554 reply->deleteLater();
559 bool isComponentComplete;
566 QStringList roleNames;
569 QNetworkReply *reply;
570 QQuickXmlListModel::Status status;
574 QStringList keyRoleResultsCache;
575 QList<QQuickXmlListModelRole *> roleObjects;
577 static void append_role(QQmlListProperty<QQuickXmlListModelRole> *list, QQuickXmlListModelRole *role);
578 static void clear_role(QQmlListProperty<QQuickXmlListModelRole> *list);
579 QList<QList<QVariant> > data;
584 void QQuickXmlListModelPrivate::append_role(QQmlListProperty<QQuickXmlListModelRole> *list, QQuickXmlListModelRole *role)
586 QQuickXmlListModel *_this = qobject_cast<QQuickXmlListModel *>(list->object);
588 int i = _this->d_func()->roleObjects.count();
589 _this->d_func()->roleObjects.append(role);
590 if (_this->d_func()->roleNames.contains(role->name())) {
591 qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name());
594 _this->d_func()->roles.insert(i, _this->d_func()->highestRole);
595 _this->d_func()->roleNames.insert(i, role->name());
596 ++_this->d_func()->highestRole;
600 //### clear needs to invalidate any cached data (in data table) as well
601 // (and the model should emit the appropriate signals)
602 void QQuickXmlListModelPrivate::clear_role(QQmlListProperty<QQuickXmlListModelRole> *list)
604 QQuickXmlListModel *_this = static_cast<QQuickXmlListModel *>(list->object);
605 _this->d_func()->roles.clear();
606 _this->d_func()->roleNames.clear();
607 _this->d_func()->roleObjects.clear();
611 \qmlclass XmlListModel QQuickXmlListModel
612 \inqmlmodule QtQuick.XmlListModel 2
613 \brief For specifying a read-only model using XPath expressions
614 \ingroup qtquick-models
617 To use this element, you will need to import the module with the following line:
619 import QtQuick.XmlListModel 2.0
622 XmlListModel is used to create a read-only model from XML data. It can be used as a data source
623 for view elements (such as ListView, PathView, GridView) and other elements that interact with model
624 data (such as \l Repeater).
626 For example, if there is a XML document at http://www.mysite.com/feed.xml like this:
629 <?xml version="1.0" encoding="utf-8"?>
634 <title>A blog post</title>
635 <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
638 <title>Another blog post</title>
639 <pubDate>Sat, 07 Sep 2010 15:35:01 GMT</pubDate>
645 A XmlListModel could create a model from this data, like this:
649 import QtQuick.XmlListModel 2.0
653 source: "http://www.mysite.com/feed.xml"
654 query: "/rss/channel/item"
656 XmlRole { name: "title"; query: "title/string()" }
657 XmlRole { name: "pubDate"; query: "pubDate/string()" }
661 The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate
662 a model item for each \c <item> in the XML document.
664 The XmlRole objects define the
665 model item attributes. Here, each model item will have \c title and \c pubDate
666 attributes that match the \c title and \c pubDate values of its corresponding \c <item>.
667 (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.)
669 The model could be used in a ListView, like this:
673 width: 180; height: 300
675 delegate: Text { text: title + ": " + pubDate }
679 \image qml-xmllistmodel-example.png
681 The XmlListModel data is loaded asynchronously, and \l status
682 is set to \c XmlListModel.Ready when loading is complete.
683 Note this means when XmlListModel is used for a view, the view is not
684 populated until the model is loaded.
687 \section2 Using key XML roles
689 You can define certain roles as "keys" so that when reload() is called,
690 the model will only add and refresh data that contains new values for
693 For example, if above role for "pubDate" was defined like this instead:
696 XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true }
699 Then when reload() is called, the model will only add and reload
700 items with a "pubDate" value that is not already
701 present in the model.
703 This is useful when displaying the contents of XML documents that
704 are incrementally updated (such as RSS feeds) to avoid repainting the
705 entire contents of a model in a view.
707 If multiple key roles are specified, the model only adds and reload items
708 with a combined value of all key roles that is not already present in
714 QQuickXmlListModel::QQuickXmlListModel(QObject *parent)
715 : QAbstractListModel(*(new QQuickXmlListModelPrivate), parent)
719 QQuickXmlListModel::~QQuickXmlListModel()
724 \qmlproperty list<XmlRole> QtQuick.XmlListModel2::XmlListModel::roles
726 The roles to make available for this model.
728 QQmlListProperty<QQuickXmlListModelRole> QQuickXmlListModel::roleObjects()
730 Q_D(QQuickXmlListModel);
731 QQmlListProperty<QQuickXmlListModelRole> list(this, d->roleObjects);
732 list.append = &QQuickXmlListModelPrivate::append_role;
733 list.clear = &QQuickXmlListModelPrivate::clear_role;
737 QModelIndex QQuickXmlListModel::index(int row, int column, const QModelIndex &parent) const
739 Q_D(const QQuickXmlListModel);
740 return !parent.isValid() && column == 0 && row >= 0 && row < d->size
741 ? createIndex(row, column)
745 int QQuickXmlListModel::rowCount(const QModelIndex &parent) const
747 Q_D(const QQuickXmlListModel);
748 return !parent.isValid() ? d->size : 0;
751 QVariant QQuickXmlListModel::data(const QModelIndex &index, int role) const
753 Q_D(const QQuickXmlListModel);
754 const int roleIndex = d->roles.indexOf(role);
755 return (roleIndex == -1 || !index.isValid())
757 : d->data.value(roleIndex).value(index.row());
760 QHash<int, QByteArray> QQuickXmlListModel::roleNames() const
762 Q_D(const QQuickXmlListModel);
763 QHash<int,QByteArray> roleNames;
764 for (int i = 0; i < d->roles.count(); ++i)
765 roleNames.insert(d->roles.at(i), d->roleNames.at(i).toUtf8());
770 \qmlproperty int QtQuick.XmlListModel2::XmlListModel::count
771 The number of data entries in the model.
773 int QQuickXmlListModel::count() const
775 Q_D(const QQuickXmlListModel);
780 \qmlproperty url QtQuick.XmlListModel2::XmlListModel::source
781 The location of the XML data source.
783 If both \c source and \l xml are set, \l xml is used.
785 QUrl QQuickXmlListModel::source() const
787 Q_D(const QQuickXmlListModel);
791 void QQuickXmlListModel::setSource(const QUrl &src)
793 Q_D(QQuickXmlListModel);
796 if (d->xml.isEmpty()) // src is only used if d->xml is not set
798 emit sourceChanged();
803 \qmlproperty string QtQuick.XmlListModel2::XmlListModel::xml
804 This property holds the XML data for this model, if set.
806 The text is assumed to be UTF-8 encoded.
808 If both \l source and \c xml are set, \c xml is used.
810 QString QQuickXmlListModel::xml() const
812 Q_D(const QQuickXmlListModel);
816 void QQuickXmlListModel::setXml(const QString &xml)
818 Q_D(QQuickXmlListModel);
827 \qmlproperty string QtQuick.XmlListModel2::XmlListModel::query
828 An absolute XPath query representing the base query for creating model items
829 from this model's XmlRole objects. The query should start with '/' or '//'.
831 QString QQuickXmlListModel::query() const
833 Q_D(const QQuickXmlListModel);
837 void QQuickXmlListModel::setQuery(const QString &query)
839 Q_D(QQuickXmlListModel);
840 if (!query.startsWith(QLatin1Char('/'))) {
841 qmlInfo(this) << QCoreApplication::translate("QQuickXmlRoleList", "An XmlListModel query must start with '/' or \"//\"");
845 if (d->query != query) {
853 \qmlproperty string QtQuick.XmlListModel2::XmlListModel::namespaceDeclarations
854 The namespace declarations to be used in the XPath queries.
856 The namespaces should be declared as in XQuery. For example, if a requested document
857 at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom",
858 this can be declared as the default namespace:
862 source: "http://mysite.com/feed.xml"
864 namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';"
866 XmlRole { name: "title"; query: "title/string()" }
870 QString QQuickXmlListModel::namespaceDeclarations() const
872 Q_D(const QQuickXmlListModel);
873 return d->namespaces;
876 void QQuickXmlListModel::setNamespaceDeclarations(const QString &declarations)
878 Q_D(QQuickXmlListModel);
879 if (d->namespaces != declarations) {
880 d->namespaces = declarations;
882 emit namespaceDeclarationsChanged();
887 \qmlmethod object QtQuick.XmlListModel2::XmlListModel::get(int index)
889 Returns the item at \a index in the model.
891 For example, for a model like this:
896 source: "http://mysite.com/feed.xml"
898 XmlRole { name: "title"; query: "title/string()" }
902 This will access the \c title value for the first item in the model:
905 var title = model.get(0).title;
908 QQmlV8Handle QQuickXmlListModel::get(int index) const
910 // Must be called with a context and handle scope
911 Q_D(const QQuickXmlListModel);
913 if (index < 0 || index >= count())
914 return QQmlV8Handle::fromHandle(v8::Undefined());
916 QQmlEngine *engine = qmlContext(this)->engine();
917 QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine);
918 v8::Local<v8::Object> rv = v8::Object::New();
919 for (int ii = 0; ii < d->roleObjects.count(); ++ii)
920 rv->Set(v8engine->toString(d->roleObjects[ii]->name()),
921 v8engine->fromVariant(d->data.value(ii).value(index)));
923 return QQmlV8Handle::fromHandle(rv);
927 \qmlproperty enumeration QtQuick.XmlListModel2::XmlListModel::status
928 Specifies the model loading status, which can be one of the following:
931 \li XmlListModel.Null - No XML data has been set for this model.
932 \li XmlListModel.Ready - The XML data has been loaded into the model.
933 \li XmlListModel.Loading - The model is in the process of reading and loading XML data.
934 \li XmlListModel.Error - An error occurred while the model was loading. See errorString() for details
941 QQuickXmlListModel::Status QQuickXmlListModel::status() const
943 Q_D(const QQuickXmlListModel);
948 \qmlproperty real QtQuick.XmlListModel2::XmlListModel::progress
950 This indicates the current progress of the downloading of the XML data
951 source. This value ranges from 0.0 (no data downloaded) to
952 1.0 (all data downloaded). If the XML data is not from a remote source,
953 the progress becomes 1.0 as soon as the data is read.
955 Note that when the progress is 1.0, the XML data has been downloaded, but
956 it is yet to be loaded into the model at this point. Use the status
957 property to find out when the XML data has been read and loaded into
962 qreal QQuickXmlListModel::progress() const
964 Q_D(const QQuickXmlListModel);
969 \qmlmethod void QtQuick.XmlListModel2::XmlListModel::errorString()
971 Returns a string description of the last error that occurred
972 if \l status is XmlListModel::Error.
974 QString QQuickXmlListModel::errorString() const
976 Q_D(const QQuickXmlListModel);
977 return d->errorString;
980 void QQuickXmlListModel::classBegin()
982 Q_D(QQuickXmlListModel);
983 d->isComponentComplete = false;
985 QQuickXmlQueryEngine *queryEngine = QQuickXmlQueryEngine::instance(qmlEngine(this));
986 connect(queryEngine, SIGNAL(queryCompleted(QQuickXmlQueryResult)),
987 SLOT(queryCompleted(QQuickXmlQueryResult)));
988 connect(queryEngine, SIGNAL(error(void*,QString)),
989 SLOT(queryError(void*,QString)));
992 void QQuickXmlListModel::componentComplete()
994 Q_D(QQuickXmlListModel);
995 d->isComponentComplete = true;
1000 \qmlmethod QtQuick.XmlListModel2::XmlListModel::reload()
1004 If no key roles have been specified, all existing model
1005 data is removed, and the model is rebuilt from scratch.
1007 Otherwise, items are only added if the model does not already
1008 contain items with matching key role values.
1010 \sa {Using key XML roles}, XmlRole::isKey
1012 void QQuickXmlListModel::reload()
1014 Q_D(QQuickXmlListModel);
1016 if (!d->isComponentComplete)
1019 QQuickXmlQueryEngine::instance(qmlEngine(this))->abort(d->queryId);
1030 if (!d->xml.isEmpty()) {
1031 d->queryId = QQuickXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache);
1032 d->notifyQueryStarted(false);
1034 } else if (d->src.isEmpty()) {
1035 d->queryId = XMLLISTMODEL_CLEAR_ID;
1036 d->notifyQueryStarted(false);
1037 QTimer::singleShot(0, this, SLOT(dataCleared()));
1040 d->notifyQueryStarted(true);
1041 QNetworkRequest req(d->src);
1042 req.setRawHeader("Accept", "application/xml,*/*");
1043 d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req);
1044 QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished()));
1045 QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
1046 this, SLOT(requestProgress(qint64,qint64)));
1050 #define XMLLISTMODEL_MAX_REDIRECT 16
1052 void QQuickXmlListModel::requestFinished()
1054 Q_D(QQuickXmlListModel);
1057 if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) {
1058 QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
1059 if (redirect.isValid()) {
1060 QUrl url = d->reply->url().resolved(redirect.toUrl());
1066 d->redirectCount = 0;
1068 if (d->reply->error() != QNetworkReply::NoError) {
1069 d->errorString = d->reply->errorString();
1073 beginRemoveRows(QModelIndex(), 0, d->size - 1);
1077 emit countChanged();
1082 emit statusChanged(d->status);
1084 QByteArray data = d->reply->readAll();
1085 if (data.isEmpty()) {
1086 d->queryId = XMLLISTMODEL_CLEAR_ID;
1087 QTimer::singleShot(0, this, SLOT(dataCleared()));
1089 d->queryId = QQuickXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
1094 emit progressChanged(d->progress);
1098 void QQuickXmlListModel::requestProgress(qint64 received, qint64 total)
1100 Q_D(QQuickXmlListModel);
1101 if (d->status == Loading && total > 0) {
1102 d->progress = qreal(received)/total;
1103 emit progressChanged(d->progress);
1107 void QQuickXmlListModel::dataCleared()
1109 Q_D(QQuickXmlListModel);
1110 QQuickXmlQueryResult r;
1111 r.queryId = XMLLISTMODEL_CLEAR_ID;
1113 r.removed << qMakePair(0, count());
1114 r.keyRoleResultsCache = d->keyRoleResultsCache;
1118 void QQuickXmlListModel::queryError(void* object, const QString& error)
1120 // Be extra careful, object may no longer exist, it's just an ID.
1121 Q_D(QQuickXmlListModel);
1122 for (int i=0; i<d->roleObjects.count(); i++) {
1123 if (d->roleObjects.at(i) == static_cast<QQuickXmlListModelRole*>(object)) {
1124 qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error);
1128 qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error);
1131 void QQuickXmlListModel::queryCompleted(const QQuickXmlQueryResult &result)
1133 Q_D(QQuickXmlListModel);
1134 if (result.queryId != d->queryId)
1137 int origCount = d->size;
1138 bool sizeChanged = result.size != d->size;
1140 d->size = result.size;
1141 d->data = result.data;
1142 d->keyRoleResultsCache = result.keyRoleResultsCache;
1143 if (d->src.isEmpty() && d->xml.isEmpty())
1147 d->errorString.clear();
1150 bool hasKeys = false;
1151 for (int i=0; i<d->roleObjects.count(); i++) {
1152 if (d->roleObjects[i]->isKey()) {
1158 if (origCount > 0) {
1159 beginRemoveRows(QModelIndex(), 0, origCount - 1);
1163 beginInsertRows(QModelIndex(), 0, d->size - 1);
1167 for (int i=0; i<result.removed.count(); i++) {
1168 const int index = result.removed[i].first;
1169 const int count = result.removed[i].second;
1171 beginRemoveRows(QModelIndex(), index, index + count - 1);
1175 for (int i=0; i<result.inserted.count(); i++) {
1176 const int index = result.inserted[i].first;
1177 const int count = result.inserted[i].second;
1179 beginInsertRows(QModelIndex(), index, index + count - 1);
1185 emit countChanged();
1187 emit statusChanged(d->status);
1192 #include <qqmlxmllistmodel.moc>