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);
pageRefQGraphicsWKViewPrivate51 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
QGraphicsWKView(QWKContext * context,BackingStoreType backingStoreType,QGraphicsItem * parent)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
~QGraphicsWKView()91 QGraphicsWKView::~QGraphicsWKView()
92 {
93 delete d->page;
94 delete d;
95 }
96
page() const97 QWKPage* QGraphicsWKView::page() const
98 {
99 return d->page;
100 }
101
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget *)102 void QGraphicsWKView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*)
103 {
104 page()->d->paint(painter, option->exposedRect.toAlignedRect());
105 }
106
setGeometry(const QRectF & rect)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
load(const QUrl & url)119 void QGraphicsWKView::load(const QUrl& url)
120 {
121 page()->load(url);
122 }
123
setUrl(const QUrl & url)124 void QGraphicsWKView::setUrl(const QUrl& url)
125 {
126 page()->setUrl(url);
127 }
128
url() const129 QUrl QGraphicsWKView::url() const
130 {
131 return page()->url();
132 }
133
title() const134 QString QGraphicsWKView::title() const
135 {
136 return page()->title();
137 }
138
triggerPageAction(QWKPage::WebAction action,bool checked)139 void QGraphicsWKView::triggerPageAction(QWKPage::WebAction action, bool checked)
140 {
141 page()->triggerAction(action, checked);
142 }
143
back()144 void QGraphicsWKView::back()
145 {
146 page()->triggerAction(QWKPage::Back);
147 }
148
forward()149 void QGraphicsWKView::forward()
150 {
151 page()->triggerAction(QWKPage::Forward);
152 }
153
reload()154 void QGraphicsWKView::reload()
155 {
156 page()->triggerAction(QWKPage::Reload);
157 }
158
stop()159 void QGraphicsWKView::stop()
160 {
161 page()->triggerAction(QWKPage::Stop);
162 }
163
updateCursor(const QCursor & cursor)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
focusNextPrevChildCallback(bool next)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 */
focusNextPrevChild(bool next)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 */
itemChange(GraphicsItemChange change,const QVariant & value)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 */
event(QEvent * event)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 */
sizeHint(Qt::SizeHint which,const QSizeF & constraint) const241 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 */
inputMethodQuery(Qt::InputMethodQuery query) const250 QVariant QGraphicsWKView::inputMethodQuery(Qt::InputMethodQuery query) const
251 {
252 // implement
253 return QVariant();
254 }
255
256 /*! \reimp
257 */
keyPressEvent(QKeyEvent * ev)258 void QGraphicsWKView::keyPressEvent(QKeyEvent* ev)
259 {
260 page()->d->keyPressEvent(ev);
261 }
262
263 /*! \reimp
264 */
keyReleaseEvent(QKeyEvent * ev)265 void QGraphicsWKView::keyReleaseEvent(QKeyEvent* ev)
266 {
267 page()->d->keyReleaseEvent(ev);
268 }
269
hoverMoveEvent(QGraphicsSceneHoverEvent * ev)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
mouseMoveEvent(QGraphicsSceneMouseEvent * ev)282 void QGraphicsWKView::mouseMoveEvent(QGraphicsSceneMouseEvent* ev)
283 {
284 page()->d->mouseMoveEvent(ev);
285 if (!ev->isAccepted())
286 QGraphicsItem::mouseMoveEvent(ev);
287 }
288
mousePressEvent(QGraphicsSceneMouseEvent * ev)289 void QGraphicsWKView::mousePressEvent(QGraphicsSceneMouseEvent* ev)
290 {
291 page()->d->mousePressEvent(ev);
292 if (!ev->isAccepted())
293 QGraphicsItem::mousePressEvent(ev);
294 }
295
mouseReleaseEvent(QGraphicsSceneMouseEvent * ev)296 void QGraphicsWKView::mouseReleaseEvent(QGraphicsSceneMouseEvent* ev)
297 {
298 page()->d->mouseReleaseEvent(ev);
299 if (!ev->isAccepted())
300 QGraphicsItem::mouseReleaseEvent(ev);
301 }
302
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * ev)303 void QGraphicsWKView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev)
304 {
305 page()->d->mouseDoubleClickEvent(ev);
306 if (!ev->isAccepted())
307 QGraphicsItem::mouseReleaseEvent(ev);
308 }
309
wheelEvent(QGraphicsSceneWheelEvent * ev)310 void QGraphicsWKView::wheelEvent(QGraphicsSceneWheelEvent* ev)
311 {
312 page()->d->wheelEvent(ev);
313 if (!ev->isAccepted())
314 QGraphicsItem::wheelEvent(ev);
315 }
316
touchEvent(QTouchEvent * ev)317 void QGraphicsWKView::touchEvent(QTouchEvent* ev)
318 {
319 page()->d->touchEvent(ev);
320 }
321
focusInEvent(QFocusEvent *)322 void QGraphicsWKView::focusInEvent(QFocusEvent*)
323 {
324 page()->d->page->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
325 }
326
focusOutEvent(QFocusEvent *)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 */
showContextMenu(QSharedPointer<QMenu> menu)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
takeSnapshot(const QSize & size,const QRect & contentsRect)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
QGraphicsWKViewPrivate(QGraphicsWKView * view)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
visibleRect() const388 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
prepareScaleChange()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
commitScaleChange()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
onScaleChanged()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
onToolTipChanged(const QString & toolTip)429 void QGraphicsWKViewPrivate::onToolTipChanged(const QString& toolTip)
430 {
431 q->setToolTip(toolTip);
432 }
433
commitScale()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