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