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