• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3  * Copyright (C) 2006 George Staikos <staikos@kde.org>
4  * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
5  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
6  * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
7  * Copyright (C) 2009 Kenneth Christiansen <kenneth@webkit.org>
8  * Copyright (C) 2009 Antonio Gomes <antonio.gomes@openbossa.org>
9  * Copyright (C) 2009 Girish Ramakrishnan <girish@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 
urlFromUserInput(const QString & string)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:
WebView(QGraphicsItem * parent=0)72     WebView(QGraphicsItem* parent = 0)
73         : QGraphicsWebView(parent)
74     {
75         if (QApplication::instance()->arguments().contains("--cacheWebView"))
76             setCacheMode(QGraphicsItem::DeviceCoordinateCache);
77     }
setYRotation(qreal angle)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     }
yRotation() const89     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:
WebPage(QObject * parent=0)102     WebPage(QObject* parent = 0)
103         : QWebPage(parent)
104     {
105         applyProxy();
106     }
107     virtual QWebPage* createWindow(QWebPage::WebWindowType);
108 
109 private:
applyProxy()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:
MainView(QWidget * parent)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 
setMainWidget(QGraphicsWidget * widget)146     void setMainWidget(QGraphicsWidget* widget)
147     {
148         QRectF rect(QRect(QPoint(0, 0), size()));
149         widget->setGeometry(rect);
150         m_mainWidget = widget;
151     }
152 
resizeEvent(QResizeEvent * event)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 
paintEvent(QPaintEvent * event)162     void paintEvent(QPaintEvent* event)
163     {
164         QGraphicsView::paintEvent(event);
165         if (m_measureFps) {
166             ++m_numPaintsSinceLastMeasure;
167             ++m_numTotalPaints;
168         }
169     }
170 
setWaitCursor()171     void setWaitCursor()
172     {
173         m_mainWidget->setCursor(Qt::WaitCursor);
174     }
175 
resetCursor()176     void resetCursor()
177     {
178         m_mainWidget->unsetCursor();
179     }
180 
181 public slots:
flip()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 
animatedFlip()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 
animatedYFlip()212     void animatedYFlip()
213     {
214         emit flipRequest();
215     }
216 
printFps()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:
SharedScene()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 
~SharedScene()254     ~SharedScene()
255     {
256         delete m_item;
257         delete m_scene;
258         delete m_page;
259     }
260 
scene() const261     QGraphicsScene* scene() const { return m_scene; }
webView() const262     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:
MainWindow(QExplicitlySharedDataPointer<SharedScene> other)274     MainWindow(QExplicitlySharedDataPointer<SharedScene> other)
275         : QMainWindow()
276         , view(new MainView(this))
277         , scene(other)
278     {
279         init();
280     }
281 
MainWindow()282     MainWindow()
283         : QMainWindow()
284         , view(new MainView(this))
285         , scene(new SharedScene())
286     {
287         init();
288     }
289 
init()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 
load(const QString & url)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 
page() const347     QWebPage* page() const
348     {
349         return scene->webView()->page();
350     }
351 
352 protected slots:
353 
openFile()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 
changeLocation()370     void changeLocation()
371     {
372         load(urlEdit->text());
373     }
374 
loadFinished(bool)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:
newWindow(const QString & url=QString ())392     void newWindow(const QString &url = QString())
393     {
394         MainWindow* mw = new MainWindow();
395         mw->load(url);
396         mw->show();
397     }
398 
clone()399     void clone()
400     {
401         MainWindow* mw = new MainWindow(scene);
402         mw->show();
403     }
404 
setWaitCursor()405     void setWaitCursor()
406     {
407         view->setWaitCursor();
408     }
409 
resetCursor()410     void resetCursor()
411     {
412         view->resetCursor();
413     }
414 
flip()415     void flip()
416     {
417         view->flip();
418     }
419 
animatedFlip()420     void animatedFlip()
421     {
422         view->animatedFlip();
423     }
424 
animatedYFlip()425     void animatedYFlip()
426     {
427         view->animatedYFlip();
428     }
429 
430 private:
431 
loadURL(const QUrl & url)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 
buildUI()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 
createWindow(QWebPage::WebWindowType)485 QWebPage* WebPage::createWindow(QWebPage::WebWindowType)
486 {
487     MainWindow* mw = new MainWindow;
488     mw->show();
489     return mw->page();
490 }
491 
main(int argc,char ** argv)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