/* * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2006 George Staikos * Copyright (C) 2006 Dirk Mueller * Copyright (C) 2006 Zack Rusin * Copyright (C) 2006 Simon Hausmann * Copyright (C) 2009 Kenneth Christiansen * Copyright (C) 2009 Antonio Gomes * Copyright (C) 2009 Girish Ramakrishnan * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static QUrl urlFromUserInput(const QString& string) { QString input(string); QFileInfo fi(input); if (fi.exists() && fi.isRelative()) input = fi.absoluteFilePath(); #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) return QUrl::fromUserInput(input); #else return QUrl(input); #endif } class WebView : public QGraphicsWebView { Q_OBJECT Q_PROPERTY(qreal yRotation READ yRotation WRITE setYRotation) public: WebView(QGraphicsItem* parent = 0) : QGraphicsWebView(parent) { if (QApplication::instance()->arguments().contains("--cacheWebView")) setCacheMode(QGraphicsItem::DeviceCoordinateCache); } void setYRotation(qreal angle) { #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) QRectF r = boundingRect(); setTransform(QTransform() .translate(r.width() / 2, r.height() / 2) .rotate(angle, Qt::YAxis) .translate(-r.width() / 2, -r.height() / 2)); #endif m_yRotation = angle; } qreal yRotation() const { return m_yRotation; } private: qreal m_yRotation; }; class WebPage : public QWebPage { Q_OBJECT public: WebPage(QObject* parent = 0) : QWebPage(parent) { applyProxy(); } virtual QWebPage* createWindow(QWebPage::WebWindowType); private: void applyProxy() { QUrl proxyUrl = urlFromUserInput(qgetenv("http_proxy")); if (proxyUrl.isValid() && !proxyUrl.host().isEmpty()) { int proxyPort = (proxyUrl.port() > 0) ? proxyUrl.port() : 8080; networkAccessManager()->setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyPort)); } } }; class MainView : public QGraphicsView { Q_OBJECT public: MainView(QWidget* parent) : QGraphicsView(parent) , m_mainWidget(0) , m_measureFps(QApplication::instance()->arguments().contains("--show-fps")) , m_numTotalPaints(0) , m_numPaintsSinceLastMeasure(0) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setFrameShape(QFrame::NoFrame); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); if (m_measureFps) { QTimer* fpsTimer = new QTimer(this); fpsTimer->setInterval(5000); m_totalStartTime = m_startTime = QTime::currentTime(); connect(fpsTimer, SIGNAL(timeout()), this, SLOT(printFps())); fpsTimer->start(); } } void setMainWidget(QGraphicsWidget* widget) { QRectF rect(QRect(QPoint(0, 0), size())); widget->setGeometry(rect); m_mainWidget = widget; } void resizeEvent(QResizeEvent* event) { QGraphicsView::resizeEvent(event); if (!m_mainWidget) return; QRectF rect(QPoint(0, 0), event->size()); m_mainWidget->setGeometry(rect); } void paintEvent(QPaintEvent* event) { QGraphicsView::paintEvent(event); if (m_measureFps) { ++m_numPaintsSinceLastMeasure; ++m_numTotalPaints; } } void setWaitCursor() { m_mainWidget->setCursor(Qt::WaitCursor); } void resetCursor() { m_mainWidget->unsetCursor(); } public slots: void flip() { #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) QSizeF center = m_mainWidget->boundingRect().size() / 2; QPointF centerPoint = QPointF(center.width(), center.height()); m_mainWidget->setTransformOriginPoint(centerPoint); m_mainWidget->setRotation(m_mainWidget->rotation() ? 0 : 180); #endif } void animatedFlip() { #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) QSizeF center = m_mainWidget->boundingRect().size() / 2; QPointF centerPoint = QPointF(center.width(), center.height()); m_mainWidget->setTransformOriginPoint(centerPoint); QPropertyAnimation* animation = new QPropertyAnimation(m_mainWidget, "rotation", this); animation->setDuration(1000); int rotation = int(m_mainWidget->rotation()); animation->setStartValue(rotation); animation->setEndValue(rotation + 180 - (rotation % 180)); animation->start(QAbstractAnimation::DeleteWhenStopped); #endif } void animatedYFlip() { emit flipRequest(); } void printFps() { // note that this might have a bug if you measure right around midnight, but we can live with that QTime now = QTime::currentTime(); int msecs = m_startTime.msecsTo(now); int totalMsecs = m_totalStartTime.msecsTo(now); int totalFps = totalMsecs ? m_numTotalPaints * 1000 / totalMsecs : 0; int curFps = msecs ? m_numPaintsSinceLastMeasure * 1000 / msecs : 0; qDebug("[FPS] From start: %d, from last paint: %d", totalFps, curFps); m_startTime = now; m_numPaintsSinceLastMeasure = 0; } signals: void flipRequest(); private: QGraphicsWidget* m_mainWidget; bool m_measureFps; int m_numTotalPaints; int m_numPaintsSinceLastMeasure; QTime m_startTime; QTime m_totalStartTime; }; class SharedScene : public QSharedData { public: SharedScene() { m_scene = new QGraphicsScene; m_item = new WebView; m_item->setPage((m_page = new WebPage)); m_scene->addItem(m_item); m_scene->setActiveWindow(m_item); } ~SharedScene() { delete m_item; delete m_scene; delete m_page; } QGraphicsScene* scene() const { return m_scene; } WebView* webView() const { return m_item; } private: QGraphicsScene* m_scene; WebView* m_item; WebPage* m_page; }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QExplicitlySharedDataPointer other) : QMainWindow() , view(new MainView(this)) , scene(other) { init(); } MainWindow() : QMainWindow() , view(new MainView(this)) , scene(new SharedScene()) { init(); } void init() { setAttribute(Qt::WA_DeleteOnClose); view->setScene(scene->scene()); const QStringList arguments = QApplication::instance()->arguments(); const int indexOfViewportUpdateMode = arguments.indexOf("--updateMode"); if (indexOfViewportUpdateMode > 1 && indexOfViewportUpdateMode < arguments.count() - 1) { const QString updateMode = arguments[indexOfViewportUpdateMode+1] + "ViewportUpdate"; view->setViewportUpdateMode(static_cast(QGraphicsView::staticMetaObject.enumerator(QGraphicsView::staticMetaObject.indexOfEnumerator("ViewportUpdateMode")).keysToValue(updateMode.toAscii()))); } setCentralWidget(view); view->setMainWidget(scene->webView()); connect(scene->webView(), SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); connect(scene->webView(), SIGNAL(titleChanged(const QString&)), this, SLOT(setWindowTitle(const QString&))); connect(scene->webView()->page(), SIGNAL(windowCloseRequested()), this, SLOT(close())); #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) QStateMachine *machine = new QStateMachine(this); QState *s0 = new QState(machine); s0->assignProperty(scene->webView(), "yRotation", 0); QState *s1 = new QState(machine); s1->assignProperty(scene->webView(), "yRotation", 90); QAbstractTransition *t1 = s0->addTransition(view, SIGNAL(flipRequest()), s1); QPropertyAnimation *yRotationAnim = new QPropertyAnimation(scene->webView(), "yRotation", this); yRotationAnim->setDuration(1000); t1->addAnimation(yRotationAnim); QState *s2 = new QState(machine); s2->assignProperty(scene->webView(), "yRotation", -90); s1->addTransition(s1, SIGNAL(propertiesAssigned()), s2); QAbstractTransition *t2 = s2->addTransition(s0); t2->addAnimation(yRotationAnim); machine->setInitialState(s0); machine->start(); #endif resize(640, 480); buildUI(); } void load(const QString& url) { QUrl deducedUrl = urlFromUserInput(url); if (!deducedUrl.isValid()) deducedUrl = QUrl("http://" + url + "/"); loadURL(deducedUrl); } QWebPage* page() const { return scene->webView()->page(); } protected slots: void openFile() { static const QString filter("HTML Files (*.htm *.html);;Text Files (*.txt);;Image Files (*.gif *.jpg *.png);;All Files (*)"); QFileDialog fileDialog(this, tr("Open"), QString(), filter); fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setOptions(QFileDialog::ReadOnly); if (fileDialog.exec()) { QString selectedFile = fileDialog.selectedFiles()[0]; if (!selectedFile.isEmpty()) loadURL(QUrl::fromLocalFile(selectedFile)); } } void changeLocation() { load(urlEdit->text()); } void loadFinished(bool) { QUrl url = scene->webView()->url(); urlEdit->setText(url.toString()); QUrl::FormattingOptions opts; opts |= QUrl::RemoveScheme; opts |= QUrl::RemoveUserInfo; opts |= QUrl::StripTrailingSlash; QString s = url.toString(opts); s = s.mid(2); if (s.isEmpty()) return; //FIXME: something missing here } public slots: void newWindow(const QString &url = QString()) { MainWindow* mw = new MainWindow(); mw->load(url); mw->show(); } void clone() { MainWindow* mw = new MainWindow(scene); mw->show(); } void setWaitCursor() { view->setWaitCursor(); } void resetCursor() { view->resetCursor(); } void flip() { view->flip(); } void animatedFlip() { view->animatedFlip(); } void animatedYFlip() { view->animatedYFlip(); } private: void loadURL(const QUrl& url) { if (!url.isValid()) return; urlEdit->setText(url.toString()); scene->webView()->load(url); scene->webView()->setFocus(Qt::OtherFocusReason); } void buildUI() { QWebPage* page = scene->webView()->page(); urlEdit = new QLineEdit(this); urlEdit->setSizePolicy(QSizePolicy::Expanding, urlEdit->sizePolicy().verticalPolicy()); connect(urlEdit, SIGNAL(returnPressed()), SLOT(changeLocation())); QToolBar* bar = addToolBar("Navigation"); bar->addAction(page->action(QWebPage::Back)); bar->addAction(page->action(QWebPage::Forward)); bar->addAction(page->action(QWebPage::Reload)); bar->addAction(page->action(QWebPage::Stop)); bar->addWidget(urlEdit); QMenu* fileMenu = menuBar()->addMenu("&File"); fileMenu->addAction("New Window", this, SLOT(newWindow()), QKeySequence::New); fileMenu->addAction("Open File...", this, SLOT(openFile()), QKeySequence::Open); fileMenu->addAction("Clone Window", this, SLOT(clone())); fileMenu->addAction("Close Window", this, SLOT(close()), QKeySequence::Close); fileMenu->addSeparator(); fileMenu->addAction("Quit", QApplication::instance(), SLOT(closeAllWindows()), QKeySequence(Qt::CTRL | Qt::Key_Q)); QMenu* viewMenu = menuBar()->addMenu("&View"); viewMenu->addAction(page->action(QWebPage::Stop)); viewMenu->addAction(page->action(QWebPage::Reload)); QMenu* testMenu = menuBar()->addMenu("&Tests"); testMenu->addAction("Set Wait Cursor", this, SLOT(setWaitCursor()), QKeySequence("Ctrl+W")); testMenu->addAction("Reset Cursor", this, SLOT(resetCursor()), QKeySequence("Ctrl+Shift+W")); QMenu* fxMenu = menuBar()->addMenu("&Effects"); fxMenu->addAction("Flip", this, SLOT(flip())); fxMenu->addAction("Animated Flip", this, SLOT(animatedFlip()), QKeySequence("Ctrl+R")); fxMenu->addAction("Animated Y-Flip", this, SLOT(animatedYFlip()), QKeySequence("Ctrl+Y")); } private: MainView* view; QExplicitlySharedDataPointer scene; QLineEdit* urlEdit; }; QWebPage* WebPage::createWindow(QWebPage::WebWindowType) { MainWindow* mw = new MainWindow; mw->show(); return mw->page(); } int main(int argc, char** argv) { QApplication app(argc, argv); if (app.arguments().contains("--help")) { qDebug() << "Usage: QGVLauncher [--url url] [--compositing] [--updateMode Full|Minimal|Smart|No|BoundingRect] [--cacheWebView]\n"; return 0; } QString url = QString("file://%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html")); app.setApplicationName("GQVLauncher"); QWebSettings::setObjectCacheCapacities((16 * 1024 * 1024) / 8, (16 * 1024 * 1024) / 8, 16 * 1024 * 1024); QWebSettings::setMaximumPagesInCache(4); QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true); QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); QWebSettings::enablePersistentStorage(); const QStringList args = app.arguments(); const int indexOfUrl = args.indexOf("--url"); if (indexOfUrl > 0 && indexOfUrl < args.count() - 1) url = args.at(indexOfUrl+1); else if (args.count() > 1) url = args.at(1); if (args.contains("--compositing")) QWebSettings::globalSettings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true); MainWindow* window = new MainWindow; window->load(url); for (int i = 2; i < args.count(); ++i) if (!args.at(i).startsWith("-") && !args.at(i - 1).startsWith("-")) window->newWindow(args.at(i)); window->show(); return app.exec(); } #include "main.moc"