/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: http://www.qt-project.org/ ** ** This file is part of the demonstration applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "ui_form.h" #define FLIGHTVIEW_URL "http://mobile.flightview.com/TrackByFlight.aspx" #define FLIGHTVIEW_RANDOM "http://mobile.flightview.com/TrackSampleFlight.aspx" // strips all invalid constructs that might trip QXmlStreamReader static QString sanitized(const QString &xml) { QString data = xml; // anything up to the html tag int i = data.indexOf(" 0) data.remove(0, i - 1); // everything inside the head tag i = data.indexOf(" 0) data.remove(i, data.indexOf("") - i + 7); // invalid link for JavaScript code while (true) { i = data.indexOf("onclick=\"gotoUrl("); if (i < 0) break; data.remove(i, data.indexOf('\"', i + 9) - i + 1); } // all inline frames while (true) { i = data.indexOf("") - i + 8); } // entities data.remove(" "); data.remove("©"); return data; } class FlightInfo : public QMainWindow { Q_OBJECT private: Ui_Form ui; QUrl m_url; QDate m_searchDate; QPixmap m_map; QNetworkAccessManager m_manager; QList mapReplies; public: FlightInfo(QMainWindow *parent = 0): QMainWindow(parent) { QWidget *w = new QWidget(this); ui.setupUi(w); setCentralWidget(w); ui.searchBar->hide(); ui.infoBox->hide(); connect(ui.searchButton, SIGNAL(clicked()), SLOT(startSearch())); connect(ui.flightEdit, SIGNAL(returnPressed()), SLOT(startSearch())); setWindowTitle("Flight Info"); // Rendered from the public-domain vectorized aircraft // http://openclipart.org/media/people/Jarno m_map = QPixmap(":/aircraft.png"); QAction *searchTodayAction = new QAction("Today's Flight", this); QAction *searchYesterdayAction = new QAction("Yesterday's Flight", this); QAction *randomAction = new QAction("Random Flight", this); connect(searchTodayAction, SIGNAL(triggered()), SLOT(today())); connect(searchYesterdayAction, SIGNAL(triggered()), SLOT(yesterday())); connect(randomAction, SIGNAL(triggered()), SLOT(randomFlight())); connect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleNetworkData(QNetworkReply*))); addAction(searchTodayAction); addAction(searchYesterdayAction); addAction(randomAction); setContextMenuPolicy(Qt::ActionsContextMenu); } private slots: void handleNetworkData(QNetworkReply *networkReply) { if (!networkReply->error()) { if (!mapReplies.contains(networkReply)) { // Assume UTF-8 encoded QByteArray data = networkReply->readAll(); QString xml = QString::fromUtf8(data); digest(xml); } else { mapReplies.removeOne(networkReply); m_map.loadFromData(networkReply->readAll()); update(); } } networkReply->deleteLater(); } void today() { QDateTime timestamp = QDateTime::currentDateTime(); m_searchDate = timestamp.date(); searchFlight(); } void yesterday() { QDateTime timestamp = QDateTime::currentDateTime(); timestamp = timestamp.addDays(-1); m_searchDate = timestamp.date(); searchFlight(); } void searchFlight() { ui.searchBar->show(); ui.infoBox->hide(); ui.flightStatus->hide(); ui.flightName->setText("Enter flight number"); ui.flightEdit->setFocus(); #ifdef QT_KEYPAD_NAVIGATION ui.flightEdit->setEditFocus(true); #endif m_map = QPixmap(); update(); } void startSearch() { ui.searchBar->hide(); QString flight = ui.flightEdit->text().simplified(); if (!flight.isEmpty()) request(flight, m_searchDate); } void randomFlight() { request(QString(), QDate::currentDate()); } public slots: void request(const QString &flightCode, const QDate &date) { setWindowTitle("Loading..."); QString code = flightCode.simplified(); QString airlineCode = code.left(2).toUpper(); QString flightNumber = code.mid(2, code.length()); ui.flightName->setText("Searching for " + code); m_url = QUrl(FLIGHTVIEW_URL); m_url.addEncodedQueryItem("view", "detail"); m_url.addEncodedQueryItem("al", QUrl::toPercentEncoding(airlineCode)); m_url.addEncodedQueryItem("fn", QUrl::toPercentEncoding(flightNumber)); m_url.addEncodedQueryItem("dpdat", QUrl::toPercentEncoding(date.toString("yyyyMMdd"))); if (code.isEmpty()) { // random flight as sample m_url = QUrl(FLIGHTVIEW_RANDOM); ui.flightName->setText("Getting a random flight..."); } m_manager.get(QNetworkRequest(m_url)); } private: void digest(const QString &content) { setWindowTitle("Flight Info"); QString data = sanitized(content); // do we only get the flight list? // we grab the first leg in the flight list // then fetch another URL for the real flight info int i = data.indexOf("a href=\"?view=detail"); if (i > 0) { QString href = data.mid(i, data.indexOf('\"', i + 8) - i + 1); QRegExp regex("dpap=([A-Za-z0-9]+)"); regex.indexIn(href); QString airport = regex.cap(1); m_url.addEncodedQueryItem("dpap", QUrl::toPercentEncoding(airport)); m_manager.get(QNetworkRequest(m_url)); return; } QXmlStreamReader xml(data); bool inFlightName = false; bool inFlightStatus = false; bool inFlightMap = false; bool inFieldName = false; bool inFieldValue = false; QString flightName; QString flightStatus; QStringList fieldNames; QStringList fieldValues; while (!xml.atEnd()) { xml.readNext(); if (xml.tokenType() == QXmlStreamReader::StartElement) { QStringRef className = xml.attributes().value("class"); inFlightName |= xml.name() == "h1"; inFlightStatus |= className == "FlightDetailHeaderStatus"; inFlightMap |= className == "flightMap"; if (xml.name() == "td" && !className.isEmpty()) { QString cn = className.toString(); if (cn.contains("fieldTitle")) { inFieldName = true; fieldNames += QString(); fieldValues += QString(); } if (cn.contains("fieldValue")) inFieldValue = true; } if (xml.name() == "img" && inFlightMap) { QString src = xml.attributes().value("src").toString(); src.prepend("http://mobile.flightview.com/"); QUrl url = QUrl::fromPercentEncoding(src.toAscii()); mapReplies.append(m_manager.get(QNetworkRequest(url))); } } if (xml.tokenType() == QXmlStreamReader::EndElement) { inFlightName &= xml.name() != "h1"; inFlightStatus &= xml.name() != "div"; inFlightMap &= xml.name() != "div"; inFieldName &= xml.name() != "td"; inFieldValue &= xml.name() != "td"; } if (xml.tokenType() == QXmlStreamReader::Characters) { if (inFlightName) flightName += xml.text(); if (inFlightStatus) flightStatus += xml.text(); if (inFieldName) fieldNames.last() += xml.text(); if (inFieldValue) fieldValues.last() += xml.text(); } } if (fieldNames.isEmpty()) { QString code = ui.flightEdit->text().simplified().left(10); QString msg = QString("Flight %1 is not found").arg(code); ui.flightName->setText(msg); return; } ui.flightName->setText(flightName); flightStatus.remove("Status: "); ui.flightStatus->setText(flightStatus); ui.flightStatus->show(); QStringList whiteList; whiteList << "Departure"; whiteList << "Arrival"; whiteList << "Scheduled"; whiteList << "Takeoff"; whiteList << "Estimated"; whiteList << "Term-Gate"; QString text; text = QString("").arg(width() - 25); for (int i = 0; i < fieldNames.count(); i++) { QString fn = fieldNames[i].simplified(); if (fn.endsWith(':')) fn = fn.left(fn.length() - 1); if (!whiteList.contains(fn)) continue; QString fv = fieldValues[i].simplified(); bool special = false; special |= fn.startsWith("Departure"); special |= fn.startsWith("Arrival"); text += ""; if (special) { text += ""; } else { text += ""; text += ""; } text += ""; } text += "
"; text += "" + fv + ""; text += ""; text += fn; text += " : "; text += " "; text += ""; text += fv; text += "
"; ui.detailedInfo->setText(text); ui.infoBox->show(); } void resizeEvent(QResizeEvent *event) { Q_UNUSED(event); ui.detailedInfo->setMaximumWidth(width() - 25); } void paintEvent(QPaintEvent *event) { QMainWindow::paintEvent(event); QPainter p(this); p.fillRect(rect(), QColor(131, 171, 210)); if (!m_map.isNull()) { int x = (width() - m_map.width()) / 2; int space = ui.infoBox->pos().y(); if (!ui.infoBox->isVisible()) space = height(); int top = ui.titleBox->height(); int y = qMax(top, (space - m_map.height()) / 2); p.drawPixmap(x, y, m_map); } p.end(); } }; #include "flightinfo.moc" int main(int argc, char **argv) { Q_INIT_RESOURCE(flightinfo); QApplication app(argc, argv); FlightInfo w; w.resize(360, 504); w.show(); return app.exec(); }