/* * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2009 Girish Ramakrishnan * Copyright (C) 2006 George Staikos * Copyright (C) 2006 Dirk Mueller * Copyright (C) 2006 Zack Rusin * Copyright (C) 2006 Simon Hausmann * * 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 #if !defined(QT_NO_PRINTER) #include #endif #ifndef QT_NO_UITOOLS #include #endif #include #include #include "mainwindow.h" #include #include #include #include #include #ifdef Q_WS_MAEMO_5 #include #endif #include "urlloader.h" #include "utils.h" #include "webinspector.h" #include "webpage.h" #include "webview.h" #ifdef Q_WS_MAEMO_5 #include #include #undef KeyPress #endif #ifndef NDEBUG void QWEBKIT_EXPORT qt_drt_garbageCollector_collect(); #endif class LauncherWindow : public MainWindow { Q_OBJECT public: LauncherWindow(QString url = QString()); virtual ~LauncherWindow(); virtual void keyPressEvent(QKeyEvent* event); void grabZoomKeys(bool grab); #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) void sendTouchEvent(); bool eventFilter(QObject* obj, QEvent* event); #endif QWebView* webView() const { return view; } protected slots: void loadStarted(); void loadFinished(); void showLinkHover(const QString &link, const QString &toolTip); void zoomIn(); void zoomOut(); void resetZoom(); void toggleZoomTextOnly(bool on); void print(); void screenshot(); void setEditable(bool on); /* void dumpPlugins() */ void dumpHtml(); void selectElements(); void setTouchMocking(bool on); public slots: void newWindow(const QString& url = QString()); private: // create the status bar, tool bar & menu void setupUI(); private: QVector zoomLevels; int currentZoom; QWebView* view; WebInspector* inspector; QAction* formatMenuAction; #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) QList touchPoints; bool touchMocking; #endif }; LauncherWindow::LauncherWindow(QString url) : MainWindow(url) , currentZoom(100) { QSplitter* splitter = new QSplitter(Qt::Vertical, this); setCentralWidget(splitter); view = new WebViewTraditional(splitter); view->setPage(page()); #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) view->installEventFilter(this); touchMocking = false; #endif connect(page(), SIGNAL(loadStarted()), this, SLOT(loadStarted())); connect(page(), SIGNAL(loadFinished(bool)), this, SLOT(loadFinished())); connect(page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), this, SLOT(showLinkHover(const QString&, const QString&))); inspector = new WebInspector(splitter); inspector->setPage(page()); inspector->hide(); connect(this, SIGNAL(destroyed()), inspector, SLOT(deleteLater())); setupUI(); // the zoom values are chosen to be like in Mozilla Firefox 3 zoomLevels << 30 << 50 << 67 << 80 << 90; zoomLevels << 100; zoomLevels << 110 << 120 << 133 << 150 << 170 << 200 << 240 << 300; grabZoomKeys(true); load(url); } LauncherWindow::~LauncherWindow() { grabZoomKeys(false); } void LauncherWindow::keyPressEvent(QKeyEvent* event) { #ifdef Q_WS_MAEMO_5 switch (event->key()) { case Qt::Key_F7: zoomIn(); event->accept(); break; case Qt::Key_F8: zoomOut(); event->accept(); break; } #endif MainWindow::keyPressEvent(event); } void LauncherWindow::grabZoomKeys(bool grab) { #ifdef Q_WS_MAEMO_5 if (!winId()) { qWarning("Can't grab keys unless we have a window id"); return; } Atom atom = XInternAtom(QX11Info::display(), "_HILDON_ZOOM_KEY_ATOM", False); if (!atom) { qWarning("Unable to obtain _HILDON_ZOOM_KEY_ATOM"); return; } unsigned long val = (grab) ? 1 : 0; XChangeProperty(QX11Info::display(), winId(), atom, XA_INTEGER, 32, PropModeReplace, reinterpret_cast(&val), 1); #endif } #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) void LauncherWindow::sendTouchEvent() { if (touchPoints.isEmpty()) return; QEvent::Type type = QEvent::TouchUpdate; if (touchPoints.size() == 1) { if (touchPoints[0].state() == Qt::TouchPointReleased) type = QEvent::TouchEnd; else if (touchPoints[0].state() == Qt::TouchPointPressed) type = QEvent::TouchBegin; } QTouchEvent touchEv(type); touchEv.setTouchPoints(touchPoints); QCoreApplication::sendEvent(page(), &touchEv); // After sending the event, remove all touchpoints that were released if (touchPoints[0].state() == Qt::TouchPointReleased) touchPoints.removeAt(0); if (touchPoints.size() > 1 && touchPoints[1].state() == Qt::TouchPointReleased) touchPoints.removeAt(1); } bool LauncherWindow::eventFilter(QObject* obj, QEvent* event) { if (!touchMocking || obj != view) return QObject::eventFilter(obj, event); if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick || event->type() == QEvent::MouseMove) { QMouseEvent* ev = static_cast(event); if (ev->type() == QEvent::MouseMove && !(ev->buttons() & Qt::LeftButton)) return false; QTouchEvent::TouchPoint touchPoint; touchPoint.setState(Qt::TouchPointMoved); if ((ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick)) touchPoint.setState(Qt::TouchPointPressed); else if (ev->type() == QEvent::MouseButtonRelease) touchPoint.setState(Qt::TouchPointReleased); touchPoint.setId(0); touchPoint.setScreenPos(ev->globalPos()); touchPoint.setPos(ev->pos()); touchPoint.setPressure(1); // If the point already exists, update it. Otherwise create it. if (touchPoints.size() > 0 && !touchPoints[0].id()) touchPoints[0] = touchPoint; else if (touchPoints.size() > 1 && !touchPoints[1].id()) touchPoints[1] = touchPoint; else touchPoints.append(touchPoint); sendTouchEvent(); } else if (event->type() == QEvent::KeyPress && static_cast(event)->key() == Qt::Key_F && static_cast(event)->modifiers() == Qt::ControlModifier) { // If the keyboard point is already pressed, release it. // Otherwise create it and append to touchPoints. if (touchPoints.size() > 0 && touchPoints[0].id() == 1) { touchPoints[0].setState(Qt::TouchPointReleased); sendTouchEvent(); } else if (touchPoints.size() > 1 && touchPoints[1].id() == 1) { touchPoints[1].setState(Qt::TouchPointReleased); sendTouchEvent(); } else { QTouchEvent::TouchPoint touchPoint; touchPoint.setState(Qt::TouchPointPressed); touchPoint.setId(1); touchPoint.setScreenPos(QCursor::pos()); touchPoint.setPos(view->mapFromGlobal(QCursor::pos())); touchPoint.setPressure(1); touchPoints.append(touchPoint); sendTouchEvent(); // After sending the event, change the touchpoint state to stationary touchPoints.last().setState(Qt::TouchPointStationary); } } return false; } #endif // QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) void LauncherWindow::loadStarted() { view->setFocus(Qt::OtherFocusReason); } void LauncherWindow::loadFinished() { QUrl url = page()->mainFrame()->url(); setAddressUrl(url.toString()); addCompleterEntry(url); } void LauncherWindow::showLinkHover(const QString &link, const QString &toolTip) { #ifndef Q_WS_MAEMO_5 statusBar()->showMessage(link); #endif #ifndef QT_NO_TOOLTIP if (!toolTip.isEmpty()) QToolTip::showText(QCursor::pos(), toolTip); #endif } void LauncherWindow::zoomIn() { int i = zoomLevels.indexOf(currentZoom); Q_ASSERT(i >= 0); if (i < zoomLevels.count() - 1) currentZoom = zoomLevels[i + 1]; page()->mainFrame()->setZoomFactor(qreal(currentZoom) / 100.0); } void LauncherWindow::zoomOut() { int i = zoomLevels.indexOf(currentZoom); Q_ASSERT(i >= 0); if (i > 0) currentZoom = zoomLevels[i - 1]; page()->mainFrame()->setZoomFactor(qreal(currentZoom) / 100.0); } void LauncherWindow::resetZoom() { currentZoom = 100; page()->mainFrame()->setZoomFactor(1.0); } void LauncherWindow::toggleZoomTextOnly(bool b) { page()->settings()->setAttribute(QWebSettings::ZoomTextOnly, b); } void LauncherWindow::print() { #if !defined(QT_NO_PRINTER) QPrintPreviewDialog dlg(this); connect(&dlg, SIGNAL(paintRequested(QPrinter*)), view, SLOT(print(QPrinter*))); dlg.exec(); #endif } void LauncherWindow::screenshot() { QPixmap pixmap = QPixmap::grabWidget(view); QLabel* label = new QLabel; label->setAttribute(Qt::WA_DeleteOnClose); label->setWindowTitle("Screenshot - Preview"); label->setPixmap(pixmap); label->show(); QString fileName = QFileDialog::getSaveFileName(label, "Screenshot"); if (!fileName.isEmpty()) { pixmap.save(fileName, "png"); label->setWindowTitle(QString("Screenshot - Saved at %1").arg(fileName)); } } void LauncherWindow::setEditable(bool on) { view->page()->setContentEditable(on); formatMenuAction->setVisible(on); } /* void LauncherWindow::dumpPlugins() { QList plugins = QWebSettings::pluginDatabase()->plugins(); foreach (const QWebPluginInfo plugin, plugins) { qDebug() << "Plugin:" << plugin.name(); foreach (const QWebPluginInfo::MimeType mime, plugin.mimeTypes()) { qDebug() << " " << mime.name; } } } */ void LauncherWindow::dumpHtml() { qDebug() << "HTML: " << page()->mainFrame()->toHtml(); } void LauncherWindow::selectElements() { bool ok; QString str = QInputDialog::getText(this, "Select elements", "Choose elements", QLineEdit::Normal, "a", &ok); if (ok && !str.isEmpty()) { QWebElementCollection result = page()->mainFrame()->findAllElements(str); foreach (QWebElement e, result) e.setStyleProperty("background-color", "yellow"); #ifndef Q_WS_MAEMO_5 statusBar()->showMessage(QString("%1 element(s) selected").arg(result.count()), 5000); #endif } } void LauncherWindow::setTouchMocking(bool on) { #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) touchMocking = on; #endif } void LauncherWindow::newWindow(const QString& url) { LauncherWindow* mw = new LauncherWindow(url); mw->show(); } void LauncherWindow::setupUI() { QMenu* fileMenu = menuBar()->addMenu("&File"); fileMenu->addAction("New Window", this, SLOT(newWindow()), QKeySequence::New); fileMenu->addAction(tr("Open File..."), this, SLOT(openFile()), QKeySequence::Open); fileMenu->addAction("Close Window", this, SLOT(close()), QKeySequence::Close); fileMenu->addSeparator(); fileMenu->addAction("Take Screen Shot...", this, SLOT(screenshot())); fileMenu->addAction(tr("Print..."), this, SLOT(print()), QKeySequence::Print); fileMenu->addSeparator(); fileMenu->addAction("Quit", QApplication::instance(), SLOT(closeAllWindows()), QKeySequence(Qt::CTRL | Qt::Key_Q)); QMenu* editMenu = menuBar()->addMenu("&Edit"); editMenu->addAction(page()->action(QWebPage::Undo)); editMenu->addAction(page()->action(QWebPage::Redo)); editMenu->addSeparator(); editMenu->addAction(page()->action(QWebPage::Cut)); editMenu->addAction(page()->action(QWebPage::Copy)); editMenu->addAction(page()->action(QWebPage::Paste)); editMenu->addSeparator(); QAction* setEditable = editMenu->addAction("Set Editable", this, SLOT(setEditable(bool))); setEditable->setCheckable(true); QMenu* viewMenu = menuBar()->addMenu("&View"); viewMenu->addAction(view->pageAction(QWebPage::Stop)); viewMenu->addAction(view->pageAction(QWebPage::Reload)); viewMenu->addSeparator(); QAction* zoomIn = viewMenu->addAction("Zoom &In", this, SLOT(zoomIn())); QAction* zoomOut = viewMenu->addAction("Zoom &Out", this, SLOT(zoomOut())); QAction* resetZoom = viewMenu->addAction("Reset Zoom", this, SLOT(resetZoom())); QAction* zoomTextOnly = viewMenu->addAction("Zoom Text Only", this, SLOT(toggleZoomTextOnly(bool))); zoomTextOnly->setCheckable(true); zoomTextOnly->setChecked(false); viewMenu->addSeparator(); viewMenu->addAction("Dump HTML", this, SLOT(dumpHtml())); // viewMenu->addAction("Dump plugins", this, SLOT(dumpPlugins())); QMenu* formatMenu = new QMenu("F&ormat", this); formatMenuAction = menuBar()->addMenu(formatMenu); formatMenuAction->setVisible(false); formatMenu->addAction(page()->action(QWebPage::ToggleBold)); formatMenu->addAction(page()->action(QWebPage::ToggleItalic)); formatMenu->addAction(page()->action(QWebPage::ToggleUnderline)); QMenu* writingMenu = formatMenu->addMenu(tr("Writing Direction")); writingMenu->addAction(page()->action(QWebPage::SetTextDirectionDefault)); writingMenu->addAction(page()->action(QWebPage::SetTextDirectionLeftToRight)); writingMenu->addAction(page()->action(QWebPage::SetTextDirectionRightToLeft)); zoomIn->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Plus)); zoomOut->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Minus)); resetZoom->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0)); QMenu* toolsMenu = menuBar()->addMenu("&Develop"); toolsMenu->addAction("Select Elements...", this, SLOT(selectElements())); QAction* showInspectorAction = toolsMenu->addAction("Show Web Inspector", inspector, SLOT(setVisible(bool)), QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_I)); showInspectorAction->setCheckable(true); showInspectorAction->connect(inspector, SIGNAL(visibleChanged(bool)), SLOT(setChecked(bool))); #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) QAction* touchMockAction = toolsMenu->addAction("Toggle multitouch mocking", this, SLOT(setTouchMocking(bool))); touchMockAction->setCheckable(true); touchMockAction->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_T)); #endif } QWebPage* WebPage::createWindow(QWebPage::WebWindowType) { LauncherWindow* mw = new LauncherWindow; mw->show(); return mw->page(); } QObject* WebPage::createPlugin(const QString &classId, const QUrl&, const QStringList&, const QStringList&) { if (classId == "alien_QLabel") { QLabel* l = new QLabel; l->winId(); return l; } #ifndef QT_NO_UITOOLS QUiLoader loader; return loader.createWidget(classId, view()); #else Q_UNUSED(classId); return 0; #endif } int launcherMain(const QApplication& app) { #ifndef NDEBUG int retVal = app.exec(); qt_drt_garbageCollector_collect(); QWebSettings::clearMemoryCaches(); return retVal; #else return app.exec(); #endif } class LauncherApplication : public QApplication { Q_OBJECT public: LauncherApplication(int& argc, char** argv); QStringList urls() const { return m_urls; } bool isRobotized() const { return m_isRobotized; } private: void handleUserOptions(); void applyDefaultSettings(); QList enumToKeys(const QMetaObject o, const QString& name, const QString& strip); QString formatKeys(QList keys); private: bool m_isRobotized; QStringList m_urls; }; void LauncherApplication::applyDefaultSettings() { QWebSettings::setMaximumPagesInCache(4); QWebSettings::setObjectCacheCapacities((16*1024*1024) / 8, (16*1024*1024) / 8, 16*1024*1024); QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true); QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); QWebSettings::enablePersistentStorage(); } LauncherApplication::LauncherApplication(int& argc, char** argv) : QApplication(argc, argv) , m_isRobotized(false) { // To allow QWebInspector's configuration persistence setOrganizationName("Nokia"); setApplicationName("QtLauncher"); setApplicationVersion("0.1"); applyDefaultSettings(); handleUserOptions(); } QString LauncherApplication::formatKeys(QList keys) { QString result; for (int i = 0; i < keys.count() - 1; i++) result.append(keys.at(i) + "|"); result.append(keys.last()); return result; } QList LauncherApplication::enumToKeys(const QMetaObject o, const QString& name, const QString& strip) { QList list; int enumIndex = o.indexOfEnumerator(name.toLatin1().data()); QMetaEnum enumerator = o.enumerator(enumIndex); if (enumerator.isValid()) { for (int i = 0; i < enumerator.keyCount(); i++) { QString key(enumerator.valueToKey(i)); list.append(key.remove(strip)); } } return list; } static void fail(const QString& errorMsg) { qDebug() << "ERROR:" << errorMsg.toLatin1().data(); exit(1); } void LauncherApplication::handleUserOptions() { QStringList args = arguments(); QFileInfo program(args.at(0)); QString programName("QtLauncher"); if (program.exists()) programName = program.baseName(); QList updateModes(enumToKeys(QGraphicsView::staticMetaObject, "ViewportUpdateMode", "ViewportUpdate")); if (args.contains("--help")) { qDebug() << "Usage:" << programName.toLatin1().data() << "[--graphicsbased]" << "[--compositing]" << QString("[--viewport-update-mode %1]").arg(formatKeys(updateModes)).toLatin1().data() << "[--cache-webview]" << "[-r list]" << "URLs"; exit(0); } bool useGraphicsView = false; if (args.contains("--graphicsbased")) useGraphicsView = true; if (args.contains("--compositing") && useGraphicsView) QWebSettings::globalSettings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true); if (args.contains("--cache-webview") && useGraphicsView) ; // view->setCacheMode(QGraphicsItem::DeviceCoordinateCache); int modeIndex = args.indexOf("--viewport-update-mode"); if (modeIndex != -1 && ++modeIndex < args.count() && !args.at(modeIndex).startsWith("-")) { QString mode = args.takeAt(modeIndex); if (useGraphicsView) { int idx = updateModes.indexOf(mode); if (idx != -1) { ; // view->setViewportUpdateMode(static_cast(idx)); } else { fail(QString("--viewport-update-mode value has to be one of [%1]") .arg(formatKeys(updateModes)).toLatin1().data()); } } } int robotIndex = args.indexOf("-r"); if (robotIndex != -1) { if (++robotIndex < args.count() && !args.at(robotIndex).startsWith("-")) { QString listFile = args.takeAt(robotIndex); if (!QFile::exists(listFile)) fail(QString("The list file supplied to -r does not exist.")); else { m_isRobotized = true; m_urls = QStringList(listFile); return; } } else fail(QString("-r needs a list file to start in robotized mode")); } int lastArg = args.lastIndexOf(QRegExp("^-.*")); m_urls = (lastArg != -1) ? args.mid(++lastArg) : args.mid(1); } int main(int argc, char **argv) { LauncherApplication app(argc, argv); if (app.isRobotized()) { LauncherWindow* window = new LauncherWindow; QWebView* view = window->webView(); UrlLoader loader(view->page()->mainFrame(), app.urls().at(0)); QObject::connect(view->page()->mainFrame(), SIGNAL(loadFinished(bool)), &loader, SLOT(loadNext())); loader.loadNext(); window->show(); return launcherMain(app); } QStringList urls = app.urls(); if (urls.isEmpty()) { QString defaultUrl = QString("file://%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html")); if (QDir(defaultUrl).exists()) urls.append(defaultUrl); else urls.append(""); } LauncherWindow* window = 0; foreach (QString url, urls) { if (!window) window = new LauncherWindow(url); else window->newWindow(url); } window->show(); return launcherMain(app); } #include "main.moc"