1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51/*
52rsslisting.cpp
53
54Provides a widget for displaying news items from RDF news sources.
55RDF is an XML-based format for storing items of information (see
56http://www.w3.org/RDF/ for details).
57
58The widget itself provides a simple user interface for specifying
59the URL of a news source, and controlling the downloading of news.
60
61The widget downloads and parses the XML asynchronously, feeding the
62data to an XML reader in pieces. This allows the user to interrupt
63its operation, and also allows very large data sources to be read.
64*/
65
66
67#include <QtCore>
68#include <QtWidgets>
69#include <QtNetwork>
70
71#include "rsslisting.h"
72
73
74/*
75 Constructs an RSSListing widget with a simple user interface, and sets
76 up the XML reader to use a custom handler class.
77
78 The user interface consists of a line edit, a push button, and a
79 list view widget. The line edit is used for entering the URLs of news
80 sources; the push button starts the process of reading the
81 news.
82*/
83
84RSSListing::RSSListing(QWidget *parent)
85 : QWidget(parent), currentReply(0)
86{
87
88 lineEdit = new QLineEdit(this);
89 lineEdit->setText("http://blog.qt.io/feed/");
90
91 fetchButton = new QPushButton(tr(s: "Fetch"), this);
92
93 treeWidget = new QTreeWidget(this);
94 connect(sender: treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
95 receiver: this, SLOT(itemActivated(QTreeWidgetItem*)));
96 QStringList headerLabels;
97 headerLabels << tr(s: "Title") << tr(s: "Link");
98 treeWidget->setHeaderLabels(headerLabels);
99 treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
100
101 connect(sender: &manager, SIGNAL(finished(QNetworkReply*)),
102 receiver: this, SLOT(finished(QNetworkReply*)));
103
104 connect(sender: lineEdit, SIGNAL(returnPressed()), receiver: this, SLOT(fetch()));
105 connect(sender: fetchButton, SIGNAL(clicked()), receiver: this, SLOT(fetch()));
106
107 QVBoxLayout *layout = new QVBoxLayout(this);
108
109 QHBoxLayout *hboxLayout = new QHBoxLayout;
110
111 hboxLayout->addWidget(lineEdit);
112 hboxLayout->addWidget(fetchButton);
113
114 layout->addLayout(layout: hboxLayout);
115 layout->addWidget(treeWidget);
116
117 setWindowTitle(tr(s: "RSS listing example"));
118 resize(w: 640,h: 480);
119}
120
121/*
122 Starts the network request and connects the needed signals
123*/
124void RSSListing::get(const QUrl &url)
125{
126 QNetworkRequest request(url);
127 if (currentReply) {
128 currentReply->disconnect(receiver: this);
129 currentReply->deleteLater();
130 }
131 currentReply = manager.get(request);
132 connect(sender: currentReply, SIGNAL(readyRead()), receiver: this, SLOT(readyRead()));
133 connect(sender: currentReply, SIGNAL(metaDataChanged()), receiver: this, SLOT(metaDataChanged()));
134 connect(sender: currentReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), receiver: this, SLOT(error(QNetworkReply::NetworkError)));
135}
136
137/*
138 Starts fetching data from a news source specified in the line
139 edit widget.
140
141 The line edit is made read only to prevent the user from modifying its
142 contents during the fetch; this is only for cosmetic purposes.
143 The fetch button is disabled, the list view is cleared, and we
144 define the last list view item to be 0, meaning that there are no
145 existing items in the list.
146
147 A URL is created with the raw contents of the line edit and
148 a get is initiated.
149*/
150
151void RSSListing::fetch()
152{
153 lineEdit->setReadOnly(true);
154 fetchButton->setEnabled(false);
155 treeWidget->clear();
156
157 xml.clear();
158
159 QUrl url(lineEdit->text());
160 get(url);
161}
162
163void RSSListing::metaDataChanged()
164{
165 QUrl redirectionTarget = currentReply->attribute(code: QNetworkRequest::RedirectionTargetAttribute).toUrl();
166 if (redirectionTarget.isValid()) {
167 get(url: redirectionTarget);
168 }
169}
170
171/*
172 Reads data received from the RDF source.
173
174 We read all the available data, and pass it to the XML
175 stream reader. Then we call the XML parsing function.
176*/
177
178void RSSListing::readyRead()
179{
180 int statusCode = currentReply->attribute(code: QNetworkRequest::HttpStatusCodeAttribute).toInt();
181 if (statusCode >= 200 && statusCode < 300) {
182 QByteArray data = currentReply->readAll();
183 xml.addData(data);
184 parseXml();
185 }
186}
187
188/*
189 Finishes processing an HTTP request.
190
191 The default behavior is to keep the text edit read only.
192
193 If an error has occurred, the user interface is made available
194 to the user for further input, allowing a new fetch to be
195 started.
196
197 If the HTTP get request has finished, we make the
198 user interface available to the user for further input.
199*/
200
201void RSSListing::finished(QNetworkReply *reply)
202{
203 Q_UNUSED(reply);
204 lineEdit->setReadOnly(false);
205 fetchButton->setEnabled(true);
206}
207
208
209/*
210 Parses the XML data and creates treeWidget items accordingly.
211*/
212void RSSListing::parseXml()
213{
214 while (!xml.atEnd()) {
215 xml.readNext();
216 if (xml.isStartElement()) {
217 if (xml.name() == "item")
218 linkString = xml.attributes().value(qualifiedName: "rss:about").toString();
219 currentTag = xml.name().toString();
220 } else if (xml.isEndElement()) {
221 if (xml.name() == "item") {
222
223 QTreeWidgetItem *item = new QTreeWidgetItem;
224 item->setText(column: 0, atext: titleString);
225 item->setText(column: 1, atext: linkString);
226 treeWidget->addTopLevelItem(item);
227
228 titleString.clear();
229 linkString.clear();
230 }
231
232 } else if (xml.isCharacters() && !xml.isWhitespace()) {
233 if (currentTag == "title")
234 titleString += xml.text();
235 else if (currentTag == "link")
236 linkString += xml.text();
237 }
238 }
239 if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
240 qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString();
241 }
242}
243
244/*
245 Open the link in the browser
246*/
247void RSSListing::itemActivated(QTreeWidgetItem * item)
248{
249 QDesktopServices::openUrl(url: QUrl(item->text(column: 1)));
250}
251
252void RSSListing::error(QNetworkReply::NetworkError)
253{
254 qWarning(msg: "error retrieving RSS feed");
255 currentReply->disconnect(receiver: this);
256 currentReply->deleteLater();
257 currentReply = 0;
258}
259

source code of qtbase/examples/xml/rsslisting/rsslisting.cpp