diff options
-rw-r--r-- | demos/declarative/flickr/flickr.qml | 22 | ||||
-rw-r--r-- | demos/declarative/flickr/flickr2.qml | 22 | ||||
-rw-r--r-- | doc/src/declarative/elements.qdoc | 2 | ||||
-rw-r--r-- | examples/declarative/flowview/flickr.qml | 22 | ||||
-rw-r--r-- | examples/declarative/xmldata/daringfireball.qml | 8 | ||||
-rw-r--r-- | examples/declarative/xmldata/yahoonews.qml | 8 | ||||
-rw-r--r-- | src/declarative/extra/qmlxmllistmodel.cpp | 115 | ||||
-rw-r--r-- | src/declarative/extra/qmlxmllistmodel.h | 15 |
8 files changed, 162 insertions, 52 deletions
diff --git a/demos/declarative/flickr/flickr.qml b/demos/declarative/flickr/flickr.qml index 3ce442e..edf1095 100644 --- a/demos/declarative/flickr/flickr.qml +++ b/demos/declarative/flickr/flickr.qml @@ -10,19 +10,19 @@ Item { id: FeedModel property string tags : TagsEdit.text source: "http://api.flickr.com/services/feeds/photos_public.gne?"+(tags ? "tags="+tags+"&" : "")+"format=rss2" - query: "doc($src)/rss/channel/item" + query: "/rss/channel/item" namespaceDeclarations: "declare namespace media=\"http://search.yahoo.com/mrss/\";" - Role { name: "title"; query: "title/string()" } - Role { name: "imagePath"; query: "media:thumbnail/@url/string()" } - Role { name: "url"; query: "media:content/@url/string()" } - Role { name: "description"; query: "description/string()" } - Role { name: "tags"; query: "media:category/string()" } - Role { name: "photoWidth"; query: "media:content/@width/string()" } - Role { name: "photoHeight"; query: "media:content/@height/string()" } - Role { name: "photoType"; query: "media:content/@type/string()" } - Role { name: "photoAuthor"; query: "author/string()" } - Role { name: "photoDate"; query: "pubDate/string()" } + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "imagePath"; query: "media:thumbnail/@url/string()" } + XmlRole { name: "url"; query: "media:content/@url/string()" } + XmlRole { name: "description"; query: "description/string()" } + XmlRole { name: "tags"; query: "media:category/string()" } + XmlRole { name: "photoWidth"; query: "media:content/@width/string()" } + XmlRole { name: "photoHeight"; query: "media:content/@height/string()" } + XmlRole { name: "photoType"; query: "media:content/@type/string()" } + XmlRole { name: "photoAuthor"; query: "author/string()" } + XmlRole { name: "photoDate"; query: "pubDate/string()" } }, Component { diff --git a/demos/declarative/flickr/flickr2.qml b/demos/declarative/flickr/flickr2.qml index 947bf9e..d6d5c95 100644 --- a/demos/declarative/flickr/flickr2.qml +++ b/demos/declarative/flickr/flickr2.qml @@ -12,19 +12,19 @@ Item { id: FeedModel property string tags : TagsEdit.text source: "http://api.flickr.com/services/feeds/photos_public.gne?"+(tags ? "tags="+tags+"&" : "")+"format=rss2" - query: "doc($src)/rss/channel/item" + query: "/rss/channel/item" namespaceDeclarations: "declare namespace media=\"http://search.yahoo.com/mrss/\";" - Role { name: "title"; query: "title/string()" } - Role { name: "imagePath"; query: "media:thumbnail/@url/string()" } - Role { name: "url"; query: "media:content/@url/string()" } - Role { name: "description"; query: "description/string()" } - Role { name: "tags"; query: "media:category/string()" } - Role { name: "photoWidth"; query: "media:content/@width/string()" } - Role { name: "photoHeight"; query: "media:content/@height/string()" } - Role { name: "photoType"; query: "media:content/@type/string()" } - Role { name: "photoAuthor"; query: "author/string()" } - Role { name: "photoDate"; query: "pubDate/string()" } + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "imagePath"; query: "media:thumbnail/@url/string()" } + XmlRole { name: "url"; query: "media:content/@url/string()" } + XmlRole { name: "description"; query: "description/string()" } + XmlRole { name: "tags"; query: "media:category/string()" } + XmlRole { name: "photoWidth"; query: "media:content/@width/string()" } + XmlRole { name: "photoHeight"; query: "media:content/@height/string()" } + XmlRole { name: "photoType"; query: "media:content/@type/string()" } + XmlRole { name: "photoAuthor"; query: "author/string()" } + XmlRole { name: "photoDate"; query: "pubDate/string()" } } delegate: Package { diff --git a/doc/src/declarative/elements.qdoc b/doc/src/declarative/elements.qdoc index 9edb087..63566f6 100644 --- a/doc/src/declarative/elements.qdoc +++ b/doc/src/declarative/elements.qdoc @@ -42,7 +42,7 @@ The following table lists the Qml elements provided by the Qt Declarative module \list \o \l Bind \o \l ListModel -\o \l XmlListModel +\o \l XmlListModel and XmlRole \o \l SqlQuery, \l SqlConnection, and \l SqlBind \o \l DateTimeFormatter \o \l NumberFormatter diff --git a/examples/declarative/flowview/flickr.qml b/examples/declarative/flowview/flickr.qml index fa8233b..2742839 100644 --- a/examples/declarative/flowview/flickr.qml +++ b/examples/declarative/flowview/flickr.qml @@ -8,19 +8,19 @@ Rect { XmlListModel { id: FeedModel source: "http://api.flickr.com/services/feeds/photos_public.gne?format=rss2" - query: "doc($src)/rss/channel/item" + query: "/rss/channel/item" namespaceDeclarations: "declare namespace media=\"http://search.yahoo.com/mrss/\";" - Role { name: "title"; query: "title/string()" } - Role { name: "imagePath"; query: "media:thumbnail/@url/string()" } - Role { name: "url"; query: "media:content/@url/string()" } - Role { name: "description"; query: "description/string()" } - Role { name: "tags"; query: "media:category/string()" } - Role { name: "photoWidth"; query: "media:content/@width/string()" } - Role { name: "photoHeight"; query: "media:content/@height/string()" } - Role { name: "photoType"; query: "media:content/@type/string()" } - Role { name: "photoAuthor"; query: "author/string()" } - Role { name: "photoDate"; query: "pubDate/string()" } + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "imagePath"; query: "media:thumbnail/@url/string()" } + XmlRole { name: "url"; query: "media:content/@url/string()" } + XmlRole { name: "description"; query: "description/string()" } + XmlRole { name: "tags"; query: "media:category/string()" } + XmlRole { name: "photoWidth"; query: "media:content/@width/string()" } + XmlRole { name: "photoHeight"; query: "media:content/@height/string()" } + XmlRole { name: "photoType"; query: "media:content/@type/string()" } + XmlRole { name: "photoAuthor"; query: "author/string()" } + XmlRole { name: "photoDate"; query: "pubDate/string()" } } ListView { diff --git a/examples/declarative/xmldata/daringfireball.qml b/examples/declarative/xmldata/daringfireball.qml index b14dfbf..6f3461e 100644 --- a/examples/declarative/xmldata/daringfireball.qml +++ b/examples/declarative/xmldata/daringfireball.qml @@ -6,17 +6,17 @@ Rect { XmlListModel { id: feedModel source: "http://daringfireball.net/index.xml" - query: "doc($src)/feed/entry" + query: "/feed/entry" namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';" - Role { + XmlRole { name: "title" query: "title/string()" } - Role { + XmlRole { name: "tagline" query: "author/name/string()" } - Role { + XmlRole { name: "content" query: "content/string()" } diff --git a/examples/declarative/xmldata/yahoonews.qml b/examples/declarative/xmldata/yahoonews.qml index d20da99..9d0c956 100644 --- a/examples/declarative/xmldata/yahoonews.qml +++ b/examples/declarative/xmldata/yahoonews.qml @@ -9,16 +9,16 @@ Rect { XmlListModel { id: feedModel source: "http://rss.news.yahoo.com/rss/oceania" - query: "doc($src)/rss/channel/item" - Role { + query: "/rss/channel/item" + XmlRole { name: "title" query: "title/string()" } - Role { + XmlRole { name: "link" query: "link/string()" } - Role { + XmlRole { name: "description" query: "description/string()" } diff --git a/src/declarative/extra/qmlxmllistmodel.cpp b/src/declarative/extra/qmlxmllistmodel.cpp index 51905fa..082c9c2 100644 --- a/src/declarative/extra/qmlxmllistmodel.cpp +++ b/src/declarative/extra/qmlxmllistmodel.cpp @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -QML_DEFINE_TYPE(XmlListModelRole, Role) +QML_DEFINE_TYPE(XmlListModelRole, XmlRole) QML_DEFINE_TYPE(QmlXmlListModel, XmlListModel) @@ -105,7 +105,7 @@ public: m_modelData.clear(); m_size = 0; m_data = data; - m_query = query; + m_query = QLatin1String("doc($src)") + query; m_namespaces = namespaces; m_roleObjects = roleObjects; if (!isRunning()) { @@ -224,6 +224,13 @@ void QmlXmlQuery::doSubQueryJob() //### we might be able to condense even further (query for everything in one go) for (int i = 0; i < m_roleObjects->size(); ++i) { XmlListModelRole *role = m_roleObjects->at(i); + if (!role->isValid()) { + QList<QVariant> resultList; + for (int j = 0; j < m_size; ++j) + resultList << QVariant(); + m_modelData << resultList; + continue; + } subquery.setQuery(m_prefix + QLatin1String("(let $v := ") + role->query() + QLatin1String(" return if ($v) then ") + role->query() + QLatin1String(" else \"\")")); QXmlResultItems output3; subquery.evaluateTo(&output3); @@ -233,6 +240,9 @@ void QmlXmlQuery::doSubQueryJob() resultList << item.toAtomicValue(); //### we used to trim strings item = output3.next(); } + //### should warn here if things have gone wrong. + while (resultList.count() < m_size) + resultList << QVariant(); m_modelData << resultList; b.seek(0); } @@ -308,6 +318,38 @@ void QmlXmlRoleList::append(XmlListModelRole *role) { model->roleNames << role->name(); ++model->highestRole; } + +/*! + \qmlclass XmlRole + \brief The XmlRole element allows you to specify a role for an XmlListModel. +*/ + +/*! + \qmlproperty string XmlRole::name + The name for the role. This name is used to access the model data for this role from Qml. + + \qml + XmlRole { name: "title"; query: "title/string()" } + + ... + + Component { + id: Delegate + Text { text: title } + } + \endqml +*/ + +/*! + \qmlproperty string XmlRole::query + The relative XPath query for this role. The query should not start with a '/' (i.e. it must be + relative). + + \qml + XmlRole { name: "title"; query: "title/string()" } + \endqml +*/ + //XXX clear, removeAt, and insert need to invalidate any cached data (in data table) as well // (and the model should emit the appropriate signals) void QmlXmlRoleList::clear() @@ -316,12 +358,15 @@ void QmlXmlRoleList::clear() model->roleNames.clear(); QmlConcreteList<XmlListModelRole *>::clear(); } + void QmlXmlRoleList::removeAt(int i) { model->roles.removeAt(i); model->roleNames.removeAt(i); QmlConcreteList<XmlListModelRole *>::removeAt(i); } + +//### we should enforce unique role names void QmlXmlRoleList::insert(int i, XmlListModelRole *role) { QmlConcreteList<XmlListModelRole *>::insert(i, role); @@ -332,7 +377,7 @@ void QmlXmlRoleList::insert(int i, XmlListModelRole *role) /*! \qmlclass XmlListModel - \brief The XmlListModel class allows you to specify a model using XQuery. + \brief The XmlListModel element allows you to specify a model using XPath expressions. XmlListModel allows you to construct a model from XML data that can then be used as a data source for the view classes (ListView, PathView, GridView) and any other classes that interact with model @@ -343,13 +388,14 @@ void QmlXmlRoleList::insert(int i, XmlListModelRole *role) XmlListModel { id: FeedModel source: "http://rss.news.yahoo.com/rss/oceania" - query: "doc($src)/rss/channel/item" - Role { name: "title"; query: "title/string()" } - Role { name: "link"; query: "link/string()" } - Role { name: "description"; query: "description/string()" } + query: "/rss/channel/item" + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "link"; query: "link/string()" } + XmlRole { name: "description"; query: "description/string()" } } \endqml - \note The model is currently static, so the above is really just a snapshot of an RSS feed. + \note The model is currently static, so the above is really just a snapshot of an RSS feed. To force a + reload of the entire model, you can call the reload function. */ QmlXmlListModel::QmlXmlListModel(QObject *parent) @@ -364,6 +410,11 @@ QmlXmlListModel::~QmlXmlListModel() { } +/*! + \qmlproperty list<XmlRole> QmlListModel::roles + + The roles to make available for this model. +*/ QmlList<XmlListModelRole *> *QmlXmlListModel::roleObjects() { Q_D(QmlXmlListModel); @@ -403,6 +454,10 @@ QString QmlXmlListModel::toString(int role) const return d->roleNames.at(index); } +/*! + \qmlproperty url XmlListModel::source + The location of the XML data source. +*/ QUrl QmlXmlListModel::source() const { Q_D(const QmlXmlListModel); @@ -418,6 +473,11 @@ void QmlXmlListModel::setSource(const QUrl &src) } } +/*! + \qmlproperty url XmlListModel::query + An absolute XPath query representing the base query for the model items. The query should start with + a '/' or '//'. +*/ QString QmlXmlListModel::query() const { Q_D(const QmlXmlListModel); @@ -427,12 +487,21 @@ QString QmlXmlListModel::query() const void QmlXmlListModel::setQuery(const QString &query) { Q_D(QmlXmlListModel); + if (!query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << "An XmlListModel query must start with '/' or \"//\""; + return; + } + if (d->query != query) { d->query = query; reload(); } } +/*! + \qmlproperty string XmlListModel::namespaceDeclarations + A set of declarations for the namespaces used in the query. +*/ QString QmlXmlListModel::namespaceDeclarations() const { Q_D(const QmlXmlListModel); @@ -447,12 +516,34 @@ void QmlXmlListModel::setNamespaceDeclarations(const QString &declarations) reload(); } } + +/*! + \qmlproperty enum XmlListModel::status + + This property holds the status of data source loading. It can be one of: + \list + \o Idle - no data source has been set, or the data source has been loaded + \o Loading - the data source is currently being loaded + \o Error - an error occurred while loading the data source + \endlist + + \sa progress + +*/ QmlXmlListModel::Status QmlXmlListModel::status() const { Q_D(const QmlXmlListModel); return d->status; } +/*! + \qmlproperty real XmlListModel::progress + + This property holds the progress of data source loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ qreal QmlXmlListModel::progress() const { Q_D(const QmlXmlListModel); @@ -466,6 +557,12 @@ void QmlXmlListModel::classComplete() reload(); } +/*! + \qmlmethod XmlListModel::reload() + + Reloads the model. All the existing model data will be removed, and the model + will be rebuilt from scratch. +*/ void QmlXmlListModel::reload() { Q_D(QmlXmlListModel); @@ -484,7 +581,7 @@ void QmlXmlListModel::reload() emit itemsRemoved(0, count); if (d->src.isEmpty()) { - qWarning() << "Can't load empty src string"; + qmlInfo(this) << "Can't load empty src string"; return; } diff --git a/src/declarative/extra/qmlxmllistmodel.h b/src/declarative/extra/qmlxmllistmodel.h index c6aae4a..0d41456 100644 --- a/src/declarative/extra/qmlxmllistmodel.h +++ b/src/declarative/extra/qmlxmllistmodel.h @@ -44,6 +44,7 @@ #include <QtDeclarative/qml.h> #include <QtDeclarative/QListModelInterface> +#include <QtDeclarative/qmlinfo.h> QT_BEGIN_HEADER @@ -66,7 +67,18 @@ public: void setName(const QString &name) { m_name = name; } QString query() const { return m_query; } - void setQuery(const QString &query) { m_query = query; } + void setQuery(const QString &query) + { + if (query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << "An XmlRole query must not start with '/'"; + return; + } + m_query = query; + } + + bool isValid() { + return !m_name.isEmpty() && !m_query.isEmpty(); + } private: QString m_name; @@ -88,6 +100,7 @@ class Q_DECLARATIVE_EXPORT QmlXmlListModel : public QListModelInterface, public Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations) Q_PROPERTY(QmlList<XmlListModelRole *> *roles READ roleObjects) Q_CLASSINFO("DefaultProperty", "roles") + public: QmlXmlListModel(QObject *parent = 0); ~QmlXmlListModel(); |