Home | History | Annotate | Download | only in QGVLauncher
      1 /*
      2  * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
      3  * Copyright (C) 2006 George Staikos <staikos (at) kde.org>
      4  * Copyright (C) 2006 Dirk Mueller <mueller (at) kde.org>
      5  * Copyright (C) 2006 Zack Rusin <zack (at) kde.org>
      6  * Copyright (C) 2006 Simon Hausmann <hausmann (at) kde.org>
      7  * Copyright (C) 2009 Kenneth Christiansen <kenneth (at) webkit.org>
      8  * Copyright (C) 2009 Antonio Gomes <antonio.gomes (at) openbossa.org>
      9  * Copyright (C) 2009 Girish Ramakrishnan <girish (at) forwardbias.in>
     10  *
     11  * All rights reserved.
     12  *
     13  * Redistribution and use in source and binary forms, with or without
     14  * modification, are permitted provided that the following conditions
     15  * are met:
     16  * 1. Redistributions of source code must retain the above copyright
     17  *    notice, this list of conditions and the following disclaimer.
     18  * 2. Redistributions in binary form must reproduce the above copyright
     19  *    notice, this list of conditions and the following disclaimer in the
     20  *    documentation and/or other materials provided with the distribution.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     26  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     30  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 
     35 #include <QDebug>
     36 #include <QFile>
     37 #include <QGraphicsScene>
     38 #include <QGraphicsView>
     39 #include <QGraphicsWidget>
     40 #include <QNetworkRequest>
     41 #include <QTextStream>
     42 #include <QVector>
     43 #include <QtGui>
     44 #include <QtNetwork/QNetworkProxy>
     45 #include <cstdio>
     46 #include <qwebelement.h>
     47 #include <qwebframe.h>
     48 #include <qgraphicswebview.h>
     49 #include <qwebpage.h>
     50 #include <qwebsettings.h>
     51 #include <qwebview.h>
     52 
     53 static QUrl urlFromUserInput(const QString& string)
     54 {
     55     QString input(string);
     56     QFileInfo fi(input);
     57     if (fi.exists() && fi.isRelative())
     58         input = fi.absoluteFilePath();
     59 
     60 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
     61     return QUrl::fromUserInput(input);
     62 #else
     63     return QUrl(input);
     64 #endif
     65 }
     66 
     67 class WebView : public QGraphicsWebView {
     68     Q_OBJECT
     69     Q_PROPERTY(qreal yRotation READ yRotation WRITE setYRotation)
     70 
     71 public:
     72     WebView(QGraphicsItem* parent = 0)
     73         : QGraphicsWebView(parent)
     74     {
     75         if (QApplication::instance()->arguments().contains("--cacheWebView"))
     76             setCacheMode(QGraphicsItem::DeviceCoordinateCache);
     77     }
     78     void setYRotation(qreal angle)
     79     {
     80 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
     81         QRectF r = boundingRect();
     82         setTransform(QTransform()
     83             .translate(r.width() / 2, r.height() / 2)
     84             .rotate(angle, Qt::YAxis)
     85             .translate(-r.width() / 2, -r.height() / 2));
     86 #endif
     87         m_yRotation = angle;
     88     }
     89     qreal yRotation() const
     90     {
     91         return m_yRotation;
     92     }
     93 
     94 private:
     95     qreal m_yRotation;
     96 };
     97 
     98 class WebPage : public QWebPage {
     99     Q_OBJECT
    100 
    101 public:
    102     WebPage(QObject* parent = 0)
    103         : QWebPage(parent)
    104     {
    105         applyProxy();
    106     }
    107     virtual QWebPage* createWindow(QWebPage::WebWindowType);
    108 
    109 private:
    110     void applyProxy()
    111     {
    112         QUrl proxyUrl = urlFromUserInput(qgetenv("http_proxy"));
    113 
    114         if (proxyUrl.isValid() && !proxyUrl.host().isEmpty()) {
    115             int proxyPort = (proxyUrl.port() > 0) ? proxyUrl.port() : 8080;
    116             networkAccessManager()->setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyPort));
    117         }
    118     }
    119 };
    120 
    121 class MainView : public QGraphicsView {
    122     Q_OBJECT
    123 
    124 public:
    125     MainView(QWidget* parent)
    126         : QGraphicsView(parent)
    127         , m_mainWidget(0)
    128         , m_measureFps(QApplication::instance()->arguments().contains("--show-fps"))
    129         , m_numTotalPaints(0)
    130         , m_numPaintsSinceLastMeasure(0)
    131     {
    132         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    133         setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    134 
    135         setFrameShape(QFrame::NoFrame);
    136         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    137         if (m_measureFps) {
    138             QTimer* fpsTimer = new QTimer(this);
    139             fpsTimer->setInterval(5000);
    140             m_totalStartTime = m_startTime = QTime::currentTime();
    141             connect(fpsTimer, SIGNAL(timeout()), this, SLOT(printFps()));
    142             fpsTimer->start();
    143         }
    144     }
    145 
    146     void setMainWidget(QGraphicsWidget* widget)
    147     {
    148         QRectF rect(QRect(QPoint(0, 0), size()));
    149         widget->setGeometry(rect);
    150         m_mainWidget = widget;
    151     }
    152 
    153     void resizeEvent(QResizeEvent* event)
    154     {
    155         QGraphicsView::resizeEvent(event);
    156         if (!m_mainWidget)
    157             return;
    158         QRectF rect(QPoint(0, 0), event->size());
    159         m_mainWidget->setGeometry(rect);
    160     }
    161 
    162     void paintEvent(QPaintEvent* event)
    163     {
    164         QGraphicsView::paintEvent(event);
    165         if (m_measureFps) {
    166             ++m_numPaintsSinceLastMeasure;
    167             ++m_numTotalPaints;
    168         }
    169     }
    170 
    171     void setWaitCursor()
    172     {
    173         m_mainWidget->setCursor(Qt::WaitCursor);
    174     }
    175 
    176     void resetCursor()
    177     {
    178         m_mainWidget->unsetCursor();
    179     }
    180 
    181 public slots:
    182     void flip()
    183     {
    184 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
    185         QSizeF center = m_mainWidget->boundingRect().size() / 2;
    186         QPointF centerPoint = QPointF(center.width(), center.height());
    187         m_mainWidget->setTransformOriginPoint(centerPoint);
    188 
    189         m_mainWidget->setRotation(m_mainWidget->rotation() ? 0 : 180);
    190 #endif
    191     }
    192 
    193     void animatedFlip()
    194     {
    195 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
    196         QSizeF center = m_mainWidget->boundingRect().size() / 2;
    197         QPointF centerPoint = QPointF(center.width(), center.height());
    198         m_mainWidget->setTransformOriginPoint(centerPoint);
    199 
    200         QPropertyAnimation* animation = new QPropertyAnimation(m_mainWidget, "rotation", this);
    201         animation->setDuration(1000);
    202 
    203         int rotation = int(m_mainWidget->rotation());
    204 
    205         animation->setStartValue(rotation);
    206         animation->setEndValue(rotation + 180 - (rotation % 180));
    207 
    208         animation->start(QAbstractAnimation::DeleteWhenStopped);
    209 #endif
    210     }
    211 
    212     void animatedYFlip()
    213     {
    214         emit flipRequest();
    215     }
    216 
    217     void printFps()
    218     {
    219         // note that this might have a bug if you measure right around midnight, but we can live with that
    220         QTime now = QTime::currentTime();
    221         int msecs = m_startTime.msecsTo(now);
    222         int totalMsecs = m_totalStartTime.msecsTo(now);
    223         int totalFps = totalMsecs ? m_numTotalPaints * 1000 / totalMsecs : 0;
    224         int curFps = msecs ? m_numPaintsSinceLastMeasure * 1000 / msecs : 0;
    225         qDebug("[FPS] From start: %d, from last paint: %d", totalFps, curFps);
    226         m_startTime = now;
    227         m_numPaintsSinceLastMeasure = 0;
    228     }
    229 
    230 signals:
    231     void flipRequest();
    232 
    233 private:
    234     QGraphicsWidget* m_mainWidget;
    235     bool m_measureFps;
    236     int m_numTotalPaints;
    237     int m_numPaintsSinceLastMeasure;
    238     QTime m_startTime;
    239     QTime m_totalStartTime;
    240 };
    241 
    242 class SharedScene : public QSharedData {
    243 public:
    244     SharedScene()
    245     {
    246         m_scene = new QGraphicsScene;
    247         m_item = new WebView;
    248         m_item->setPage((m_page = new WebPage));
    249 
    250         m_scene->addItem(m_item);
    251         m_scene->setActiveWindow(m_item);
    252     }
    253 
    254     ~SharedScene()
    255     {
    256         delete m_item;
    257         delete m_scene;
    258         delete m_page;
    259     }
    260 
    261     QGraphicsScene* scene() const { return m_scene; }
    262     WebView* webView() const { return m_item; }
    263 
    264 private:
    265     QGraphicsScene* m_scene;
    266     WebView* m_item;
    267     WebPage* m_page;
    268 };
    269 
    270 class MainWindow : public QMainWindow {
    271     Q_OBJECT
    272 
    273 public:
    274     MainWindow(QExplicitlySharedDataPointer<SharedScene> other)
    275         : QMainWindow()
    276         , view(new MainView(this))
    277         , scene(other)
    278     {
    279         init();
    280     }
    281 
    282     MainWindow()
    283         : QMainWindow()
    284         , view(new MainView(this))
    285         , scene(new SharedScene())
    286     {
    287         init();
    288     }
    289 
    290     void init()
    291     {
    292         setAttribute(Qt::WA_DeleteOnClose);
    293 
    294         view->setScene(scene->scene());
    295         const QStringList arguments = QApplication::instance()->arguments();
    296         const int indexOfViewportUpdateMode = arguments.indexOf("--updateMode");
    297         if (indexOfViewportUpdateMode > 1 && indexOfViewportUpdateMode < arguments.count() - 1) {
    298             const QString updateMode = arguments[indexOfViewportUpdateMode+1] + "ViewportUpdate";
    299             view->setViewportUpdateMode(static_cast<QGraphicsView::ViewportUpdateMode>(QGraphicsView::staticMetaObject.enumerator(QGraphicsView::staticMetaObject.indexOfEnumerator("ViewportUpdateMode")).keysToValue(updateMode.toAscii())));
    300         }
    301 
    302         setCentralWidget(view);
    303 
    304         view->setMainWidget(scene->webView());
    305 
    306         connect(scene->webView(), SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
    307         connect(scene->webView(), SIGNAL(titleChanged(const QString&)), this, SLOT(setWindowTitle(const QString&)));
    308         connect(scene->webView()->page(), SIGNAL(windowCloseRequested()), this, SLOT(close()));
    309 
    310 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
    311         QStateMachine *machine = new QStateMachine(this);
    312         QState *s0 = new QState(machine);
    313         s0->assignProperty(scene->webView(), "yRotation", 0);
    314 
    315         QState *s1 = new QState(machine);
    316         s1->assignProperty(scene->webView(), "yRotation", 90);
    317 
    318         QAbstractTransition *t1 = s0->addTransition(view, SIGNAL(flipRequest()), s1);
    319         QPropertyAnimation *yRotationAnim = new QPropertyAnimation(scene->webView(), "yRotation", this);
    320         yRotationAnim->setDuration(1000);
    321         t1->addAnimation(yRotationAnim);
    322 
    323         QState *s2 = new QState(machine);
    324         s2->assignProperty(scene->webView(), "yRotation", -90);
    325         s1->addTransition(s1, SIGNAL(propertiesAssigned()), s2);
    326 
    327         QAbstractTransition *t2 = s2->addTransition(s0);
    328         t2->addAnimation(yRotationAnim);
    329 
    330         machine->setInitialState(s0);
    331         machine->start();
    332 #endif
    333 
    334         resize(640, 480);
    335         buildUI();
    336     }
    337 
    338     void load(const QString& url)
    339     {
    340         QUrl deducedUrl = urlFromUserInput(url);
    341         if (!deducedUrl.isValid())
    342             deducedUrl = QUrl("http://" + url + "/");
    343 
    344         loadURL(deducedUrl);
    345     }
    346 
    347     QWebPage* page() const
    348     {
    349         return scene->webView()->page();
    350     }
    351 
    352 protected slots:
    353 
    354     void openFile()
    355     {
    356         static const QString filter("HTML Files (*.htm *.html);;Text Files (*.txt);;Image Files (*.gif *.jpg *.png);;All Files (*)");
    357 
    358         QFileDialog fileDialog(this, tr("Open"), QString(), filter);
    359         fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
    360         fileDialog.setFileMode(QFileDialog::ExistingFile);
    361         fileDialog.setOptions(QFileDialog::ReadOnly);
    362 
    363         if (fileDialog.exec()) {
    364             QString selectedFile = fileDialog.selectedFiles()[0];
    365             if (!selectedFile.isEmpty())
    366                 loadURL(QUrl::fromLocalFile(selectedFile));
    367         }
    368     }
    369 
    370     void changeLocation()
    371     {
    372         load(urlEdit->text());
    373     }
    374 
    375     void loadFinished(bool)
    376     {
    377         QUrl url = scene->webView()->url();
    378         urlEdit->setText(url.toString());
    379 
    380         QUrl::FormattingOptions opts;
    381         opts |= QUrl::RemoveScheme;
    382         opts |= QUrl::RemoveUserInfo;
    383         opts |= QUrl::StripTrailingSlash;
    384         QString s = url.toString(opts);
    385         s = s.mid(2);
    386         if (s.isEmpty())
    387             return;
    388         //FIXME: something missing here
    389     }
    390 
    391 public slots:
    392     void newWindow(const QString &url = QString())
    393     {
    394         MainWindow* mw = new MainWindow();
    395         mw->load(url);
    396         mw->show();
    397     }
    398 
    399     void clone()
    400     {
    401         MainWindow* mw = new MainWindow(scene);
    402         mw->show();
    403     }
    404 
    405     void setWaitCursor()
    406     {
    407         view->setWaitCursor();
    408     }
    409 
    410     void resetCursor()
    411     {
    412         view->resetCursor();
    413     }
    414 
    415     void flip()
    416     {
    417         view->flip();
    418     }
    419 
    420     void animatedFlip()
    421     {
    422         view->animatedFlip();
    423     }
    424 
    425     void animatedYFlip()
    426     {
    427         view->animatedYFlip();
    428     }
    429 
    430 private:
    431 
    432     void loadURL(const QUrl& url)
    433     {
    434         if (!url.isValid())
    435             return;
    436 
    437         urlEdit->setText(url.toString());
    438         scene->webView()->load(url);
    439         scene->webView()->setFocus(Qt::OtherFocusReason);
    440     }
    441 
    442     void buildUI()
    443     {
    444         QWebPage* page = scene->webView()->page();
    445         urlEdit = new QLineEdit(this);
    446         urlEdit->setSizePolicy(QSizePolicy::Expanding, urlEdit->sizePolicy().verticalPolicy());
    447         connect(urlEdit, SIGNAL(returnPressed()), SLOT(changeLocation()));
    448 
    449         QToolBar* bar = addToolBar("Navigation");
    450         bar->addAction(page->action(QWebPage::Back));
    451         bar->addAction(page->action(QWebPage::Forward));
    452         bar->addAction(page->action(QWebPage::Reload));
    453         bar->addAction(page->action(QWebPage::Stop));
    454         bar->addWidget(urlEdit);
    455 
    456         QMenu* fileMenu = menuBar()->addMenu("&File");
    457         fileMenu->addAction("New Window", this, SLOT(newWindow()), QKeySequence::New);
    458         fileMenu->addAction("Open File...", this, SLOT(openFile()), QKeySequence::Open);
    459         fileMenu->addAction("Clone Window", this, SLOT(clone()));
    460         fileMenu->addAction("Close Window", this, SLOT(close()), QKeySequence::Close);
    461         fileMenu->addSeparator();
    462         fileMenu->addAction("Quit", QApplication::instance(), SLOT(closeAllWindows()), QKeySequence(Qt::CTRL | Qt::Key_Q));
    463 
    464         QMenu* viewMenu = menuBar()->addMenu("&View");
    465         viewMenu->addAction(page->action(QWebPage::Stop));
    466         viewMenu->addAction(page->action(QWebPage::Reload));
    467 
    468         QMenu* testMenu = menuBar()->addMenu("&Tests");
    469         testMenu->addAction("Set Wait Cursor", this, SLOT(setWaitCursor()), QKeySequence("Ctrl+W"));
    470         testMenu->addAction("Reset Cursor", this, SLOT(resetCursor()), QKeySequence("Ctrl+Shift+W"));
    471 
    472         QMenu* fxMenu = menuBar()->addMenu("&Effects");
    473         fxMenu->addAction("Flip", this, SLOT(flip()));
    474         fxMenu->addAction("Animated Flip", this, SLOT(animatedFlip()), QKeySequence("Ctrl+R"));
    475         fxMenu->addAction("Animated Y-Flip", this, SLOT(animatedYFlip()), QKeySequence("Ctrl+Y"));
    476     }
    477 
    478 private:
    479     MainView* view;
    480     QExplicitlySharedDataPointer<SharedScene> scene;
    481 
    482     QLineEdit* urlEdit;
    483 };
    484 
    485 QWebPage* WebPage::createWindow(QWebPage::WebWindowType)
    486 {
    487     MainWindow* mw = new MainWindow;
    488     mw->show();
    489     return mw->page();
    490 }
    491 
    492 int main(int argc, char** argv)
    493 {
    494     QApplication app(argc, argv);
    495     if (app.arguments().contains("--help")) {
    496         qDebug() << "Usage: QGVLauncher [--url url] [--compositing] [--updateMode Full|Minimal|Smart|No|BoundingRect] [--cacheWebView]\n";
    497         return 0;
    498     }
    499     QString url = QString("file://%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html"));
    500 
    501     app.setApplicationName("GQVLauncher");
    502 
    503     QWebSettings::setObjectCacheCapacities((16 * 1024 * 1024) / 8, (16 * 1024 * 1024) / 8, 16 * 1024 * 1024);
    504     QWebSettings::setMaximumPagesInCache(4);
    505     QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
    506     QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
    507     QWebSettings::enablePersistentStorage();
    508 
    509     const QStringList args = app.arguments();
    510     const int indexOfUrl = args.indexOf("--url");
    511     if (indexOfUrl > 0 && indexOfUrl < args.count() - 1)
    512         url = args.at(indexOfUrl+1);
    513     else if (args.count() > 1)
    514         url = args.at(1);
    515     if (args.contains("--compositing"))
    516         QWebSettings::globalSettings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true);
    517 
    518     MainWindow* window = new MainWindow;
    519     window->load(url);
    520 
    521     for (int i = 2; i < args.count(); ++i)
    522         if (!args.at(i).startsWith("-") && !args.at(i - 1).startsWith("-"))
    523             window->newWindow(args.at(i));
    524 
    525     window->show();
    526     return app.exec();
    527 }
    528 
    529 #include "main.moc"
    530