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