| /* |
| * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this program; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| #include "qgraphicswkview.h" |
| |
| #include "ChunkedUpdateDrawingAreaProxy.h" |
| #include "IntSize.h" |
| #include "RunLoop.h" |
| #include "TiledDrawingAreaProxy.h" |
| #include "UpdateChunk.h" |
| #include "WKAPICast.h" |
| #include "qwkpage.h" |
| #include "qwkpage_p.h" |
| #include <QApplication> |
| #include <QCursor> |
| #include <QGraphicsSceneMouseEvent> |
| #include <QGraphicsView> |
| #include <QMenu> |
| #include <QPainter> |
| #include <QScrollBar> |
| #include <QStyleOptionGraphicsItem> |
| #include <QUrl> |
| #include <QtDebug> |
| #include <WebKit2/WKRetainPtr.h> |
| #include <wtf/RefPtr.h> |
| #include <wtf/text/WTFString.h> |
| |
| using namespace WebKit; |
| using namespace WebCore; |
| |
| struct QGraphicsWKViewPrivate { |
| QGraphicsWKViewPrivate(QGraphicsWKView* view); |
| WKPageRef pageRef() const { return page->pageRef(); } |
| |
| void onToolTipChanged(const QString&); |
| void onScaleChanged(); |
| void commitScale(); |
| |
| QGraphicsWKView* q; |
| QWKPage* page; |
| QSharedPointer<QMenu> activeMenu; |
| RunLoop::Timer<QGraphicsWKViewPrivate> m_scaleCommitTimer; |
| bool m_isChangingScale; |
| }; |
| |
| QGraphicsWKView::QGraphicsWKView(QWKContext* context, BackingStoreType backingStoreType, QGraphicsItem* parent) |
| : QGraphicsWidget(parent) |
| , d(new QGraphicsWKViewPrivate(this)) |
| { |
| setFocusPolicy(Qt::StrongFocus); |
| setAcceptHoverEvents(true); |
| |
| |
| #if ENABLE(TILED_BACKING_STORE) |
| if (backingStoreType == Tiled) |
| connect(this, SIGNAL(scaleChanged()), this, SLOT(onScaleChanged())); |
| #endif |
| |
| d->page = new QWKPage(context); |
| d->page->d->init(this, backingStoreType); |
| connect(d->page, SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString))); |
| connect(d->page, SIGNAL(loadStarted()), this, SIGNAL(loadStarted())); |
| connect(d->page, SIGNAL(loadFinished(bool)), this, SIGNAL(loadFinished(bool))); |
| connect(d->page, SIGNAL(loadProgress(int)), this, SIGNAL(loadProgress(int))); |
| connect(d->page, SIGNAL(initialLayoutCompleted()), this, SIGNAL(initialLayoutCompleted())); |
| connect(d->page, SIGNAL(urlChanged(const QUrl&)), this, SIGNAL(urlChanged(const QUrl&))); |
| connect(d->page, SIGNAL(cursorChanged(const QCursor&)), this, SLOT(updateCursor(const QCursor&))); |
| connect(d->page, SIGNAL(focusNextPrevChild(bool)), this, SLOT(focusNextPrevChildCallback(bool))); |
| connect(d->page, SIGNAL(showContextMenu(QSharedPointer<QMenu>)), this, SLOT(showContextMenu(QSharedPointer<QMenu>))); |
| connect(d->page, SIGNAL(toolTipChanged(QString)), this, SLOT(onToolTipChanged(QString))); |
| } |
| |
| QGraphicsWKView::~QGraphicsWKView() |
| { |
| delete d->page; |
| delete d; |
| } |
| |
| QWKPage* QGraphicsWKView::page() const |
| { |
| return d->page; |
| } |
| |
| void QGraphicsWKView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*) |
| { |
| page()->d->paint(painter, option->exposedRect.toAlignedRect()); |
| } |
| |
| void QGraphicsWKView::setGeometry(const QRectF& rect) |
| { |
| QSizeF oldSize = geometry().size(); |
| QGraphicsWidget::setGeometry(rect); |
| if (geometry().size() == oldSize) |
| return; |
| |
| // NOTE: call geometry() as setGeometry ensures that |
| // the geometry is within legal bounds (minimumSize, maximumSize) |
| page()->setViewportSize(geometry().size().toSize()); |
| } |
| |
| void QGraphicsWKView::load(const QUrl& url) |
| { |
| page()->load(url); |
| } |
| |
| void QGraphicsWKView::setUrl(const QUrl& url) |
| { |
| page()->setUrl(url); |
| } |
| |
| QUrl QGraphicsWKView::url() const |
| { |
| return page()->url(); |
| } |
| |
| QString QGraphicsWKView::title() const |
| { |
| return page()->title(); |
| } |
| |
| void QGraphicsWKView::triggerPageAction(QWKPage::WebAction action, bool checked) |
| { |
| page()->triggerAction(action, checked); |
| } |
| |
| void QGraphicsWKView::back() |
| { |
| page()->triggerAction(QWKPage::Back); |
| } |
| |
| void QGraphicsWKView::forward() |
| { |
| page()->triggerAction(QWKPage::Forward); |
| } |
| |
| void QGraphicsWKView::reload() |
| { |
| page()->triggerAction(QWKPage::Reload); |
| } |
| |
| void QGraphicsWKView::stop() |
| { |
| page()->triggerAction(QWKPage::Stop); |
| } |
| |
| void QGraphicsWKView::updateCursor(const QCursor& cursor) |
| { |
| setCursor(cursor); |
| } |
| |
| class FriendlyWidget : public QWidget |
| { |
| public: |
| bool focusNextPrevChild(bool next); |
| }; |
| |
| void QGraphicsWKView::focusNextPrevChildCallback(bool next) |
| { |
| if (hasFocus()) { |
| // find the view which has the focus: |
| QList<QGraphicsView*> views = scene()->views(); |
| const int viewCount = views.count(); |
| QGraphicsView* focusedView = 0; |
| for (int i = 0; i < viewCount; ++i) { |
| if (views[i]->hasFocus()) { |
| focusedView = views[i]; |
| break; |
| } |
| } |
| |
| if (focusedView) { |
| QWidget* window = focusedView->window(); |
| FriendlyWidget* friendlyWindow = static_cast<FriendlyWidget*>(window); |
| friendlyWindow->focusNextPrevChild(next); |
| } |
| } |
| } |
| |
| /*! \reimp |
| */ |
| bool QGraphicsWKView::focusNextPrevChild(bool next) |
| { |
| QKeyEvent ev(QEvent::KeyPress, Qt::Key_Tab, Qt::KeyboardModifiers(next ? Qt::NoModifier : Qt::ShiftModifier)); |
| page()->d->keyPressEvent(&ev); |
| return true; |
| } |
| |
| /*! \reimp |
| */ |
| QVariant QGraphicsWKView::itemChange(GraphicsItemChange change, const QVariant& value) |
| { |
| // Here so that it can be reimplemented without breaking ABI. |
| return QGraphicsWidget::itemChange(change, value); |
| } |
| |
| /*! \reimp |
| */ |
| bool QGraphicsWKView::event(QEvent* event) |
| { |
| QEvent::Type eventType = event->type(); |
| switch (eventType) { |
| case QEvent::TouchBegin: |
| case QEvent::TouchEnd: |
| case QEvent::TouchUpdate: |
| touchEvent(static_cast<QTouchEvent*>(event)); |
| return true; |
| case QEvent::Show: |
| page()->d->page->drawingArea()->setPageIsVisible(true); |
| break; |
| case QEvent::Hide: |
| page()->d->page->drawingArea()->setPageIsVisible(false); |
| break; |
| default: |
| break; |
| } |
| |
| // Here so that it can be reimplemented without breaking ABI. |
| return QGraphicsWidget::event(event); |
| } |
| |
| /*! \reimp |
| */ |
| QSizeF QGraphicsWKView::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const |
| { |
| if (which == Qt::PreferredSize) |
| return QSizeF(800, 600); |
| return QGraphicsWidget::sizeHint(which, constraint); |
| } |
| |
| /*! \reimp |
| */ |
| QVariant QGraphicsWKView::inputMethodQuery(Qt::InputMethodQuery query) const |
| { |
| // implement |
| return QVariant(); |
| } |
| |
| /*! \reimp |
| */ |
| void QGraphicsWKView::keyPressEvent(QKeyEvent* ev) |
| { |
| page()->d->keyPressEvent(ev); |
| } |
| |
| /*! \reimp |
| */ |
| void QGraphicsWKView::keyReleaseEvent(QKeyEvent* ev) |
| { |
| page()->d->keyReleaseEvent(ev); |
| } |
| |
| void QGraphicsWKView::hoverMoveEvent(QGraphicsSceneHoverEvent* ev) |
| { |
| QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMouseMove); |
| me.setPos(ev->pos()); |
| me.setScreenPos(ev->screenPos()); |
| |
| page()->d->mouseMoveEvent(&me); |
| |
| if (!ev->isAccepted()) |
| QGraphicsItem::hoverMoveEvent(ev); |
| } |
| |
| void QGraphicsWKView::mouseMoveEvent(QGraphicsSceneMouseEvent* ev) |
| { |
| page()->d->mouseMoveEvent(ev); |
| if (!ev->isAccepted()) |
| QGraphicsItem::mouseMoveEvent(ev); |
| } |
| |
| void QGraphicsWKView::mousePressEvent(QGraphicsSceneMouseEvent* ev) |
| { |
| page()->d->mousePressEvent(ev); |
| if (!ev->isAccepted()) |
| QGraphicsItem::mousePressEvent(ev); |
| } |
| |
| void QGraphicsWKView::mouseReleaseEvent(QGraphicsSceneMouseEvent* ev) |
| { |
| page()->d->mouseReleaseEvent(ev); |
| if (!ev->isAccepted()) |
| QGraphicsItem::mouseReleaseEvent(ev); |
| } |
| |
| void QGraphicsWKView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev) |
| { |
| page()->d->mouseDoubleClickEvent(ev); |
| if (!ev->isAccepted()) |
| QGraphicsItem::mouseReleaseEvent(ev); |
| } |
| |
| void QGraphicsWKView::wheelEvent(QGraphicsSceneWheelEvent* ev) |
| { |
| page()->d->wheelEvent(ev); |
| if (!ev->isAccepted()) |
| QGraphicsItem::wheelEvent(ev); |
| } |
| |
| void QGraphicsWKView::touchEvent(QTouchEvent* ev) |
| { |
| page()->d->touchEvent(ev); |
| } |
| |
| void QGraphicsWKView::focusInEvent(QFocusEvent*) |
| { |
| page()->d->page->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive); |
| } |
| |
| void QGraphicsWKView::focusOutEvent(QFocusEvent*) |
| { |
| page()->d->page->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive); |
| } |
| |
| |
| /*! |
| This slot is called when the engine require a context sensitive menu to be displayed. |
| |
| The \a menu passed as a parameter is the menu to be displayed. It is populated with the |
| actions possible for its current position. The menu is empty if there is no action for the position. |
| */ |
| void QGraphicsWKView::showContextMenu(QSharedPointer<QMenu> menu) |
| { |
| // Remove the active menu in case this function is called twice. |
| if (d->activeMenu) |
| d->activeMenu->hide(); |
| |
| if (menu->isEmpty()) |
| return; |
| |
| d->activeMenu = menu; |
| |
| QWidget* view = 0; |
| if (QGraphicsScene* myScene = scene()) { |
| const QList<QGraphicsView*> views = myScene->views(); |
| for (unsigned i = 0; i < views.size(); ++i) { |
| if (views.at(i) == QApplication::focusWidget()) { |
| view = views.at(i); |
| break; |
| } |
| } |
| if (!view) |
| view = views.value(0, 0); |
| } |
| if (view) |
| menu->setParent(view, menu->windowFlags()); |
| menu->exec(view->mapToGlobal(menu->pos())); |
| if (d->activeMenu == menu) |
| d->activeMenu.clear(); |
| } |
| |
| void QGraphicsWKView::takeSnapshot(const QSize& size, const QRect& contentsRect) |
| { |
| #if ENABLE(TILED_BACKING_STORE) |
| DrawingAreaProxy* drawingArea = page()->d->page->drawingArea(); |
| if (drawingArea->type() != DrawingAreaTypeTiled) |
| return; |
| TiledDrawingAreaProxy* tiledDrawingArea = static_cast<TiledDrawingAreaProxy*>(drawingArea); |
| tiledDrawingArea->takeSnapshot(size, contentsRect); |
| #endif |
| } |
| |
| QGraphicsWKViewPrivate::QGraphicsWKViewPrivate(QGraphicsWKView* view) |
| : q(view) |
| , activeMenu(0) |
| , m_scaleCommitTimer(RunLoop::current(), this, &QGraphicsWKViewPrivate::commitScale) |
| , m_isChangingScale(false) |
| { |
| } |
| |
| QRectF QGraphicsWKView::visibleRect() const |
| { |
| if (!scene()) |
| return QRectF(); |
| |
| QList<QGraphicsView*> views = scene()->views(); |
| if (views.isEmpty()) |
| return QRectF(); |
| |
| QGraphicsView* graphicsView = views.at(0); |
| int xOffset = graphicsView->horizontalScrollBar()->value(); |
| int yOffset = graphicsView->verticalScrollBar()->value(); |
| return mapRectFromScene(QRectF(QPointF(xOffset, yOffset), graphicsView->viewport()->size())); |
| } |
| |
| void QGraphicsWKView::prepareScaleChange() |
| { |
| #if ENABLE(TILED_BACKING_STORE) |
| ASSERT(!d->m_isChangingScale); |
| d->m_isChangingScale = true; |
| d->m_scaleCommitTimer.stop(); |
| #endif |
| } |
| |
| void QGraphicsWKView::commitScaleChange() |
| { |
| #if ENABLE(TILED_BACKING_STORE) |
| ASSERT(d->m_isChangingScale); |
| d->m_isChangingScale = false; |
| d->commitScale(); |
| #endif |
| } |
| |
| void QGraphicsWKViewPrivate::onScaleChanged() |
| { |
| #if ENABLE(TILED_BACKING_STORE) |
| if (!m_isChangingScale) |
| m_scaleCommitTimer.startOneShot(0.1); |
| #endif |
| } |
| |
| void QGraphicsWKViewPrivate::onToolTipChanged(const QString& toolTip) |
| { |
| q->setToolTip(toolTip); |
| } |
| |
| void QGraphicsWKViewPrivate::commitScale() |
| { |
| #if ENABLE(TILED_BACKING_STORE) |
| DrawingAreaProxy* drawingArea = page->d->page->drawingArea(); |
| float newScale = q->scale(); |
| if (drawingArea->type() == DrawingAreaTypeTiled) { |
| TiledDrawingAreaProxy* tiledDrawingArea = static_cast<TiledDrawingAreaProxy*>(drawingArea); |
| if (tiledDrawingArea->contentsScale() == newScale) |
| return; |
| tiledDrawingArea->setContentsScale(newScale); |
| // For now we block until complete. |
| tiledDrawingArea->waitUntilUpdatesComplete(); |
| } |
| #endif |
| } |
| |
| #include "moc_qgraphicswkview.cpp" |