author | Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com> |
Mon, 15 Mar 2010 12:43:09 +0200 | |
branch | RCL_3 |
changeset 6 | dee5afe5301f |
parent 4 | 3b1da2848fc7 |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 4 |
** All rights reserved. |
5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 |
** |
|
7 |
** This file is part of the demonstration applications of the Qt Toolkit. |
|
8 |
** |
|
9 |
** $QT_BEGIN_LICENSE:LGPL$ |
|
10 |
** No Commercial Usage |
|
11 |
** This file contains pre-release code and may not be distributed. |
|
12 |
** You may use this file in accordance with the terms and conditions |
|
13 |
** contained in the Technology Preview License Agreement accompanying |
|
14 |
** this package. |
|
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 |
** If you have questions regarding the use of this file, please contact |
|
29 |
** Nokia at qt-info@nokia.com. |
|
30 |
** |
|
31 |
** |
|
32 |
** |
|
33 |
** |
|
34 |
** |
|
35 |
** |
|
36 |
** |
|
37 |
** |
|
38 |
** $QT_END_LICENSE$ |
|
39 |
** |
|
40 |
****************************************************************************/ |
|
41 |
||
42 |
#include <QtCore> |
|
43 |
#include <QtGui> |
|
44 |
#include <QtNetwork> |
|
45 |
||
46 |
#if defined (Q_OS_SYMBIAN) |
|
47 |
#include "sym_iap_util.h" |
|
48 |
#endif |
|
49 |
||
50 |
#include "ui_form.h" |
|
51 |
||
52 |
#define FLIGHTVIEW_URL "http://mobile.flightview.com/TrackByFlight.aspx" |
|
53 |
#define FLIGHTVIEW_RANDOM "http://mobile.flightview.com/TrackSampleFlight.aspx" |
|
54 |
||
55 |
// strips all invalid constructs that might trip QXmlStreamReader |
|
56 |
static QString sanitized(const QString &xml) |
|
57 |
{ |
|
58 |
QString data = xml; |
|
59 |
||
60 |
// anything up to the html tag |
|
61 |
int i = data.indexOf("<html"); |
|
62 |
if (i > 0) |
|
63 |
data.remove(0, i - 1); |
|
64 |
||
65 |
// everything inside the head tag |
|
66 |
i = data.indexOf("<head"); |
|
67 |
if (i > 0) |
|
68 |
data.remove(i, data.indexOf("</head>") - i + 7); |
|
69 |
||
70 |
// invalid link for JavaScript code |
|
71 |
while (true) { |
|
72 |
i = data.indexOf("onclick=\"gotoUrl("); |
|
73 |
if (i < 0) |
|
74 |
break; |
|
75 |
data.remove(i, data.indexOf('\"', i + 9) - i + 1); |
|
76 |
} |
|
77 |
||
78 |
// all inline frames |
|
79 |
while (true) { |
|
80 |
i = data.indexOf("<iframe"); |
|
81 |
if (i < 0) |
|
82 |
break; |
|
83 |
data.remove(i, data.indexOf("</iframe>") - i + 8); |
|
84 |
} |
|
85 |
||
86 |
// entities |
|
87 |
data.remove(" "); |
|
88 |
data.remove("©"); |
|
89 |
||
90 |
return data; |
|
91 |
} |
|
92 |
||
93 |
class FlightInfo : public QMainWindow |
|
94 |
{ |
|
95 |
Q_OBJECT |
|
96 |
||
97 |
private: |
|
98 |
||
99 |
Ui_Form ui; |
|
100 |
QUrl m_url; |
|
101 |
QDate m_searchDate; |
|
102 |
QPixmap m_map; |
|
103 |
||
104 |
public: |
|
105 |
||
106 |
FlightInfo(QMainWindow *parent = 0): QMainWindow(parent) { |
|
107 |
||
108 |
QWidget *w = new QWidget(this); |
|
109 |
ui.setupUi(w); |
|
110 |
setCentralWidget(w); |
|
111 |
||
112 |
ui.searchBar->hide(); |
|
113 |
ui.infoBox->hide(); |
|
114 |
connect(ui.searchButton, SIGNAL(clicked()), SLOT(startSearch())); |
|
115 |
connect(ui.flightEdit, SIGNAL(returnPressed()), SLOT(startSearch())); |
|
116 |
||
117 |
setWindowTitle("Flight Info"); |
|
118 |
QTimer::singleShot(0, this, SLOT(delayedInit())); |
|
119 |
||
120 |
// Rendered from the public-domain vectorized aircraft |
|
121 |
// http://openclipart.org/media/people/Jarno |
|
122 |
m_map = QPixmap(":/aircraft.png"); |
|
123 |
||
124 |
QAction *searchTodayAction = new QAction("Today's Flight", this); |
|
125 |
QAction *searchYesterdayAction = new QAction("Yesterday's Flight", this); |
|
126 |
QAction *randomAction = new QAction("Random Flight", this); |
|
127 |
connect(searchTodayAction, SIGNAL(triggered()), SLOT(today())); |
|
128 |
connect(searchYesterdayAction, SIGNAL(triggered()), SLOT(yesterday())); |
|
129 |
connect(randomAction, SIGNAL(triggered()), SLOT(randomFlight())); |
|
130 |
#if defined(Q_OS_SYMBIAN) |
|
131 |
menuBar()->addAction(searchTodayAction); |
|
132 |
menuBar()->addAction(searchYesterdayAction); |
|
133 |
menuBar()->addAction(randomAction); |
|
134 |
#else |
|
135 |
addAction(searchTodayAction); |
|
136 |
addAction(searchYesterdayAction); |
|
137 |
addAction(randomAction); |
|
138 |
setContextMenuPolicy(Qt::ActionsContextMenu); |
|
139 |
#endif |
|
140 |
} |
|
141 |
||
142 |
private slots: |
|
143 |
void delayedInit() { |
|
144 |
#if defined(Q_OS_SYMBIAN) |
|
145 |
qt_SetDefaultIap(); |
|
146 |
#endif |
|
147 |
} |
|
148 |
||
149 |
||
150 |
void handleNetworkData(QNetworkReply *networkReply) { |
|
151 |
if (!networkReply->error()) { |
|
152 |
// Assume UTF-8 encoded |
|
153 |
QByteArray data = networkReply->readAll(); |
|
154 |
QString xml = QString::fromUtf8(data); |
|
155 |
digest(xml); |
|
156 |
} |
|
157 |
networkReply->deleteLater(); |
|
158 |
networkReply->manager()->deleteLater(); |
|
159 |
} |
|
160 |
||
161 |
void handleMapData(QNetworkReply *networkReply) { |
|
162 |
if (!networkReply->error()) { |
|
163 |
m_map.loadFromData(networkReply->readAll()); |
|
164 |
update(); |
|
165 |
} |
|
166 |
networkReply->deleteLater(); |
|
167 |
networkReply->manager()->deleteLater(); |
|
168 |
} |
|
169 |
||
170 |
void today() { |
|
171 |
QDateTime timestamp = QDateTime::currentDateTime(); |
|
172 |
m_searchDate = timestamp.date(); |
|
173 |
searchFlight(); |
|
174 |
} |
|
175 |
||
176 |
void yesterday() { |
|
177 |
QDateTime timestamp = QDateTime::currentDateTime(); |
|
178 |
timestamp = timestamp.addDays(-1); |
|
179 |
m_searchDate = timestamp.date(); |
|
180 |
searchFlight(); |
|
181 |
} |
|
182 |
||
183 |
void searchFlight() { |
|
184 |
ui.searchBar->show(); |
|
185 |
ui.infoBox->hide(); |
|
186 |
ui.flightStatus->hide(); |
|
187 |
ui.flightName->setText("Enter flight number"); |
|
188 |
m_map = QPixmap(); |
|
189 |
update(); |
|
190 |
} |
|
191 |
||
192 |
void startSearch() { |
|
193 |
ui.searchBar->hide(); |
|
194 |
QString flight = ui.flightEdit->text().simplified(); |
|
195 |
if (!flight.isEmpty()) |
|
196 |
request(flight, m_searchDate); |
|
197 |
} |
|
198 |
||
199 |
void randomFlight() { |
|
200 |
request(QString(), QDate::currentDate()); |
|
201 |
} |
|
202 |
||
203 |
public slots: |
|
204 |
||
205 |
void request(const QString &flightCode, const QDate &date) { |
|
206 |
||
207 |
setWindowTitle("Loading..."); |
|
208 |
||
209 |
QString code = flightCode.simplified(); |
|
210 |
QString airlineCode = code.left(2).toUpper(); |
|
211 |
QString flightNumber = code.mid(2, code.length()); |
|
212 |
||
213 |
ui.flightName->setText("Searching for " + code); |
|
214 |
||
215 |
m_url = QUrl(FLIGHTVIEW_URL); |
|
216 |
m_url.addEncodedQueryItem("view", "detail"); |
|
217 |
m_url.addEncodedQueryItem("al", QUrl::toPercentEncoding(airlineCode)); |
|
218 |
m_url.addEncodedQueryItem("fn", QUrl::toPercentEncoding(flightNumber)); |
|
219 |
m_url.addEncodedQueryItem("dpdat", QUrl::toPercentEncoding(date.toString("yyyyMMdd"))); |
|
220 |
||
221 |
if (code.isEmpty()) { |
|
222 |
// random flight as sample |
|
223 |
m_url = QUrl(FLIGHTVIEW_RANDOM); |
|
224 |
ui.flightName->setText("Getting a random flight..."); |
|
225 |
} |
|
226 |
||
227 |
QNetworkAccessManager *manager = new QNetworkAccessManager(this); |
|
228 |
connect(manager, SIGNAL(finished(QNetworkReply*)), |
|
229 |
this, SLOT(handleNetworkData(QNetworkReply*))); |
|
230 |
manager->get(QNetworkRequest(m_url)); |
|
231 |
} |
|
232 |
||
233 |
||
234 |
private: |
|
235 |
||
236 |
void digest(const QString &content) { |
|
237 |
||
238 |
setWindowTitle("Flight Info"); |
|
239 |
QString data = sanitized(content); |
|
240 |
||
241 |
// do we only get the flight list? |
|
242 |
// we grab the first leg in the flight list |
|
243 |
// then fetch another URL for the real flight info |
|
244 |
int i = data.indexOf("a href=\"?view=detail"); |
|
245 |
if (i > 0) { |
|
246 |
QString href = data.mid(i, data.indexOf('\"', i + 8) - i + 1); |
|
247 |
QRegExp regex("dpap=([A-Za-z0-9]+)"); |
|
248 |
regex.indexIn(href); |
|
249 |
QString airport = regex.cap(1); |
|
250 |
m_url.addEncodedQueryItem("dpap", QUrl::toPercentEncoding(airport)); |
|
251 |
QNetworkAccessManager *manager = new QNetworkAccessManager(this); |
|
252 |
connect(manager, SIGNAL(finished(QNetworkReply*)), |
|
253 |
this, SLOT(handleNetworkData(QNetworkReply*))); |
|
254 |
manager->get(QNetworkRequest(m_url)); |
|
255 |
return; |
|
256 |
} |
|
257 |
||
258 |
QXmlStreamReader xml(data); |
|
259 |
bool inFlightName = false; |
|
260 |
bool inFlightStatus = false; |
|
261 |
bool inFlightMap = false; |
|
262 |
bool inFieldName = false; |
|
263 |
bool inFieldValue = false; |
|
264 |
||
265 |
QString flightName; |
|
266 |
QString flightStatus; |
|
267 |
QStringList fieldNames; |
|
268 |
QStringList fieldValues; |
|
269 |
||
270 |
while (!xml.atEnd()) { |
|
271 |
xml.readNext(); |
|
272 |
||
273 |
if (xml.tokenType() == QXmlStreamReader::StartElement) { |
|
274 |
QStringRef className = xml.attributes().value("class"); |
|
275 |
inFlightName |= xml.name() == "h1"; |
|
276 |
inFlightStatus |= className == "FlightDetailHeaderStatus"; |
|
277 |
inFlightMap |= className == "flightMap"; |
|
278 |
if (xml.name() == "td" && !className.isEmpty()) { |
|
279 |
QString cn = className.toString(); |
|
280 |
if (cn.contains("fieldTitle")) { |
|
281 |
inFieldName = true; |
|
282 |
fieldNames += QString(); |
|
283 |
fieldValues += QString(); |
|
284 |
} |
|
285 |
if (cn.contains("fieldValue")) |
|
286 |
inFieldValue = true; |
|
287 |
} |
|
288 |
if (xml.name() == "img" && inFlightMap) { |
|
289 |
QString src = xml.attributes().value("src").toString(); |
|
290 |
src.prepend("http://mobile.flightview.com"); |
|
291 |
QUrl url = QUrl::fromPercentEncoding(src.toAscii()); |
|
292 |
QNetworkAccessManager *manager = new QNetworkAccessManager(this); |
|
293 |
connect(manager, SIGNAL(finished(QNetworkReply*)), |
|
294 |
this, SLOT(handleMapData(QNetworkReply*))); |
|
295 |
manager->get(QNetworkRequest(url)); |
|
296 |
} |
|
297 |
} |
|
298 |
||
299 |
if (xml.tokenType() == QXmlStreamReader::EndElement) { |
|
300 |
inFlightName &= xml.name() != "h1"; |
|
301 |
inFlightStatus &= xml.name() != "div"; |
|
302 |
inFlightMap &= xml.name() != "div"; |
|
303 |
inFieldName &= xml.name() != "td"; |
|
304 |
inFieldValue &= xml.name() != "td"; |
|
305 |
} |
|
306 |
||
307 |
if (xml.tokenType() == QXmlStreamReader::Characters) { |
|
308 |
if (inFlightName) |
|
309 |
flightName += xml.text(); |
|
310 |
if (inFlightStatus) |
|
311 |
flightStatus += xml.text(); |
|
312 |
if (inFieldName) |
|
313 |
fieldNames.last() += xml.text(); |
|
314 |
if (inFieldValue) |
|
315 |
fieldValues.last() += xml.text(); |
|
316 |
} |
|
317 |
} |
|
318 |
||
319 |
if (fieldNames.isEmpty()) { |
|
320 |
QString code = ui.flightEdit->text().simplified().left(10); |
|
321 |
QString msg = QString("Flight %1 is not found").arg(code); |
|
322 |
ui.flightName->setText(msg); |
|
323 |
return; |
|
324 |
} |
|
325 |
||
326 |
ui.flightName->setText(flightName); |
|
327 |
flightStatus.remove("Status: "); |
|
328 |
ui.flightStatus->setText(flightStatus); |
|
329 |
ui.flightStatus->show(); |
|
330 |
||
331 |
QStringList whiteList; |
|
332 |
whiteList << "Departure"; |
|
333 |
whiteList << "Arrival"; |
|
334 |
whiteList << "Scheduled"; |
|
335 |
whiteList << "Takeoff"; |
|
336 |
whiteList << "Estimated"; |
|
337 |
whiteList << "Term-Gate"; |
|
338 |
||
339 |
QString text; |
|
340 |
text = QString("<table width=%1>").arg(width() - 25); |
|
341 |
for (int i = 0; i < fieldNames.count(); i++) { |
|
342 |
QString fn = fieldNames[i].simplified(); |
|
343 |
if (fn.endsWith(':')) |
|
344 |
fn = fn.left(fn.length() - 1); |
|
345 |
if (!whiteList.contains(fn)) |
|
346 |
continue; |
|
347 |
||
348 |
QString fv = fieldValues[i].simplified(); |
|
349 |
bool special = false; |
|
350 |
special |= fn.startsWith("Departure"); |
|
351 |
special |= fn.startsWith("Arrival"); |
|
352 |
text += "<tr>"; |
|
353 |
if (special) { |
|
354 |
text += "<td align=center colspan=2>"; |
|
355 |
text += "<b><font size=+1>" + fv + "</font></b>"; |
|
356 |
text += "</td>"; |
|
357 |
} else { |
|
358 |
text += "<td align=right>"; |
|
359 |
text += fn; |
|
360 |
text += " : "; |
|
361 |
text += " "; |
|
362 |
text += "</td>"; |
|
363 |
text += "<td>"; |
|
364 |
text += fv; |
|
365 |
text += "</td>"; |
|
366 |
} |
|
367 |
text += "</tr>"; |
|
368 |
} |
|
369 |
text += "</table>"; |
|
370 |
ui.detailedInfo->setText(text); |
|
371 |
ui.infoBox->show(); |
|
372 |
} |
|
373 |
||
374 |
void resizeEvent(QResizeEvent *event) { |
|
375 |
Q_UNUSED(event); |
|
376 |
ui.detailedInfo->setMaximumWidth(width() - 25); |
|
377 |
} |
|
378 |
||
379 |
void paintEvent(QPaintEvent *event) { |
|
380 |
QMainWindow::paintEvent(event); |
|
381 |
QPainter p(this); |
|
382 |
p.fillRect(rect(), QColor(131, 171, 210)); |
|
383 |
if (!m_map.isNull()) { |
|
384 |
int x = (width() - m_map.width()) / 2; |
|
385 |
int space = ui.infoBox->pos().y(); |
|
386 |
if (!ui.infoBox->isVisible()) |
|
387 |
space = height(); |
|
388 |
int top = ui.titleBox->height(); |
|
389 |
int y = qMax(top, (space - m_map.height()) / 2); |
|
390 |
p.drawPixmap(x, y, m_map); |
|
391 |
} |
|
392 |
p.end(); |
|
393 |
} |
|
394 |
||
395 |
}; |
|
396 |
||
397 |
||
398 |
#include "flightinfo.moc" |
|
399 |
||
400 |
int main(int argc, char **argv) |
|
401 |
{ |
|
402 |
Q_INIT_RESOURCE(flightinfo); |
|
403 |
||
404 |
QApplication app(argc, argv); |
|
405 |
||
406 |
FlightInfo w; |
|
407 |
#if defined(Q_OS_SYMBIAN) |
|
408 |
w.showMaximized(); |
|
409 |
#else |
|
410 |
w.resize(360, 504); |
|
411 |
w.show(); |
|
412 |
#endif |
|
413 |
||
414 |
return app.exec(); |
|
415 |
} |