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