/*
* 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"