blob: 0536af560a6868978c8d8fee25ac179c04f004e7 [file] [log] [blame]
/*
* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2006 George Staikos <staikos@kde.org>
* Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
* Copyright (C) 2006 Zack Rusin <zack@kde.org>
* Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
* Copyright (C) 2009 Kenneth Christiansen <kenneth@webkit.org>
* Copyright (C) 2009 Antonio Gomes <antonio.gomes@openbossa.org>
* Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
*
* 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 <QDebug>
#include <QFile>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QNetworkRequest>
#include <QTextStream>
#include <QVector>
#include <QtGui>
#include <QtNetwork/QNetworkProxy>
#include <cstdio>
#include <qwebelement.h>
#include <qwebframe.h>
#include <qgraphicswebview.h>
#include <qwebpage.h>
#include <qwebsettings.h>
#include <qwebview.h>
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<SharedScene> 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::ViewportUpdateMode>(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<SharedScene> 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"