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