1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation (qt-info@nokia.com)
|
---|
6 | **
|
---|
7 | ** This file is part of the QtDeclarative module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Lesser General Public License Usage
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
18 | ** General Public License version 2.1 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
20 | ** packaging of this file. Please review the following information to
|
---|
21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
23 | **
|
---|
24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at qt-info@nokia.com.
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "private/qdeclarativexmllistmodel_p.h"
|
---|
43 |
|
---|
44 | #include <qdeclarativecontext.h>
|
---|
45 | #include <qdeclarativeengine_p.h>
|
---|
46 |
|
---|
47 | #include <QDebug>
|
---|
48 | #include <QStringList>
|
---|
49 | #include <QQueue>
|
---|
50 | #include <QApplication>
|
---|
51 | #include <QThread>
|
---|
52 | #include <QMutex>
|
---|
53 | #include <QWaitCondition>
|
---|
54 | #include <QXmlQuery>
|
---|
55 | #include <QXmlResultItems>
|
---|
56 | #include <QXmlNodeModelIndex>
|
---|
57 | #include <QBuffer>
|
---|
58 | #include <QNetworkRequest>
|
---|
59 | #include <QNetworkReply>
|
---|
60 | #include <QTimer>
|
---|
61 |
|
---|
62 | #include <private/qobject_p.h>
|
---|
63 |
|
---|
64 | Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult)
|
---|
65 |
|
---|
66 | QT_BEGIN_NAMESPACE
|
---|
67 |
|
---|
68 |
|
---|
69 | typedef QPair<int, int> QDeclarativeXmlListRange;
|
---|
70 |
|
---|
71 | #define XMLLISTMODEL_CLEAR_ID 0
|
---|
72 |
|
---|
73 | /*!
|
---|
74 | \qmlclass XmlRole QDeclarativeXmlListModelRole
|
---|
75 | \ingroup qml-working-with-data
|
---|
76 | \since 4.7
|
---|
77 | \brief The XmlRole element allows you to specify a role for an XmlListModel.
|
---|
78 |
|
---|
79 | \sa {QtDeclarative}
|
---|
80 | */
|
---|
81 |
|
---|
82 | /*!
|
---|
83 | \qmlproperty string XmlRole::name
|
---|
84 |
|
---|
85 | The name for the role. This name is used to access the model data for this role.
|
---|
86 |
|
---|
87 | For example, the following model has a role named "title", which can be accessed
|
---|
88 | from the view's delegate:
|
---|
89 |
|
---|
90 | \qml
|
---|
91 | XmlListModel {
|
---|
92 | id: xmlModel
|
---|
93 | ...
|
---|
94 | XmlRole { name: "title"; query: "title/string()" }
|
---|
95 | }
|
---|
96 |
|
---|
97 | ListView {
|
---|
98 | model: xmlModel
|
---|
99 | delegate: Text { text: title }
|
---|
100 | }
|
---|
101 | \endqml
|
---|
102 | */
|
---|
103 |
|
---|
104 | /*!
|
---|
105 | \qmlproperty string XmlRole::query
|
---|
106 | The relative XPath expression query for this role. The query must be relative; it cannot start
|
---|
107 | with a '/'.
|
---|
108 |
|
---|
109 | For example, if there is an XML document like this:
|
---|
110 |
|
---|
111 | \quotefile doc/src/snippets/declarative/xmlrole.xml
|
---|
112 |
|
---|
113 | Here are some valid XPath expressions for XmlRole queries on this document:
|
---|
114 |
|
---|
115 | \snippet doc/src/snippets/declarative/xmlrole.qml 0
|
---|
116 | \dots 4
|
---|
117 | \snippet doc/src/snippets/declarative/xmlrole.qml 1
|
---|
118 |
|
---|
119 | See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information.
|
---|
120 | */
|
---|
121 |
|
---|
122 | /*!
|
---|
123 | \qmlproperty bool XmlRole::isKey
|
---|
124 | Defines whether this is a key role.
|
---|
125 |
|
---|
126 | Key roles are used to to determine whether a set of values should
|
---|
127 | be updated or added to the XML list model when XmlListModel::reload()
|
---|
128 | is called.
|
---|
129 |
|
---|
130 | \sa XmlListModel
|
---|
131 | */
|
---|
132 |
|
---|
133 | struct XmlQueryJob
|
---|
134 | {
|
---|
135 | int queryId;
|
---|
136 | QByteArray data;
|
---|
137 | QString query;
|
---|
138 | QString namespaces;
|
---|
139 | QStringList roleQueries;
|
---|
140 | QList<void*> roleQueryErrorId; // the ptr to send back if there is an error
|
---|
141 | QStringList keyRoleQueries;
|
---|
142 | QStringList keyRoleResultsCache;
|
---|
143 | };
|
---|
144 |
|
---|
145 | class QDeclarativeXmlQuery : public QThread
|
---|
146 | {
|
---|
147 | Q_OBJECT
|
---|
148 | public:
|
---|
149 | QDeclarativeXmlQuery(QObject *parent=0)
|
---|
150 | : QThread(parent), m_quit(false), m_abortQueryId(-1), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1) {
|
---|
151 | qRegisterMetaType<QDeclarativeXmlQueryResult>("QDeclarativeXmlQueryResult");
|
---|
152 | m_currentJob.queryId = -1;
|
---|
153 | }
|
---|
154 |
|
---|
155 | ~QDeclarativeXmlQuery() {
|
---|
156 | m_mutex.lock();
|
---|
157 | m_quit = true;
|
---|
158 | m_condition.wakeOne();
|
---|
159 | m_mutex.unlock();
|
---|
160 |
|
---|
161 | wait();
|
---|
162 | }
|
---|
163 |
|
---|
164 | void abort(int id) {
|
---|
165 | QMutexLocker locker(&m_mutex);
|
---|
166 | QQueue<XmlQueryJob>::iterator it;
|
---|
167 | for (it = m_jobs.begin(); it != m_jobs.end(); ++it) {
|
---|
168 | if ((*it).queryId == id) {
|
---|
169 | m_jobs.erase(it);
|
---|
170 | return;
|
---|
171 | }
|
---|
172 | }
|
---|
173 | m_abortQueryId = id;
|
---|
174 | }
|
---|
175 |
|
---|
176 | int doQuery(QString query, QString namespaces, QByteArray data, QList<QDeclarativeXmlListModelRole *> *roleObjects, QStringList keyRoleResultsCache) {
|
---|
177 | QMutexLocker locker(&m_mutex);
|
---|
178 |
|
---|
179 | XmlQueryJob job;
|
---|
180 | job.queryId = m_queryIds;
|
---|
181 | job.data = data;
|
---|
182 | job.query = QLatin1String("doc($src)") + query;
|
---|
183 | job.namespaces = namespaces;
|
---|
184 | job.keyRoleResultsCache = keyRoleResultsCache;
|
---|
185 |
|
---|
186 | for (int i=0; i<roleObjects->count(); i++) {
|
---|
187 | if (!roleObjects->at(i)->isValid()) {
|
---|
188 | job.roleQueries << QString();
|
---|
189 | continue;
|
---|
190 | }
|
---|
191 | job.roleQueries << roleObjects->at(i)->query();
|
---|
192 | job.roleQueryErrorId << static_cast<void*>(roleObjects->at(i));
|
---|
193 | if (roleObjects->at(i)->isKey())
|
---|
194 | job.keyRoleQueries << job.roleQueries.last();
|
---|
195 | }
|
---|
196 | m_jobs.enqueue(job);
|
---|
197 | m_queryIds++;
|
---|
198 |
|
---|
199 | if (!isRunning())
|
---|
200 | start(QThread::IdlePriority);
|
---|
201 | else
|
---|
202 | m_condition.wakeOne();
|
---|
203 | return job.queryId;
|
---|
204 | }
|
---|
205 |
|
---|
206 | Q_SIGNALS:
|
---|
207 | void queryCompleted(const QDeclarativeXmlQueryResult &);
|
---|
208 | void error(void*, const QString&);
|
---|
209 |
|
---|
210 | protected:
|
---|
211 | void run() {
|
---|
212 | m_mutex.lock();
|
---|
213 |
|
---|
214 | while (!m_quit) {
|
---|
215 | if (!m_jobs.isEmpty())
|
---|
216 | m_currentJob = m_jobs.dequeue();
|
---|
217 | m_mutex.unlock();
|
---|
218 |
|
---|
219 | QDeclarativeXmlQueryResult r;
|
---|
220 | if (m_currentJob.queryId != -1) {
|
---|
221 | doQueryJob();
|
---|
222 | doSubQueryJob();
|
---|
223 | r.queryId = m_currentJob.queryId;
|
---|
224 | r.size = m_size;
|
---|
225 | r.data = m_modelData;
|
---|
226 | r.inserted = m_insertedItemRanges;
|
---|
227 | r.removed = m_removedItemRanges;
|
---|
228 | r.keyRoleResultsCache = m_currentJob.keyRoleResultsCache;
|
---|
229 | }
|
---|
230 |
|
---|
231 | m_mutex.lock();
|
---|
232 | if (m_currentJob.queryId != -1 && m_abortQueryId != m_currentJob.queryId)
|
---|
233 | emit queryCompleted(r);
|
---|
234 | if (m_jobs.isEmpty() && !m_quit)
|
---|
235 | m_condition.wait(&m_mutex);
|
---|
236 | m_currentJob.queryId = -1;
|
---|
237 | m_abortQueryId = -1;
|
---|
238 | }
|
---|
239 |
|
---|
240 | m_mutex.unlock();
|
---|
241 | }
|
---|
242 |
|
---|
243 | private:
|
---|
244 | void doQueryJob();
|
---|
245 | void doSubQueryJob();
|
---|
246 | void getValuesOfKeyRoles(QStringList *values, QXmlQuery *query) const;
|
---|
247 | void addIndexToRangeList(QList<QDeclarativeXmlListRange> *ranges, int index) const;
|
---|
248 |
|
---|
249 | private:
|
---|
250 | QMutex m_mutex;
|
---|
251 | QWaitCondition m_condition;
|
---|
252 | QQueue<XmlQueryJob> m_jobs;
|
---|
253 | XmlQueryJob m_currentJob;
|
---|
254 | bool m_quit;
|
---|
255 | int m_abortQueryId;
|
---|
256 | QString m_prefix;
|
---|
257 | int m_size;
|
---|
258 | int m_queryIds;
|
---|
259 | QList<QList<QVariant> > m_modelData;
|
---|
260 | QList<QDeclarativeXmlListRange> m_insertedItemRanges;
|
---|
261 | QList<QDeclarativeXmlListRange> m_removedItemRanges;
|
---|
262 | };
|
---|
263 |
|
---|
264 | Q_GLOBAL_STATIC(QDeclarativeXmlQuery, globalXmlQuery)
|
---|
265 |
|
---|
266 | void QDeclarativeXmlQuery::doQueryJob()
|
---|
267 | {
|
---|
268 | Q_ASSERT(m_currentJob.queryId != -1);
|
---|
269 |
|
---|
270 | QString r;
|
---|
271 | QXmlQuery query;
|
---|
272 | QBuffer buffer(&m_currentJob.data);
|
---|
273 | buffer.open(QIODevice::ReadOnly);
|
---|
274 | query.bindVariable(QLatin1String("src"), &buffer);
|
---|
275 | query.setQuery(m_currentJob.namespaces + m_currentJob.query);
|
---|
276 | query.evaluateTo(&r);
|
---|
277 |
|
---|
278 | //always need a single root element
|
---|
279 | QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>";
|
---|
280 | QBuffer b(&xml);
|
---|
281 | b.open(QIODevice::ReadOnly);
|
---|
282 |
|
---|
283 | QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + m_currentJob.namespaces;
|
---|
284 | QString prefix = QLatin1String("doc($inputDocument)/dummy:items") +
|
---|
285 | m_currentJob.query.mid(m_currentJob.query.lastIndexOf(QLatin1Char('/')));
|
---|
286 |
|
---|
287 | //figure out how many items we are dealing with
|
---|
288 | int count = -1;
|
---|
289 | {
|
---|
290 | QXmlResultItems result;
|
---|
291 | QXmlQuery countquery;
|
---|
292 | countquery.bindVariable(QLatin1String("inputDocument"), &b);
|
---|
293 | countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')'));
|
---|
294 | countquery.evaluateTo(&result);
|
---|
295 | QXmlItem item(result.next());
|
---|
296 | if (item.isAtomicValue())
|
---|
297 | count = item.toAtomicValue().toInt();
|
---|
298 | }
|
---|
299 |
|
---|
300 | m_currentJob.data = xml;
|
---|
301 | m_prefix = namespaces + prefix + QLatin1Char('/');
|
---|
302 | m_size = 0;
|
---|
303 | if (count > 0)
|
---|
304 | m_size = count;
|
---|
305 | }
|
---|
306 |
|
---|
307 | void QDeclarativeXmlQuery::getValuesOfKeyRoles(QStringList *values, QXmlQuery *query) const
|
---|
308 | {
|
---|
309 | Q_ASSERT(m_currentJob.queryId != -1);
|
---|
310 |
|
---|
311 | const QStringList &keysQueries = m_currentJob.keyRoleQueries;
|
---|
312 | QString keysQuery;
|
---|
313 | if (keysQueries.count() == 1)
|
---|
314 | keysQuery = m_prefix + keysQueries[0];
|
---|
315 | else if (keysQueries.count() > 1)
|
---|
316 | keysQuery = m_prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")");
|
---|
317 |
|
---|
318 | if (!keysQuery.isEmpty()) {
|
---|
319 | query->setQuery(keysQuery);
|
---|
320 | QXmlResultItems resultItems;
|
---|
321 | query->evaluateTo(&resultItems);
|
---|
322 | QXmlItem item(resultItems.next());
|
---|
323 | while (!item.isNull()) {
|
---|
324 | values->append(item.toAtomicValue().toString());
|
---|
325 | item = resultItems.next();
|
---|
326 | }
|
---|
327 | }
|
---|
328 | }
|
---|
329 |
|
---|
330 | void QDeclarativeXmlQuery::addIndexToRangeList(QList<QDeclarativeXmlListRange> *ranges, int index) const {
|
---|
331 | if (ranges->isEmpty())
|
---|
332 | ranges->append(qMakePair(index, 1));
|
---|
333 | else if (ranges->last().first + ranges->last().second == index)
|
---|
334 | ranges->last().second += 1;
|
---|
335 | else
|
---|
336 | ranges->append(qMakePair(index, 1));
|
---|
337 | }
|
---|
338 |
|
---|
339 | void QDeclarativeXmlQuery::doSubQueryJob()
|
---|
340 | {
|
---|
341 | Q_ASSERT(m_currentJob.queryId != -1);
|
---|
342 | m_modelData.clear();
|
---|
343 |
|
---|
344 | QBuffer b(&m_currentJob.data);
|
---|
345 | b.open(QIODevice::ReadOnly);
|
---|
346 |
|
---|
347 | QXmlQuery subquery;
|
---|
348 | subquery.bindVariable(QLatin1String("inputDocument"), &b);
|
---|
349 |
|
---|
350 | QStringList keyRoleResults;
|
---|
351 | getValuesOfKeyRoles(&keyRoleResults, &subquery);
|
---|
352 |
|
---|
353 | // See if any values of key roles have been inserted or removed.
|
---|
354 |
|
---|
355 | m_insertedItemRanges.clear();
|
---|
356 | m_removedItemRanges.clear();
|
---|
357 | if (m_currentJob.keyRoleResultsCache.isEmpty()) {
|
---|
358 | m_insertedItemRanges << qMakePair(0, m_size);
|
---|
359 | } else {
|
---|
360 | if (keyRoleResults != m_currentJob.keyRoleResultsCache) {
|
---|
361 | QStringList temp;
|
---|
362 | for (int i=0; i<m_currentJob.keyRoleResultsCache.count(); i++) {
|
---|
363 | if (!keyRoleResults.contains(m_currentJob.keyRoleResultsCache[i]))
|
---|
364 | addIndexToRangeList(&m_removedItemRanges, i);
|
---|
365 | else
|
---|
366 | temp << m_currentJob.keyRoleResultsCache[i];
|
---|
367 | }
|
---|
368 |
|
---|
369 | for (int i=0; i<keyRoleResults.count(); i++) {
|
---|
370 | if (temp.count() == i || keyRoleResults[i] != temp[i]) {
|
---|
371 | temp.insert(i, keyRoleResults[i]);
|
---|
372 | addIndexToRangeList(&m_insertedItemRanges, i);
|
---|
373 | }
|
---|
374 | }
|
---|
375 | }
|
---|
376 | }
|
---|
377 | m_currentJob.keyRoleResultsCache = keyRoleResults;
|
---|
378 |
|
---|
379 | // Get the new values for each role.
|
---|
380 | //### we might be able to condense even further (query for everything in one go)
|
---|
381 | const QStringList &queries = m_currentJob.roleQueries;
|
---|
382 | for (int i = 0; i < queries.size(); ++i) {
|
---|
383 | QList<QVariant> resultList;
|
---|
384 | if (!queries[i].isEmpty()) {
|
---|
385 | subquery.setQuery(m_prefix + QLatin1String("(let $v := ") + queries[i] + QLatin1String(" return if ($v) then ") + queries[i] + QLatin1String(" else \"\")"));
|
---|
386 | if (subquery.isValid()) {
|
---|
387 | QXmlResultItems resultItems;
|
---|
388 | subquery.evaluateTo(&resultItems);
|
---|
389 | QXmlItem item(resultItems.next());
|
---|
390 | while (!item.isNull()) {
|
---|
391 | resultList << item.toAtomicValue(); //### we used to trim strings
|
---|
392 | item = resultItems.next();
|
---|
393 | }
|
---|
394 | } else {
|
---|
395 | emit error(m_currentJob.roleQueryErrorId.at(i), queries[i]);
|
---|
396 | }
|
---|
397 | }
|
---|
398 | //### should warn here if things have gone wrong.
|
---|
399 | while (resultList.count() < m_size)
|
---|
400 | resultList << QVariant();
|
---|
401 | m_modelData << resultList;
|
---|
402 | b.seek(0);
|
---|
403 | }
|
---|
404 |
|
---|
405 | //this method is much slower, but works better for incremental loading
|
---|
406 | /*for (int j = 0; j < m_size; ++j) {
|
---|
407 | QList<QVariant> resultList;
|
---|
408 | for (int i = 0; i < m_roleObjects->size(); ++i) {
|
---|
409 | QDeclarativeXmlListModelRole *role = m_roleObjects->at(i);
|
---|
410 | subquery.setQuery(m_prefix.arg(j+1) + role->query());
|
---|
411 | if (role->isStringList()) {
|
---|
412 | QStringList data;
|
---|
413 | subquery.evaluateTo(&data);
|
---|
414 | resultList << QVariant(data);
|
---|
415 | //qDebug() << data;
|
---|
416 | } else {
|
---|
417 | QString s;
|
---|
418 | subquery.evaluateTo(&s);
|
---|
419 | if (role->isCData()) {
|
---|
420 | //un-escape
|
---|
421 | s.replace(QLatin1String("<"), QLatin1String("<"));
|
---|
422 | s.replace(QLatin1String(">"), QLatin1String(">"));
|
---|
423 | s.replace(QLatin1String("&"), QLatin1String("&"));
|
---|
424 | }
|
---|
425 | resultList << s.trimmed();
|
---|
426 | //qDebug() << s;
|
---|
427 | }
|
---|
428 | b.seek(0);
|
---|
429 | }
|
---|
430 | m_modelData << resultList;
|
---|
431 | }*/
|
---|
432 | }
|
---|
433 |
|
---|
434 | class QDeclarativeXmlListModelPrivate : public QObjectPrivate
|
---|
435 | {
|
---|
436 | Q_DECLARE_PUBLIC(QDeclarativeXmlListModel)
|
---|
437 | public:
|
---|
438 | QDeclarativeXmlListModelPrivate()
|
---|
439 | : isComponentComplete(true), size(-1), highestRole(Qt::UserRole)
|
---|
440 | , reply(0), status(QDeclarativeXmlListModel::Null), progress(0.0)
|
---|
441 | , queryId(-1), roleObjects(), redirectCount(0) {}
|
---|
442 |
|
---|
443 |
|
---|
444 | void notifyQueryStarted(bool remoteSource) {
|
---|
445 | Q_Q(QDeclarativeXmlListModel);
|
---|
446 | progress = remoteSource ? 0.0 : 1.0;
|
---|
447 | status = QDeclarativeXmlListModel::Loading;
|
---|
448 | errorString.clear();
|
---|
449 | emit q->progressChanged(progress);
|
---|
450 | emit q->statusChanged(status);
|
---|
451 | }
|
---|
452 |
|
---|
453 | bool isComponentComplete;
|
---|
454 | QUrl src;
|
---|
455 | QString xml;
|
---|
456 | QString query;
|
---|
457 | QString namespaces;
|
---|
458 | int size;
|
---|
459 | QList<int> roles;
|
---|
460 | QStringList roleNames;
|
---|
461 | int highestRole;
|
---|
462 | QNetworkReply *reply;
|
---|
463 | QDeclarativeXmlListModel::Status status;
|
---|
464 | QString errorString;
|
---|
465 | qreal progress;
|
---|
466 | int queryId;
|
---|
467 | QStringList keyRoleResultsCache;
|
---|
468 | QList<QDeclarativeXmlListModelRole *> roleObjects;
|
---|
469 | static void append_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, QDeclarativeXmlListModelRole *role);
|
---|
470 | static void clear_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list);
|
---|
471 | QList<QList<QVariant> > data;
|
---|
472 | int redirectCount;
|
---|
473 | };
|
---|
474 |
|
---|
475 |
|
---|
476 | void QDeclarativeXmlListModelPrivate::append_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list, QDeclarativeXmlListModelRole *role)
|
---|
477 | {
|
---|
478 | QDeclarativeXmlListModel *_this = qobject_cast<QDeclarativeXmlListModel *>(list->object);
|
---|
479 | if (_this && role) {
|
---|
480 | int i = _this->d_func()->roleObjects.count();
|
---|
481 | _this->d_func()->roleObjects.append(role);
|
---|
482 | if (_this->d_func()->roleNames.contains(role->name())) {
|
---|
483 | qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name());
|
---|
484 | return;
|
---|
485 | }
|
---|
486 | _this->d_func()->roles.insert(i, _this->d_func()->highestRole);
|
---|
487 | _this->d_func()->roleNames.insert(i, role->name());
|
---|
488 | ++_this->d_func()->highestRole;
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | //### clear needs to invalidate any cached data (in data table) as well
|
---|
493 | // (and the model should emit the appropriate signals)
|
---|
494 | void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListProperty<QDeclarativeXmlListModelRole> *list)
|
---|
495 | {
|
---|
496 | QDeclarativeXmlListModel *_this = static_cast<QDeclarativeXmlListModel *>(list->object);
|
---|
497 | _this->d_func()->roles.clear();
|
---|
498 | _this->d_func()->roleNames.clear();
|
---|
499 | _this->d_func()->roleObjects.clear();
|
---|
500 | }
|
---|
501 |
|
---|
502 | /*!
|
---|
503 | \qmlclass XmlListModel QDeclarativeXmlListModel
|
---|
504 | \ingroup qml-working-with-data
|
---|
505 | \since 4.7
|
---|
506 | \brief The XmlListModel element is used to specify a read-only model using XPath expressions.
|
---|
507 |
|
---|
508 | XmlListModel is used to create a read-only model from XML data. It can be used as a data source
|
---|
509 | for view elements (such as ListView, PathView, GridView) and other elements that interact with model
|
---|
510 | data (such as \l Repeater).
|
---|
511 |
|
---|
512 | For example, if there is a XML document at http://www.mysite.com/feed.xml like this:
|
---|
513 |
|
---|
514 | \code
|
---|
515 | <?xml version="1.0" encoding="utf-8"?>
|
---|
516 | <rss version="2.0">
|
---|
517 | ...
|
---|
518 | <channel>
|
---|
519 | <item>
|
---|
520 | <title>A blog post</title>
|
---|
521 | <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
|
---|
522 | </item>
|
---|
523 | <item>
|
---|
524 | <title>Another blog post</title>
|
---|
525 | <pubDate>Sat, 07 Sep 2010 15:35:01 GMT</pubDate>
|
---|
526 | </item>
|
---|
527 | </channel>
|
---|
528 | </rss>
|
---|
529 | \endcode
|
---|
530 |
|
---|
531 | A XmlListModel could create a model from this data, like this:
|
---|
532 |
|
---|
533 | \qml
|
---|
534 | import QtQuick 1.0
|
---|
535 |
|
---|
536 | XmlListModel {
|
---|
537 | id: xmlModel
|
---|
538 | source: "http://www.mysite.com/feed.xml"
|
---|
539 | query: "/rss/channel/item"
|
---|
540 |
|
---|
541 | XmlRole { name: "title"; query: "title/string()" }
|
---|
542 | XmlRole { name: "pubDate"; query: "pubDate/string()" }
|
---|
543 | }
|
---|
544 | \endqml
|
---|
545 |
|
---|
546 | The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate
|
---|
547 | a model item for each \c <item> in the XML document.
|
---|
548 |
|
---|
549 | The XmlRole objects define the
|
---|
550 | model item attributes. Here, each model item will have \c title and \c pubDate
|
---|
551 | attributes that match the \c title and \c pubDate values of its corresponding \c <item>.
|
---|
552 | (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.)
|
---|
553 |
|
---|
554 | The model could be used in a ListView, like this:
|
---|
555 |
|
---|
556 | \qml
|
---|
557 | ListView {
|
---|
558 | width: 180; height: 300
|
---|
559 | model: xmlModel
|
---|
560 | delegate: Text { text: title + ": " + pubDate }
|
---|
561 | }
|
---|
562 | \endqml
|
---|
563 |
|
---|
564 | \image qml-xmllistmodel-example.png
|
---|
565 |
|
---|
566 | The XmlListModel data is loaded asynchronously, and \l status
|
---|
567 | is set to \c XmlListModel.Ready when loading is complete.
|
---|
568 | Note this means when XmlListModel is used for a view, the view is not
|
---|
569 | populated until the model is loaded.
|
---|
570 |
|
---|
571 |
|
---|
572 | \section2 Using key XML roles
|
---|
573 |
|
---|
574 | You can define certain roles as "keys" so that when reload() is called,
|
---|
575 | the model will only add and refresh data that contains new values for
|
---|
576 | these keys.
|
---|
577 |
|
---|
578 | For example, if above role for "pubDate" was defined like this instead:
|
---|
579 |
|
---|
580 | \qml
|
---|
581 | XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true }
|
---|
582 | \endqml
|
---|
583 |
|
---|
584 | Then when reload() is called, the model will only add and reload
|
---|
585 | items with a "pubDate" value that is not already
|
---|
586 | present in the model.
|
---|
587 |
|
---|
588 | This is useful when displaying the contents of XML documents that
|
---|
589 | are incrementally updated (such as RSS feeds) to avoid repainting the
|
---|
590 | entire contents of a model in a view.
|
---|
591 |
|
---|
592 | If multiple key roles are specified, the model only adds and reload items
|
---|
593 | with a combined value of all key roles that is not already present in
|
---|
594 | the model.
|
---|
595 |
|
---|
596 | \sa {RSS News}
|
---|
597 | */
|
---|
598 |
|
---|
599 | QDeclarativeXmlListModel::QDeclarativeXmlListModel(QObject *parent)
|
---|
600 | : QListModelInterface(*(new QDeclarativeXmlListModelPrivate), parent)
|
---|
601 | {
|
---|
602 | connect(globalXmlQuery(), SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)),
|
---|
603 | this, SLOT(queryCompleted(QDeclarativeXmlQueryResult)));
|
---|
604 | connect(globalXmlQuery(), SIGNAL(error(void*,QString)),
|
---|
605 | this, SLOT(queryError(void*,QString)));
|
---|
606 | }
|
---|
607 |
|
---|
608 | QDeclarativeXmlListModel::~QDeclarativeXmlListModel()
|
---|
609 | {
|
---|
610 | }
|
---|
611 |
|
---|
612 | /*!
|
---|
613 | \qmlproperty list<XmlRole> XmlListModel::roles
|
---|
614 |
|
---|
615 | The roles to make available for this model.
|
---|
616 | */
|
---|
617 | QDeclarativeListProperty<QDeclarativeXmlListModelRole> QDeclarativeXmlListModel::roleObjects()
|
---|
618 | {
|
---|
619 | Q_D(QDeclarativeXmlListModel);
|
---|
620 | QDeclarativeListProperty<QDeclarativeXmlListModelRole> list(this, d->roleObjects);
|
---|
621 | list.append = &QDeclarativeXmlListModelPrivate::append_role;
|
---|
622 | list.clear = &QDeclarativeXmlListModelPrivate::clear_role;
|
---|
623 | return list;
|
---|
624 | }
|
---|
625 |
|
---|
626 | QHash<int,QVariant> QDeclarativeXmlListModel::data(int index, const QList<int> &roles) const
|
---|
627 | {
|
---|
628 | Q_D(const QDeclarativeXmlListModel);
|
---|
629 | QHash<int, QVariant> rv;
|
---|
630 | for (int i = 0; i < roles.size(); ++i) {
|
---|
631 | int role = roles.at(i);
|
---|
632 | int roleIndex = d->roles.indexOf(role);
|
---|
633 | rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index));
|
---|
634 | }
|
---|
635 | return rv;
|
---|
636 | }
|
---|
637 |
|
---|
638 | QVariant QDeclarativeXmlListModel::data(int index, int role) const
|
---|
639 | {
|
---|
640 | Q_D(const QDeclarativeXmlListModel);
|
---|
641 | int roleIndex = d->roles.indexOf(role);
|
---|
642 | return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index);
|
---|
643 | }
|
---|
644 |
|
---|
645 | /*!
|
---|
646 | \qmlproperty int XmlListModel::count
|
---|
647 | The number of data entries in the model.
|
---|
648 | */
|
---|
649 | int QDeclarativeXmlListModel::count() const
|
---|
650 | {
|
---|
651 | Q_D(const QDeclarativeXmlListModel);
|
---|
652 | return d->size;
|
---|
653 | }
|
---|
654 |
|
---|
655 | QList<int> QDeclarativeXmlListModel::roles() const
|
---|
656 | {
|
---|
657 | Q_D(const QDeclarativeXmlListModel);
|
---|
658 | return d->roles;
|
---|
659 | }
|
---|
660 |
|
---|
661 | QString QDeclarativeXmlListModel::toString(int role) const
|
---|
662 | {
|
---|
663 | Q_D(const QDeclarativeXmlListModel);
|
---|
664 | int index = d->roles.indexOf(role);
|
---|
665 | if (index == -1)
|
---|
666 | return QString();
|
---|
667 | return d->roleNames.at(index);
|
---|
668 | }
|
---|
669 |
|
---|
670 | /*!
|
---|
671 | \qmlproperty url XmlListModel::source
|
---|
672 | The location of the XML data source.
|
---|
673 |
|
---|
674 | If both \c source and \l xml are set, \l xml is used.
|
---|
675 | */
|
---|
676 | QUrl QDeclarativeXmlListModel::source() const
|
---|
677 | {
|
---|
678 | Q_D(const QDeclarativeXmlListModel);
|
---|
679 | return d->src;
|
---|
680 | }
|
---|
681 |
|
---|
682 | void QDeclarativeXmlListModel::setSource(const QUrl &src)
|
---|
683 | {
|
---|
684 | Q_D(QDeclarativeXmlListModel);
|
---|
685 | if (d->src != src) {
|
---|
686 | d->src = src;
|
---|
687 | if (d->xml.isEmpty()) // src is only used if d->xml is not set
|
---|
688 | reload();
|
---|
689 | emit sourceChanged();
|
---|
690 | }
|
---|
691 | }
|
---|
692 |
|
---|
693 | /*!
|
---|
694 | \qmlproperty string XmlListModel::xml
|
---|
695 | This property holds the XML data for this model, if set.
|
---|
696 |
|
---|
697 | The text is assumed to be UTF-8 encoded.
|
---|
698 |
|
---|
699 | If both \l source and \c xml are set, \c xml is used.
|
---|
700 | */
|
---|
701 | QString QDeclarativeXmlListModel::xml() const
|
---|
702 | {
|
---|
703 | Q_D(const QDeclarativeXmlListModel);
|
---|
704 | return d->xml;
|
---|
705 | }
|
---|
706 |
|
---|
707 | void QDeclarativeXmlListModel::setXml(const QString &xml)
|
---|
708 | {
|
---|
709 | Q_D(QDeclarativeXmlListModel);
|
---|
710 | if (d->xml != xml) {
|
---|
711 | d->xml = xml;
|
---|
712 | reload();
|
---|
713 | emit xmlChanged();
|
---|
714 | }
|
---|
715 | }
|
---|
716 |
|
---|
717 | /*!
|
---|
718 | \qmlproperty string XmlListModel::query
|
---|
719 | An absolute XPath query representing the base query for creating model items
|
---|
720 | from this model's XmlRole objects. The query should start with '/' or '//'.
|
---|
721 | */
|
---|
722 | QString QDeclarativeXmlListModel::query() const
|
---|
723 | {
|
---|
724 | Q_D(const QDeclarativeXmlListModel);
|
---|
725 | return d->query;
|
---|
726 | }
|
---|
727 |
|
---|
728 | void QDeclarativeXmlListModel::setQuery(const QString &query)
|
---|
729 | {
|
---|
730 | Q_D(QDeclarativeXmlListModel);
|
---|
731 | if (!query.startsWith(QLatin1Char('/'))) {
|
---|
732 | qmlInfo(this) << QCoreApplication::translate("QDeclarativeXmlRoleList", "An XmlListModel query must start with '/' or \"//\"");
|
---|
733 | return;
|
---|
734 | }
|
---|
735 |
|
---|
736 | if (d->query != query) {
|
---|
737 | d->query = query;
|
---|
738 | reload();
|
---|
739 | emit queryChanged();
|
---|
740 | }
|
---|
741 | }
|
---|
742 |
|
---|
743 | /*!
|
---|
744 | \qmlproperty string XmlListModel::namespaceDeclarations
|
---|
745 | The namespace declarations to be used in the XPath queries.
|
---|
746 |
|
---|
747 | The namespaces should be declared as in XQuery. For example, if a requested document
|
---|
748 | at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom",
|
---|
749 | this can be declared as the default namespace:
|
---|
750 |
|
---|
751 | \qml
|
---|
752 | XmlListModel {
|
---|
753 | source: "http://mysite.com/feed.xml"
|
---|
754 | query: "/feed/entry"
|
---|
755 | namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';"
|
---|
756 |
|
---|
757 | XmlRole { name: "title"; query: "title/string()" }
|
---|
758 | }
|
---|
759 | \endqml
|
---|
760 | */
|
---|
761 | QString QDeclarativeXmlListModel::namespaceDeclarations() const
|
---|
762 | {
|
---|
763 | Q_D(const QDeclarativeXmlListModel);
|
---|
764 | return d->namespaces;
|
---|
765 | }
|
---|
766 |
|
---|
767 | void QDeclarativeXmlListModel::setNamespaceDeclarations(const QString &declarations)
|
---|
768 | {
|
---|
769 | Q_D(QDeclarativeXmlListModel);
|
---|
770 | if (d->namespaces != declarations) {
|
---|
771 | d->namespaces = declarations;
|
---|
772 | reload();
|
---|
773 | emit namespaceDeclarationsChanged();
|
---|
774 | }
|
---|
775 | }
|
---|
776 |
|
---|
777 | /*!
|
---|
778 | \qmlmethod object XmlListModel::get(int index)
|
---|
779 |
|
---|
780 | Returns the item at \a index in the model.
|
---|
781 |
|
---|
782 | For example, for a model like this:
|
---|
783 |
|
---|
784 | \qml
|
---|
785 | XmlListModel {
|
---|
786 | id: model
|
---|
787 | source: "http://mysite.com/feed.xml"
|
---|
788 | query: "/feed/entry"
|
---|
789 | XmlRole { name: "title"; query: "title/string()" }
|
---|
790 | }
|
---|
791 | \endqml
|
---|
792 |
|
---|
793 | This will access the \c title value for the first item in the model:
|
---|
794 |
|
---|
795 | \qml
|
---|
796 | var title = model.get(0).title;
|
---|
797 | \endqml
|
---|
798 | */
|
---|
799 | QScriptValue QDeclarativeXmlListModel::get(int index) const
|
---|
800 | {
|
---|
801 | Q_D(const QDeclarativeXmlListModel);
|
---|
802 |
|
---|
803 | QScriptEngine *sengine = QDeclarativeEnginePrivate::getScriptEngine(qmlContext(this)->engine());
|
---|
804 | if (index < 0 || index >= count())
|
---|
805 | return sengine->undefinedValue();
|
---|
806 |
|
---|
807 | QScriptValue sv = sengine->newObject();
|
---|
808 | for (int i=0; i<d->roleObjects.count(); i++)
|
---|
809 | sv.setProperty(d->roleObjects[i]->name(), qScriptValueFromValue(sengine, d->data.value(i).value(index)));
|
---|
810 | return sv;
|
---|
811 | }
|
---|
812 |
|
---|
813 | /*!
|
---|
814 | \qmlproperty enumeration XmlListModel::status
|
---|
815 | Specifies the model loading status, which can be one of the following:
|
---|
816 |
|
---|
817 | \list
|
---|
818 | \o XmlListModel.Null - No XML data has been set for this model.
|
---|
819 | \o XmlListModel.Ready - The XML data has been loaded into the model.
|
---|
820 | \o XmlListModel.Loading - The model is in the process of reading and loading XML data.
|
---|
821 | \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details
|
---|
822 | about the error.
|
---|
823 | \endlist
|
---|
824 |
|
---|
825 | \sa progress
|
---|
826 |
|
---|
827 | */
|
---|
828 | QDeclarativeXmlListModel::Status QDeclarativeXmlListModel::status() const
|
---|
829 | {
|
---|
830 | Q_D(const QDeclarativeXmlListModel);
|
---|
831 | return d->status;
|
---|
832 | }
|
---|
833 |
|
---|
834 | /*!
|
---|
835 | \qmlproperty real XmlListModel::progress
|
---|
836 |
|
---|
837 | This indicates the current progress of the downloading of the XML data
|
---|
838 | source. This value ranges from 0.0 (no data downloaded) to
|
---|
839 | 1.0 (all data downloaded). If the XML data is not from a remote source,
|
---|
840 | the progress becomes 1.0 as soon as the data is read.
|
---|
841 |
|
---|
842 | Note that when the progress is 1.0, the XML data has been downloaded, but
|
---|
843 | it is yet to be loaded into the model at this point. Use the status
|
---|
844 | property to find out when the XML data has been read and loaded into
|
---|
845 | the model.
|
---|
846 |
|
---|
847 | \sa status, source
|
---|
848 | */
|
---|
849 | qreal QDeclarativeXmlListModel::progress() const
|
---|
850 | {
|
---|
851 | Q_D(const QDeclarativeXmlListModel);
|
---|
852 | return d->progress;
|
---|
853 | }
|
---|
854 |
|
---|
855 | /*!
|
---|
856 | \qmlmethod void XmlListModel::errorString()
|
---|
857 |
|
---|
858 | Returns a string description of the last error that occurred
|
---|
859 | if \l status is XmlListModel::Error.
|
---|
860 | */
|
---|
861 | QString QDeclarativeXmlListModel::errorString() const
|
---|
862 | {
|
---|
863 | Q_D(const QDeclarativeXmlListModel);
|
---|
864 | return d->errorString;
|
---|
865 | }
|
---|
866 |
|
---|
867 | void QDeclarativeXmlListModel::classBegin()
|
---|
868 | {
|
---|
869 | Q_D(QDeclarativeXmlListModel);
|
---|
870 | d->isComponentComplete = false;
|
---|
871 | }
|
---|
872 |
|
---|
873 | void QDeclarativeXmlListModel::componentComplete()
|
---|
874 | {
|
---|
875 | Q_D(QDeclarativeXmlListModel);
|
---|
876 | d->isComponentComplete = true;
|
---|
877 | reload();
|
---|
878 | }
|
---|
879 |
|
---|
880 | /*!
|
---|
881 | \qmlmethod XmlListModel::reload()
|
---|
882 |
|
---|
883 | Reloads the model.
|
---|
884 |
|
---|
885 | If no key roles have been specified, all existing model
|
---|
886 | data is removed, and the model is rebuilt from scratch.
|
---|
887 |
|
---|
888 | Otherwise, items are only added if the model does not already
|
---|
889 | contain items with matching key role values.
|
---|
890 |
|
---|
891 | \sa {Using key XML roles}, XmlRole::isKey
|
---|
892 | */
|
---|
893 | void QDeclarativeXmlListModel::reload()
|
---|
894 | {
|
---|
895 | Q_D(QDeclarativeXmlListModel);
|
---|
896 |
|
---|
897 | if (!d->isComponentComplete)
|
---|
898 | return;
|
---|
899 |
|
---|
900 | globalXmlQuery()->abort(d->queryId);
|
---|
901 | d->queryId = -1;
|
---|
902 |
|
---|
903 | if (d->size < 0)
|
---|
904 | d->size = 0;
|
---|
905 |
|
---|
906 | if (d->reply) {
|
---|
907 | d->reply->abort();
|
---|
908 | if (d->reply) {
|
---|
909 | // abort will generally have already done this (and more)
|
---|
910 | d->reply->deleteLater();
|
---|
911 | d->reply = 0;
|
---|
912 | }
|
---|
913 | }
|
---|
914 |
|
---|
915 | if (!d->xml.isEmpty()) {
|
---|
916 | d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache);
|
---|
917 | d->notifyQueryStarted(false);
|
---|
918 |
|
---|
919 | } else if (d->src.isEmpty()) {
|
---|
920 | d->queryId = XMLLISTMODEL_CLEAR_ID;
|
---|
921 | d->notifyQueryStarted(false);
|
---|
922 | QTimer::singleShot(0, this, SLOT(dataCleared()));
|
---|
923 |
|
---|
924 | } else {
|
---|
925 | d->notifyQueryStarted(true);
|
---|
926 | QNetworkRequest req(d->src);
|
---|
927 | req.setRawHeader("Accept", "application/xml");
|
---|
928 | d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req);
|
---|
929 | QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished()));
|
---|
930 | QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
|
---|
931 | this, SLOT(requestProgress(qint64,qint64)));
|
---|
932 | }
|
---|
933 | }
|
---|
934 |
|
---|
935 | #define XMLLISTMODEL_MAX_REDIRECT 16
|
---|
936 |
|
---|
937 | void QDeclarativeXmlListModel::requestFinished()
|
---|
938 | {
|
---|
939 | Q_D(QDeclarativeXmlListModel);
|
---|
940 |
|
---|
941 | d->redirectCount++;
|
---|
942 | if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) {
|
---|
943 | QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
---|
944 | if (redirect.isValid()) {
|
---|
945 | QUrl url = d->reply->url().resolved(redirect.toUrl());
|
---|
946 | d->reply->deleteLater();
|
---|
947 | d->reply = 0;
|
---|
948 | setSource(url);
|
---|
949 | return;
|
---|
950 | }
|
---|
951 | }
|
---|
952 | d->redirectCount = 0;
|
---|
953 |
|
---|
954 | if (d->reply->error() != QNetworkReply::NoError) {
|
---|
955 | d->errorString = d->reply->errorString();
|
---|
956 | disconnect(d->reply, 0, this, 0);
|
---|
957 | d->reply->deleteLater();
|
---|
958 | d->reply = 0;
|
---|
959 |
|
---|
960 | int count = this->count();
|
---|
961 | d->data.clear();
|
---|
962 | d->size = 0;
|
---|
963 | if (count > 0) {
|
---|
964 | emit itemsRemoved(0, count);
|
---|
965 | emit countChanged();
|
---|
966 | }
|
---|
967 |
|
---|
968 | d->status = Error;
|
---|
969 | d->queryId = -1;
|
---|
970 | emit statusChanged(d->status);
|
---|
971 | } else {
|
---|
972 | QByteArray data = d->reply->readAll();
|
---|
973 | if (data.isEmpty()) {
|
---|
974 | d->queryId = XMLLISTMODEL_CLEAR_ID;
|
---|
975 | QTimer::singleShot(0, this, SLOT(dataCleared()));
|
---|
976 | } else {
|
---|
977 | d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
|
---|
978 | }
|
---|
979 | disconnect(d->reply, 0, this, 0);
|
---|
980 | d->reply->deleteLater();
|
---|
981 | d->reply = 0;
|
---|
982 |
|
---|
983 | d->progress = 1.0;
|
---|
984 | emit progressChanged(d->progress);
|
---|
985 | }
|
---|
986 | }
|
---|
987 |
|
---|
988 | void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total)
|
---|
989 | {
|
---|
990 | Q_D(QDeclarativeXmlListModel);
|
---|
991 | if (d->status == Loading && total > 0) {
|
---|
992 | d->progress = qreal(received)/total;
|
---|
993 | emit progressChanged(d->progress);
|
---|
994 | }
|
---|
995 | }
|
---|
996 |
|
---|
997 | void QDeclarativeXmlListModel::dataCleared()
|
---|
998 | {
|
---|
999 | Q_D(QDeclarativeXmlListModel);
|
---|
1000 | QDeclarativeXmlQueryResult r;
|
---|
1001 | r.queryId = XMLLISTMODEL_CLEAR_ID;
|
---|
1002 | r.size = 0;
|
---|
1003 | r.removed << qMakePair(0, count());
|
---|
1004 | r.keyRoleResultsCache = d->keyRoleResultsCache;
|
---|
1005 | queryCompleted(r);
|
---|
1006 | }
|
---|
1007 |
|
---|
1008 | void QDeclarativeXmlListModel::queryError(void* object, const QString& error)
|
---|
1009 | {
|
---|
1010 | // Be extra careful, object may no longer exist, it's just an ID.
|
---|
1011 | Q_D(QDeclarativeXmlListModel);
|
---|
1012 | for (int i=0; i<d->roleObjects.count(); i++) {
|
---|
1013 | if (d->roleObjects.at(i) == static_cast<QDeclarativeXmlListModelRole*>(object)) {
|
---|
1014 | qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error);
|
---|
1015 | return;
|
---|
1016 | }
|
---|
1017 | }
|
---|
1018 | qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error);
|
---|
1019 | }
|
---|
1020 |
|
---|
1021 | void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result)
|
---|
1022 | {
|
---|
1023 | Q_D(QDeclarativeXmlListModel);
|
---|
1024 | if (result.queryId != d->queryId)
|
---|
1025 | return;
|
---|
1026 |
|
---|
1027 | int origCount = d->size;
|
---|
1028 | bool sizeChanged = result.size != d->size;
|
---|
1029 |
|
---|
1030 | d->size = result.size;
|
---|
1031 | d->data = result.data;
|
---|
1032 | d->keyRoleResultsCache = result.keyRoleResultsCache;
|
---|
1033 | d->status = Ready;
|
---|
1034 | d->errorString.clear();
|
---|
1035 | d->queryId = -1;
|
---|
1036 |
|
---|
1037 | bool hasKeys = false;
|
---|
1038 | for (int i=0; i<d->roleObjects.count(); i++) {
|
---|
1039 | if (d->roleObjects[i]->isKey()) {
|
---|
1040 | hasKeys = true;
|
---|
1041 | break;
|
---|
1042 | }
|
---|
1043 | }
|
---|
1044 | if (!hasKeys) {
|
---|
1045 | if (!(origCount == 0 && d->size == 0)) {
|
---|
1046 | emit itemsRemoved(0, origCount);
|
---|
1047 | emit itemsInserted(0, d->size);
|
---|
1048 | emit countChanged();
|
---|
1049 | }
|
---|
1050 |
|
---|
1051 | } else {
|
---|
1052 | for (int i=0; i<result.removed.count(); i++)
|
---|
1053 | emit itemsRemoved(result.removed[i].first, result.removed[i].second);
|
---|
1054 | for (int i=0; i<result.inserted.count(); i++)
|
---|
1055 | emit itemsInserted(result.inserted[i].first, result.inserted[i].second);
|
---|
1056 |
|
---|
1057 | if (sizeChanged)
|
---|
1058 | emit countChanged();
|
---|
1059 | }
|
---|
1060 |
|
---|
1061 | emit statusChanged(d->status);
|
---|
1062 | }
|
---|
1063 |
|
---|
1064 | QT_END_NAMESPACE
|
---|
1065 |
|
---|
1066 | #include <qdeclarativexmllistmodel.moc>
|
---|