qgraphicswkview.cpp revision 65f03d4f644ce73618e5f4f50dd694b26f55ae12
1/*
2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "qgraphicswkview.h"
22
23#include "ChunkedUpdateDrawingAreaProxy.h"
24#include "IntSize.h"
25#include "RunLoop.h"
26#include "TiledDrawingAreaProxy.h"
27#include "UpdateChunk.h"
28#include "WKAPICast.h"
29#include "qwkpage.h"
30#include "qwkpage_p.h"
31#include <QApplication>
32#include <QCursor>
33#include <QGraphicsSceneMouseEvent>
34#include <QGraphicsView>
35#include <QMenu>
36#include <QPainter>
37#include <QScrollBar>
38#include <QStyleOptionGraphicsItem>
39#include <QUrl>
40#include <QtDebug>
41#include <WebKit2/WKRetainPtr.h>
42#include <wtf/RefPtr.h>
43#include <wtf/text/WTFString.h>
44
45using namespace WebKit;
46using namespace WebCore;
47
48struct QGraphicsWKViewPrivate {
49    QGraphicsWKViewPrivate(QGraphicsWKView* view);
50    WKPageRef pageRef() const { return page->pageRef(); }
51
52    void onScaleChanged();
53    void commitScale();
54
55    QGraphicsWKView* q;
56    QWKPage* page;
57    QMenu* activeMenu;
58    RunLoop::Timer<QGraphicsWKViewPrivate> m_scaleCommitTimer;
59    bool m_isChangingScale;
60};
61
62QGraphicsWKView::QGraphicsWKView(QWKContext* context, BackingStoreType backingStoreType, QGraphicsItem* parent)
63    : QGraphicsWidget(parent)
64    , d(new QGraphicsWKViewPrivate(this))
65{
66    setFocusPolicy(Qt::StrongFocus);
67    setAcceptHoverEvents(true);
68
69    PassOwnPtr<DrawingAreaProxy> drawingAreaProxy;
70
71    d->page = new QWKPage(context);
72
73    switch (backingStoreType) {
74#if ENABLE(TILED_BACKING_STORE)
75    case Tiled:
76        drawingAreaProxy = TiledDrawingAreaProxy::create(this, toImpl(page()->pageRef()));
77        connect(this, SIGNAL(scaleChanged()), this, SLOT(onScaleChanged()));
78        break;
79#endif
80    case Simple:
81    default:
82        drawingAreaProxy = ChunkedUpdateDrawingAreaProxy::create(this, toImpl(page()->pageRef()));
83        break;
84    }
85
86    d->page->d->init(this, drawingAreaProxy);
87    connect(d->page, SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString)));
88    connect(d->page, SIGNAL(loadStarted()), this, SIGNAL(loadStarted()));
89    connect(d->page, SIGNAL(loadFinished(bool)), this, SIGNAL(loadFinished(bool)));
90    connect(d->page, SIGNAL(loadProgress(int)), this, SIGNAL(loadProgress(int)));
91    connect(d->page, SIGNAL(initialLayoutCompleted()), this, SIGNAL(initialLayoutCompleted()));
92    connect(d->page, SIGNAL(urlChanged(const QUrl&)), this, SIGNAL(urlChanged(const QUrl&)));
93    connect(d->page, SIGNAL(cursorChanged(const QCursor&)), this, SLOT(updateCursor(const QCursor&)));
94    connect(d->page, SIGNAL(focusNextPrevChild(bool)), this, SLOT(focusNextPrevChildCallback(bool)));
95    connect(d->page, SIGNAL(showContextMenu(QMenu*)), this, SLOT(showContextMenu(QMenu*)));
96}
97
98QGraphicsWKView::~QGraphicsWKView()
99{
100    delete d->page;
101    delete d;
102}
103
104QWKPage* QGraphicsWKView::page() const
105{
106    return d->page;
107}
108
109void QGraphicsWKView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*)
110{
111    page()->d->paint(painter, option->exposedRect.toAlignedRect());
112}
113
114void QGraphicsWKView::setGeometry(const QRectF& rect)
115{
116    QSizeF oldSize = geometry().size();
117    QGraphicsWidget::setGeometry(rect);
118    if (geometry().size() == oldSize)
119        return;
120
121    // NOTE: call geometry() as setGeometry ensures that
122    // the geometry is within legal bounds (minimumSize, maximumSize)
123    page()->setViewportSize(geometry().size().toSize());
124}
125
126void QGraphicsWKView::load(const QUrl& url)
127{
128    page()->load(url);
129}
130
131void QGraphicsWKView::setUrl(const QUrl& url)
132{
133    page()->setUrl(url);
134}
135
136QUrl QGraphicsWKView::url() const
137{
138    return page()->url();
139}
140
141QString QGraphicsWKView::title() const
142{
143    return page()->title();
144}
145
146void QGraphicsWKView::triggerPageAction(QWKPage::WebAction action, bool checked)
147{
148    page()->triggerAction(action, checked);
149}
150
151void QGraphicsWKView::back()
152{
153    page()->triggerAction(QWKPage::Back);
154}
155
156void QGraphicsWKView::forward()
157{
158    page()->triggerAction(QWKPage::Forward);
159}
160
161void QGraphicsWKView::reload()
162{
163    page()->triggerAction(QWKPage::Reload);
164}
165
166void QGraphicsWKView::stop()
167{
168    page()->triggerAction(QWKPage::Stop);
169}
170
171void QGraphicsWKView::updateCursor(const QCursor& cursor)
172{
173    setCursor(cursor);
174}
175
176class FriendlyWidget : public QWidget
177{
178public:
179    bool focusNextPrevChild(bool next);
180};
181
182void QGraphicsWKView::focusNextPrevChildCallback(bool next)
183{
184    if (hasFocus()) {
185        // find the view which has the focus:
186        QList<QGraphicsView*> views = scene()->views();
187        const int viewCount = views.count();
188        QGraphicsView* focusedView = 0;
189        for (int i = 0; i < viewCount; ++i) {
190            if (views[i]->hasFocus()) {
191                focusedView = views[i];
192                break;
193            }
194        }
195
196        if (focusedView) {
197            QWidget* window = focusedView->window();
198            FriendlyWidget* friendlyWindow = static_cast<FriendlyWidget*>(window);
199            friendlyWindow->focusNextPrevChild(next);
200        }
201    }
202}
203
204/*! \reimp
205*/
206bool QGraphicsWKView::focusNextPrevChild(bool next)
207{
208    QKeyEvent ev(QEvent::KeyPress, Qt::Key_Tab, Qt::KeyboardModifiers(next ? Qt::NoModifier : Qt::ShiftModifier));
209    page()->d->keyPressEvent(&ev);
210    return true;
211}
212
213/*! \reimp
214*/
215QVariant QGraphicsWKView::itemChange(GraphicsItemChange change, const QVariant& value)
216{
217    // Here so that it can be reimplemented without breaking ABI.
218    return QGraphicsWidget::itemChange(change, value);
219}
220
221/*! \reimp
222*/
223bool QGraphicsWKView::event(QEvent* event)
224{
225    QEvent::Type eventType = event->type();
226    switch (eventType) {
227    case QEvent::TouchBegin:
228    case QEvent::TouchEnd:
229    case QEvent::TouchUpdate:
230        touchEvent(static_cast<QTouchEvent*>(event));
231        return true;
232    case QEvent::Show:
233        page()->d->page->drawingArea()->setPageIsVisible(true);
234        break;
235    case QEvent::Hide:
236        page()->d->page->drawingArea()->setPageIsVisible(false);
237        break;
238    default:
239        break;
240    }
241
242    // Here so that it can be reimplemented without breaking ABI.
243    return QGraphicsWidget::event(event);
244}
245
246/*! \reimp
247*/
248QSizeF QGraphicsWKView::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const
249{
250    if (which == Qt::PreferredSize)
251        return QSizeF(800, 600);
252    return QGraphicsWidget::sizeHint(which, constraint);
253}
254
255/*! \reimp
256*/
257QVariant QGraphicsWKView::inputMethodQuery(Qt::InputMethodQuery query) const
258{
259    // implement
260    return QVariant();
261}
262
263/*! \reimp
264*/
265void QGraphicsWKView::keyPressEvent(QKeyEvent* ev)
266{
267    page()->d->keyPressEvent(ev);
268}
269
270/*! \reimp
271*/
272void QGraphicsWKView::keyReleaseEvent(QKeyEvent* ev)
273{
274    page()->d->keyReleaseEvent(ev);
275}
276
277void QGraphicsWKView::hoverMoveEvent(QGraphicsSceneHoverEvent* ev)
278{
279    QGraphicsSceneMouseEvent me(QEvent::GraphicsSceneMouseMove);
280    me.setPos(ev->pos());
281    me.setScreenPos(ev->screenPos());
282
283    page()->d->mouseMoveEvent(&me);
284
285    if (!ev->isAccepted())
286        QGraphicsItem::hoverMoveEvent(ev);
287}
288
289void QGraphicsWKView::mouseMoveEvent(QGraphicsSceneMouseEvent* ev)
290{
291    page()->d->mouseMoveEvent(ev);
292    if (!ev->isAccepted())
293        QGraphicsItem::mouseMoveEvent(ev);
294}
295
296void QGraphicsWKView::mousePressEvent(QGraphicsSceneMouseEvent* ev)
297{
298    page()->d->mousePressEvent(ev);
299    if (!ev->isAccepted())
300        QGraphicsItem::mousePressEvent(ev);
301}
302
303void QGraphicsWKView::mouseReleaseEvent(QGraphicsSceneMouseEvent* ev)
304{
305    page()->d->mouseReleaseEvent(ev);
306    if (!ev->isAccepted())
307        QGraphicsItem::mouseReleaseEvent(ev);
308}
309
310void QGraphicsWKView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev)
311{
312    page()->d->mouseDoubleClickEvent(ev);
313    if (!ev->isAccepted())
314        QGraphicsItem::mouseReleaseEvent(ev);
315}
316
317void QGraphicsWKView::wheelEvent(QGraphicsSceneWheelEvent* ev)
318{
319    page()->d->wheelEvent(ev);
320    if (!ev->isAccepted())
321        QGraphicsItem::wheelEvent(ev);
322}
323
324void QGraphicsWKView::touchEvent(QTouchEvent* ev)
325{
326    page()->d->touchEvent(ev);
327}
328
329void QGraphicsWKView::focusInEvent(QFocusEvent*)
330{
331    page()->d->page->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
332}
333
334void QGraphicsWKView::focusOutEvent(QFocusEvent*)
335{
336    page()->d->page->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
337}
338
339void QGraphicsWKView::showContextMenu(QMenu* menu)
340{
341    // Remove the active menu in case this function is called twice.
342    if (d->activeMenu)
343        d->activeMenu->hide();
344
345    d->activeMenu = menu;
346
347    QWidget* view = 0;
348    if (QGraphicsScene* myScene = scene()) {
349        const QList<QGraphicsView*> views = myScene->views();
350        for (unsigned i = 0; i < views.size(); ++i) {
351            if (views.at(i) == QApplication::focusWidget()) {
352                view = views.at(i);
353                break;
354            }
355        }
356        if (!view)
357            view = views.value(0, 0);
358    }
359    if (view)
360        menu->setParent(view, menu->windowFlags());
361    menu->exec(view->mapToGlobal(menu->pos()));
362    if (d->activeMenu == menu)
363        d->activeMenu = 0;
364}
365
366void QGraphicsWKView::takeSnapshot(const QSize& size, const QRect& contentsRect)
367{
368#if ENABLE(TILED_BACKING_STORE)
369    DrawingAreaProxy* drawingArea = page()->d->page->drawingArea();
370    if (drawingArea->info().type != DrawingAreaInfo::Tiled)
371        return;
372    TiledDrawingAreaProxy* tiledDrawingArea = static_cast<TiledDrawingAreaProxy*>(drawingArea);
373    tiledDrawingArea->takeSnapshot(size, contentsRect);
374#endif
375}
376
377QGraphicsWKViewPrivate::QGraphicsWKViewPrivate(QGraphicsWKView* view)
378    : q(view)
379    , activeMenu(0)
380    , m_scaleCommitTimer(RunLoop::current(), this, &QGraphicsWKViewPrivate::commitScale)
381    , m_isChangingScale(false)
382{
383}
384
385QRectF QGraphicsWKView::visibleRect() const
386{
387    if (!scene())
388        return QRectF();
389
390    QList<QGraphicsView*> views = scene()->views();
391    if (views.isEmpty())
392        return QRectF();
393
394    QGraphicsView* graphicsView = views.at(0);
395    int xOffset = graphicsView->horizontalScrollBar()->value();
396    int yOffset = graphicsView->verticalScrollBar()->value();
397    return mapRectFromScene(QRectF(QPointF(xOffset, yOffset), graphicsView->viewport()->size()));
398}
399
400void QGraphicsWKView::prepareScaleChange()
401{
402#if ENABLE(TILED_BACKING_STORE)
403    ASSERT(!d->m_isChangingScale);
404    d->m_isChangingScale = true;
405    d->m_scaleCommitTimer.stop();
406#endif
407}
408
409void QGraphicsWKView::commitScaleChange()
410{
411#if ENABLE(TILED_BACKING_STORE)
412    ASSERT(d->m_isChangingScale);
413    d->m_isChangingScale = false;
414    d->commitScale();
415#endif
416}
417
418void QGraphicsWKViewPrivate::onScaleChanged()
419{
420#if ENABLE(TILED_BACKING_STORE)
421    if (!m_isChangingScale)
422        m_scaleCommitTimer.startOneShot(0.1);
423#endif
424}
425
426void QGraphicsWKViewPrivate::commitScale()
427{
428#if ENABLE(TILED_BACKING_STORE)
429    DrawingAreaProxy* drawingArea = page->d->page->drawingArea();
430    float newScale = q->scale();
431    if (drawingArea->info().type == DrawingAreaInfo::Tiled) {
432        TiledDrawingAreaProxy* tiledDrawingArea = static_cast<TiledDrawingAreaProxy*>(drawingArea);
433        if (tiledDrawingArea->contentsScale() == newScale)
434            return;
435        tiledDrawingArea->setContentsScale(newScale);
436        // For now we block until complete.
437        tiledDrawingArea->waitUntilUpdatesComplete();
438    }
439#endif
440}
441
442#include "moc_qgraphicswkview.cpp"
443