diff -r 000000000000 -r 1450b09d0cfd ginebra2/GWebTouchNavigation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ginebra2/GWebTouchNavigation.cpp Tue May 04 12:39:35 2010 +0300 @@ -0,0 +1,1321 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + + +#include "GWebTouchNavigation.h" +#include "qwebpage.h" +#include "qwebframe.h" +#include "qapplication.h" +#include "qwebelement.h" +#include "GWebContentViewWidget.h" +#include "GWebContentView.h" +#include +#include +#include +#include +#include + +#include "webcontentview.h" +#include "wrtBrowserUtils.h" +#include "ChromeWidget.h" +#include + + +#define LONG_PRESS_DURATION 500 + +const int KFlickSpeed = 400; +const qreal PI = 3.1415926535897932; +const qreal KAngleTolerance = 30; +const int KAnimationTimeout = 40; +const qreal KDecelerationFactor = 0.8; +const int KMilliSecond = 1000; +const int KThresholdForRightToLeftMotion = 100; // 100 pixels +const int KThresholdForLeftToRightMotion = -100; // 100 pixels +const int XAlignVal = 10; +const int KDecelerationTimer = 125; //Decelerate only if flicked/swiped after KDecelerationTimer milli seconds after last drag event + +const int KDoubleClickTimeOut = 400; +const QPoint KFocussPoint(5, 50); +const int KTouchThresholdX = 20; +const int KTouchThresholdY = 20; +const int KPanThreshold = 2; +const int KThreshHoldValForLink = 10; +const qreal KDeccelaration = 1000.00; +const int KDefaultViewportWidth = 980; +const int KDefaultPortraitScaleWidth = 540; + +const int KKineticTimeout = 60; + +static const int KStartPanDistance = 50; +static const int KWaitForClickTimeoutMS = 200; +//The amount of pixels to try to pan before pan mode unlocks +static const int KPanModeChangeDelta = 100; + + +namespace GVA { + +/*! + \class WebTouchNavigation + \since cwrt 1.0 + \brief cwrt navigation. + + \sa WebNavigation, WebHtmlTabIndexedNavigation, WebCursorNavigation, WebDirectionalNavigation +*/ +GWebTouchNavigation::GWebTouchNavigation(QWebPage* webPage,GWebContentViewWidget* view) +: m_webPage(webPage) +, m_view(view) +, m_frame(0) +, m_doubleClickTimer(0) +, m_pressEvent(0) +, m_scrollTimer(0) +, m_releaseEvent(0) +, m_focusedBlockPt(-1, -1) +, m_ishighlighted(false) +, m_offset(0) +, m_longPressTimer(0) +, m_finalzoomfactor(0) +, m_kineticTimer(0) +, m_isPanning(false) +, m_enabled(true) +, m_isLoading(false) +{ + install(); + connect(m_view, SIGNAL(BlockFocusChanged(QPoint)), this, SLOT(BlockFocusChanged(QPoint))); + connect(m_webPage, SIGNAL(loadStarted()), this, SLOT(onLoadStarted())); + connect(m_webPage, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool))); +} + +/*! +*/ +GWebTouchNavigation::~GWebTouchNavigation() +{ + disconnect(m_doubleClickTimer, SIGNAL(timeout()), this, SLOT(doubleClickTimerExpired())); + if (m_doubleClickTimer) { + m_doubleClickTimer->stop(); + delete m_doubleClickTimer; + } + disconnect(m_scrollTimer, SIGNAL(timeout()),this,SLOT(pan())); + delete m_scrollTimer; + + disconnect(m_kineticTimer, SIGNAL(timeout()),this,SLOT(kineticScroll())); + delete m_kineticTimer; + uninstall(); +} + +void GWebTouchNavigation::install() +{ + m_view->installEventFilter(this); +} + +void GWebTouchNavigation::BlockFocusChanged(QPoint pt) +{ + m_focusedBlockPt = pt; +} + +void GWebTouchNavigation::uninstall() +{ + if (m_view) + m_view->removeEventFilter(this); +} + +bool GWebTouchNavigation::eventFilter(QObject *object, QEvent *event) +{ + if (object != m_view || !enabled()) + return false; + + switch (event->type()) { + case QEvent::MouseButtonPress: + if (static_cast(event)->buttons() & Qt::LeftButton) { + mousePressEvent(static_cast(event)->pos()); + return true; + } + break; + case QEvent::MouseMove: + if (static_cast(event)->buttons() & Qt::LeftButton) { + QPoint diff = m_touchPosition - static_cast(event)->globalPos(); + mouseMoveEvent(static_cast(event)->pos(), diff); + return true; + } + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent(static_cast(event)->pos()); + return true; + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent(static_cast(event)->pos()); + return true; + case QEvent::GraphicsSceneMousePress: + if (static_cast(event)->buttons() & Qt::LeftButton) { + mousePressEvent(static_cast(event)->pos().toPoint()); + emit mouseEvent(QEvent::GraphicsSceneMousePress); + return true; + } + break; + case QEvent::GraphicsSceneMouseMove: + if (static_cast(event)->buttons() & Qt::LeftButton) { + QPoint diff = static_cast(event)->lastScenePos().toPoint() - static_cast(event)->scenePos().toPoint(); + mouseMoveEvent(static_cast(event)->pos().toPoint(), diff); + return true; + } + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(static_cast(event)->pos().toPoint()); + emit mouseEvent(QEvent::GraphicsSceneMouseRelease); + return true; + break; + case QEvent::GraphicsSceneMouseDoubleClick: + mouseDoubleClickEvent(static_cast(event)->pos().toPoint()); + emit mouseEvent(QEvent::GraphicsSceneMouseDoubleClick); + return true; + break; + default: + break; + } + return false; +} + +void GWebTouchNavigation::stopScrolling() +{ + if (m_scrollTimer && m_scrollTimer->isActive()) { + m_scrollTimer->stop(); + m_scrollDelta = QPoint(0,0); + } + + if (m_kineticTimer && m_kineticTimer->isActive()) { + m_kineticTimer->stop(); + m_kineticSpeed.setX(0.0); + m_kineticSpeed.setY(0.0); + } +} + + +void GWebTouchNavigation::pan() +{ + if (m_scrollDelta.x() != 0 || m_scrollDelta.y() != 0) { + scrollCurrentFrame(-m_scrollDelta.x(), m_scrollDelta.y()); + m_scrollDelta = QPoint(0,0); + } +} + +#define DECELERATION 0.0002 + +void GWebTouchNavigation::kineticScroll() +{ + qreal decelX = m_kineticSpeed.x() > 0 ? -DECELERATION : DECELERATION; + qreal decelY = m_kineticSpeed.y() > 0 ? -DECELERATION : DECELERATION; + qreal dx = 0; + qreal dy = 0; + qreal vx = 0; + qreal vy = 0; + + m_kineticScrollTime += m_actualTime.elapsed(); + + if (m_kineticSpeed.x()) { + vx = m_kineticSpeed.x() + decelX * m_kineticScrollTime; + if (vx * m_kineticSpeed.x() < 0) { + dx = 0; + vx = 0; + } + else { + dx = m_kineticScrollTime * m_kineticSpeed.x() + + 0.5 * decelX * m_kineticScrollTime * m_kineticScrollTime; + } + } + + if (m_kineticSpeed.y()) { + vy = m_kineticSpeed.y() + decelY * m_kineticScrollTime; + if (vy * m_kineticSpeed.y() < 0) { + dy = 0; + vy = 0; + } + else { + dy = m_kineticScrollTime * m_kineticSpeed.y() + + 0.5 * decelY * m_kineticScrollTime * m_kineticScrollTime; + } + } + + QPoint scrollPos = m_frame->scrollPosition(); + QPoint distPos = m_initialScrollPos + QPointF(dx, dy).toPoint(); + + if (vx != 0 || vy != 0) { + setCurrentFrameScrollPosition(distPos); + } + + if ((vx == 0 && vy == 0) || scrollPos == m_frame->scrollPosition()) { + stopScrolling(); + } + +} + + + +/*! + Scrolls QWebFrame to the given position + \sa QWebFrame::scroll() +*/ +void GWebTouchNavigation::scrollFrame(const QPoint& diff) +{ + if (qAbs(diff.x()) < KPanThreshold && qAbs(diff.y()) < KPanThreshold) + return; + else if(m_dragPoints.size() == 1) { + //WebContentWidget* view = qobject_cast(m_view); + m_view->setBlockElement(QWebElement()); + } + + if(!m_isPanning) { + qreal dy = qAbs(diff.y()); + qreal dx = qAbs(diff.x()); + if (dy > KStartPanDistance || dx > KStartPanDistance + || m_delayedPressMoment.elapsed() > KWaitForClickTimeoutMS) { + //get the scroll direction + Direction scrollDir = findDirectionWithAngle(diff); + if(scrollDir == UP || scrollDir == DOWN) + startPanGesture(VerticalPan); + else if(scrollDir == LEFT || scrollDir == RIGHT) + startPanGesture(HorizontalPan); + else + startPanGesture(RandomPan); + m_isPanning = true; + } + } + + if (m_isPanning) { + m_scrolled= false; + m_frame = getNextScrollableFrame(diff); + QPoint scrollPosition = m_frame->scrollPosition(); + if (diff.manhattanLength()) + panBy(diff); + + m_scrolled = (scrollPosition != m_frame->scrollPosition()); + } +} + +void GWebTouchNavigation::startPanGesture(PanDirection directionHint) { + emit startingPanGesture((int)directionHint); + m_panDirection = directionHint; + m_panModeResidue = QPointF(0., 0.); +} + +void GWebTouchNavigation::panBy(const QPointF& delta) { + + m_panModeResidue += delta; + + if (qAbs(m_panModeResidue.x()) > KPanModeChangeDelta) + m_panDirection = HorizontalPan; + + if (qAbs(m_panModeResidue.y()) > KPanModeChangeDelta) + m_panDirection = VerticalPan; + + if(qAbs(m_panModeResidue.x()) > KPanModeChangeDelta + && qAbs(m_panModeResidue.y()) > KPanModeChangeDelta) + m_panDirection = RandomPan; + + QPointF p; + if(m_panDirection == HorizontalPan) + p.setX(delta.x()); + if(m_panDirection == VerticalPan) + p.setY(delta.y()); + if(m_panDirection == RandomPan) { + p.setX(delta.x()); + p.setY(delta.y()); + } + + scrollCurrentFrame(p.x(),p.y()); +} + +void GWebTouchNavigation::highlightableElement(QMouseEvent* ev) { + m_anchorElement = getClosestAnchorElement(ev); +} + +inline int xInRect(const QRect& r, int x) +{ + return std::min(std::max(x, r.x()), r.x() + r.width()); +} + +inline int yInRect(const QRect& r, int y) +{ + return std::min(std::max(y, r.y()), r.y() + r.height()); +} + +bool GWebTouchNavigation::traverseNextNode(QWebElement parentNode,QWebElement& nextNode) +{ + if (!parentNode.firstChild().isNull()) { + nextNode = parentNode.firstChild(); + return true; + } + + if (!parentNode.nextSibling().isNull()) { + nextNode = parentNode.nextSibling(); + return true; + } + + QWebElement n = parentNode; + while (!n.isNull() && n.nextSibling().isNull()) + n = n.parent (); + + if (!n.isNull()) { + nextNode = n.nextSibling(); + return true; + } + + return false; +} + + +QWebElement GWebTouchNavigation::getClosestAnchorElement(QMouseEvent* ev) +{ + QWebElement webElement; + QWebHitTestResult htRes = getHitTestResult(ev); + QWebElement hitElement = htRes.element(); + //check whether hit test returns a link element + if(!htRes.linkElement().isNull()) { + webElement = htRes.linkElement(); + m_higlightedPos = ev->pos(); + m_ishighlighted = true; + } + //get the closet anchor element + else { + QPoint docPoint = (m_touchPosition + m_frame->scrollPosition()); + int dist = 99999999; + QWebElement result; + QWebElement ele = m_webPage->currentFrame()->documentElement(); + do { + if(ele.tagName().compare("a",Qt::CaseInsensitive) == 0 ) { + QRect r = ele.geometry(); + if(r.contains(docPoint)) { + dist = 0; + result = ele; + break; + } + + int x = xInRect(r, docPoint.x()); + int y = yInRect(r, docPoint.y()); + int d = (docPoint.x() - x) * (docPoint.x() - x) + (docPoint.y() - y) * (docPoint.y() - y); + if (dist > d) { + dist = d; + result = ele; + } + } + }while(traverseNextNode(ele,ele)); + +// WebContentWidget* view = qobject_cast(m_view); + // check if we are close enough and calcualte with zoom factor. + if (dist< (KThreshHoldValForLink/m_view->zoomFactor())) { + QRect r = result.geometry(); + r.translate(2,2); + r.setWidth(2+2); + r.setHeight(2+2); + webElement = result; + m_higlightedPos = QPoint(xInRect(r, docPoint.x()), yInRect(r, docPoint.y())) - m_frame->scrollPosition(); + m_ishighlighted = true; + QPoint centerpt = webElement.geometry().center(); + m_offset = (docPoint.x()- centerpt.x())*(docPoint.x()- centerpt.x()) + (docPoint.y()- centerpt.y())*(docPoint.y()- centerpt.y()); + + } + } + return webElement; +} + +/*! + Sends a mouse press event to QWebPage +*/ +void GWebTouchNavigation::mousePressEvent(const QPoint& pos) +{ + m_delayedPressMoment.start(); + // stop deceleration and don't send further events to engine if scroll timer is active + if(m_scrollTimer && m_scrollTimer->isActive()) { + m_prevPoint.setX(0); + m_prevPoint.setY(0); + m_scrollDistance.setX(0); + m_scrollDistance.setY(0); + + m_lastMoveEventTime.setHMS(0,0,0,0); //H, M, S, mS + m_scrolled = false; + m_ishighlighted = false; + m_higlightedPos = m_touchPosition = pos; + m_frame = m_webPage->frameAt(pos); + if (!m_frame) + m_frame = m_webPage->currentFrame(); + + m_dragPoints.clear(); + + DragPoint dragPoint; + dragPoint.iPoint = m_touchPosition; + dragPoint.iTime = QTime::currentTime(); + m_dragPoints.append(dragPoint); + m_offset = 0; + + m_pressEvent = new QMouseEvent(QEvent::MouseButtonPress, m_touchPosition, Qt::LeftButton, Qt::LeftButton, getEventModifier(m_touchPosition)); + m_scrollTimer->stop(); + return; + } + + if (m_doubleClickTimer) { + // If there is another click event almost on the same region + // as the previous click before doubleClick timeout + // Consider it as double click. + if (m_doubleClickTimer->isActive() && m_pressEvent) { + QPoint diff = m_pressEvent->pos() - pos; + if (qAbs(diff.x()) < KTouchThresholdX && qAbs(diff.y()) < KTouchThresholdY) { + mouseDoubleClickEvent(pos); + return; + } + } + m_doubleClickTimer->stop(); + } else { + m_doubleClickTimer = new QTimer(this); + connect(m_doubleClickTimer, SIGNAL(timeout()), this, SLOT(doubleClickTimerExpired())); + } + + //Clear the previous press and release events. + if (m_pressEvent) { + delete m_pressEvent; + m_pressEvent = NULL; + } + + if (m_releaseEvent) { + delete m_releaseEvent; + m_releaseEvent = NULL; + } + + m_doubleClickTimer->start(KDoubleClickTimeOut); + m_pressEvent = new QMouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, getEventModifier(pos)); +} + +void GWebTouchNavigation::handleMousePressEvent(QMouseEvent* ev) +{ + m_lastMoveEventTime.setHMS(0,0,0,0); //H, M, S, mS + if(!m_longPressTimer){ + delete m_longPressTimer; + m_longPressTimer = 0; + } + startTimer(); + m_scrolled = false; + m_ishighlighted = false; + m_higlightedPos = m_touchPosition = ev->pos(); + m_frame = m_webPage->frameAt(ev->pos()); + if (!m_frame) + m_frame = m_webPage->currentFrame(); + + m_dragPoints.clear(); + + DragPoint dragPoint; + dragPoint.iPoint = m_touchPosition; + dragPoint.iTime = QTime::currentTime(); + m_dragPoints.append(dragPoint); + + m_offset = 0; + + highlightableElement(ev); + getFocusedElement(); + + //send a mouse press + QMouseEvent iev(ev->type(), m_touchPosition, ev->button(), ev->buttons(), getEventModifier(m_higlightedPos)); + m_webPage->event(&iev); +} + +void GWebTouchNavigation::doubleClickTimerExpired() +{ + handleMousePressEvent(m_pressEvent); + + delete m_pressEvent; + m_pressEvent = NULL; + + if (m_releaseEvent) { + handleMouseReleaseEvent(m_releaseEvent); + + //clear release event + delete m_releaseEvent; + m_releaseEvent = NULL; + } + if (m_doubleClickTimer && m_doubleClickTimer->isActive()) + m_doubleClickTimer->stop(); +} + +bool GWebTouchNavigation::canDehighlight(QMouseEvent* ev) + { + bool checkDehighlight = false; + QPoint pt = ev->pos() + m_frame->scrollPosition(); + + if(m_scrolled && m_ishighlighted) { + checkDehighlight = true; + } + else if(!m_scrolled && m_ishighlighted) { + QRect rect = m_anchorElement.geometry(); + + if(m_anchorElement.geometry().contains(pt)) { + checkDehighlight = false; + } + else if(m_offset){ + QPoint centerpt = m_anchorElement.geometry().center(); + int newoffset = (pt.x()- centerpt.x())*(pt.x()- centerpt.x()) + (pt.y()- centerpt.y())*(pt.y()- centerpt.y()); + + if(newoffset <= m_offset ) { + m_offset = newoffset; + checkDehighlight = false; + } + else { + m_offset =0; + checkDehighlight = true; + } + } + else { + checkDehighlight = true; + } + } + return checkDehighlight; +} + void GWebTouchNavigation::dehighlight(QMouseEvent* ev) +{ + m_higlightedPos = QPoint(0,0); + m_ishighlighted = false; + QMouseEvent iev(QEvent::MouseButtonPress,m_higlightedPos,ev->button(), ev->buttons(), ev->modifiers()); + m_webPage->event(&iev); +} + +void GWebTouchNavigation::handleHighlightChange(QMouseEvent* ev) +{ + if (!canDehighlight(ev)) return; + + dehighlight(ev); +} + +/*! + Scrolls the frame + \sa scrollFrame() +*/ +void GWebTouchNavigation::mouseMoveEvent(const QPoint& pos, const QPoint& diff) +{ + + if (m_pressEvent){ + QPoint diff2 = m_pressEvent->pos() - pos; + if (qAbs(diff2.x()) < KTouchThresholdX && qAbs(diff2.y()) < KTouchThresholdY) + return; + } + + if (m_doubleClickTimer && m_doubleClickTimer->isActive()) { + //send mousePressEvent + m_doubleClickTimer->stop(); + handleMousePressEvent(m_pressEvent); + delete m_pressEvent; + m_pressEvent = NULL; + } + + stopTimer(); + QMouseEvent tmpEv(QEvent::MouseMove, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + handleHighlightChange(&tmpEv); + + scrollFrame(diff); + + //m_touchPosition = pos; + DragPoint dragPoint; + dragPoint.iPoint = pos; + dragPoint.iTime = QTime::currentTime(); + m_dragPoints.append(dragPoint); + + QTime now(QTime::currentTime()); + m_lastMoveEventTime.setHMS(now.hour(),now.minute(), now.second(), now.msec()); + + while (m_dragPoints.size() > 4) + m_dragPoints.removeFirst(); + +} + +/*! + If WebCanvas or QWebFrame were scrolled starts the deceleration alogrithm + Otherwise sends the mouse release event to QWebPage + \sa startDeceleration() +*/ +void GWebTouchNavigation::mouseReleaseEvent(const QPoint& pos) +{ + if(m_isPanning) + m_isPanning = false; + if (m_doubleClickTimer && m_doubleClickTimer->isActive()) { + m_releaseEvent = new QMouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + return; + } + //mouseReleaseEvent is called in two cases. 1. Double click, 2. Mouse Drag + //m_releaseEvent is not null only in Mouse double click + //So delete m_releaseEvent only in double click case. + //Send release event in mouse move case + if (m_releaseEvent) { + delete m_releaseEvent ; + m_releaseEvent = NULL; + } else { + QMouseEvent tmpEv(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + handleMouseReleaseEvent(&tmpEv); + } +} +Qt::KeyboardModifier GWebTouchNavigation::getEventModifier(const QPoint& pos) +{ + QWebFrame *frame = m_webPage->frameAt(pos); + if (!frame) + frame = m_webPage->currentFrame(); + QWebHitTestResult htr = frame->hitTestContent(pos); + if (htr.element().tagName().toLower().compare("select")==0 && htr.element().hasAttribute("multiple")) + return Qt::ControlModifier; + return Qt::NoModifier; +} + +void GWebTouchNavigation::handleMouseReleaseEvent(QMouseEvent* ev) +{ + m_frame = m_webPage->frameAt(ev->pos()); + if (!m_frame) + m_frame = m_webPage->currentFrame(); + assert(m_frame); + + stopTimer(); + if (m_scrolled) { + int msecs = 0; + if (!m_lastMoveEventTime.isNull()) { + //Start deceleration only if the delta since last drag event is less than threshold + QTime now(QTime::currentTime()); + msecs = m_lastMoveEventTime.msecsTo(now); + m_lastMoveEventTime.setHMS(0,0,0,0); + } + if (msecs < KDecelerationTimer) { + if( isFlick()) { + updateFlickScrollDistance(); + } + else { + QRect enclosingRect = findEnclosingBlock(ev); + QPoint blockDocPt(enclosingRect.x(), enclosingRect.y()); + QPoint blockCanvasPoint(blockDocPt - m_frame->scrollPosition()); + calculateActualScrollDistance(); + int thresholdCheckVal = blockCanvasPoint.x() - m_actualScrollDistance.x(); + setNewScrollDistance(blockCanvasPoint, thresholdCheckVal); + } + startScrollTimer(); + } + } else { + QMouseEvent iev = m_ishighlighted ? + QMouseEvent(ev->type(), m_higlightedPos, ev->button(), ev->buttons(), getEventModifier(m_higlightedPos)) : + QMouseEvent(QEvent::MouseButtonRelease, m_touchPosition, ev->button(), ev->buttons(), getEventModifier(m_touchPosition)); + m_webPage->event(&iev); +#if defined __SYMBIAN32__ +// FIXME Remove this, it will be fixed Qt 4.6.3 ? + if (ev->button() == Qt::LeftButton) { + QWebHitTestResult htr = m_frame->hitTestContent(ev->pos()); + if (htr.isContentEditable()) { + QEvent vkbEvent(QEvent::RequestSoftwareInputPanel); + QList views = m_view->scene()->views(); + QWidget* view = qobject_cast(views.value(0)); + if (view) + QApplication::sendEvent(view, &vkbEvent); + } + } +#endif + } +} + +void GWebTouchNavigation::mouseDoubleClickEvent(const QPoint& pos) +{ + + if(m_doubleClickTimer && !m_doubleClickTimer->isActive()) + return; + else if(m_doubleClickTimer) + m_doubleClickTimer->stop(); + + //If the page is already scrolling(because of a previous doubletap) + //we need to stop the timer before we start scrolling the new block again. + if (m_scrollTimer && m_scrollTimer->isActive()) + m_scrollTimer->stop(); + + QMouseEvent tmpEv(QEvent::MouseButtonDblClick, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + handleDoubleClickEvent(&tmpEv); +} + +void GWebTouchNavigation::handleDoubleClickEvent(QMouseEvent* ev) +{ + QPoint imageFocusPoint; + QWebHitTestResult hitTest = getHitTestResult(ev); + + QWebElement block = hitTest.element(); + + if (block.tagName() != "IMG" && (block.styleProperty(QString("background-image"),QWebElement::InlineStyle) == "")) + block = hitTest.enclosingBlockElement(); + + QWebFrame* frame = m_webPage->frameAt(ev->pos()); + m_frame = (frame) ? frame : m_webPage->mainFrame(); + + QRect enclosingRect = block.geometry(); + + QPoint blockCanvasPt = QPoint(enclosingRect.topLeft()) - m_frame->scrollPosition(); + //WebContentWidget* view = qobject_cast(m_view); + m_view->setBlockElement(QWebElement()); + if(m_focusedBlockPt == blockCanvasPt) { + m_focusedBlockPt.setX(-1); + m_focusedBlockPt.setY(-1); + qreal m_Ratiox = (qreal) blockCanvasPt.x() / block.geometry().width(); + qreal m_Ratioy = (qreal) blockCanvasPt.y() / block.geometry().height(); + m_view->setZoomFactor(m_view->initialScale()); + QPoint m_InfocusBlockPt = QPoint(block.geometry().topLeft()) - m_webPage->mainFrame()->scrollPosition(); + m_webPage->currentFrame()->scroll(m_InfocusBlockPt.x() - (m_Ratiox * block.geometry().width()), + m_InfocusBlockPt.y() - (m_Ratioy * block.geometry().height())); + m_finalzoomfactor = 0; + }else { + if(block.tagName() != "IMG" && (block.styleProperty(QString("background-image"),QWebElement::InlineStyle) == "")) { + m_finalzoomfactor = (qreal) (m_webPage->viewportSize().width() - 10) * m_view->zoomFactor(); + m_finalzoomfactor = (qreal) m_finalzoomfactor / (enclosingRect.width()); + QString str; + str.setNum(m_finalzoomfactor,'f',2); + m_finalzoomfactor = str.toDouble(); + }else { + qreal factor; + factor = 1/m_view->initialScale(); + int boundW = block.geometry().width() * factor/m_view->zoomFactor(); + int boundH = block.geometry().height() * factor/m_view->zoomFactor(); + qreal factorw = 0.0,factorh = 0.0 ; + + if( boundW > m_webPage->viewportSize().width()) + factorw = (qreal)(m_webPage->viewportSize().width()-5)/ block.geometry().width(); + + if(boundH > m_webPage->viewportSize().height()) + factorh = (qreal)(m_webPage->viewportSize().height()-5)/ block.geometry().height(); + + if( (factorw == 0.0) && (factorh == 0.0)) + ; + else if(factorw == 0.0) + factor = factorh * m_view->zoomFactor(); + else if(factorh == 0.0) + factor = factorw * m_view->zoomFactor(); + else { + factor = ((factorh < factorw) ? factorh : factorw) * m_view->zoomFactor(); + } + + QString str; + str.setNum(factor,'f',2); + factor = str.toDouble(); + + if(m_finalzoomfactor != factor) + m_finalzoomfactor = factor; + } + + + if (m_finalzoomfactor != m_view->zoomFactor()) { + m_view->setZoomFactor(m_finalzoomfactor); + m_focusedBlockPt = QPoint(block.geometry().topLeft()) - m_frame->scrollPosition(); + + if(block.tagName() != "IMG" && (block.styleProperty(QString("background-image"),QWebElement::InlineStyle) == "")) + scrollCurrentFrame(m_focusedBlockPt.x() - KFocussPoint.x() , m_focusedBlockPt.y() - KFocussPoint.y()); + else { + if((m_webPage->viewportSize().width() - block.geometry().width()) > 0) + imageFocusPoint.setX((m_webPage->viewportSize().width() - block.geometry().width())/2); + else + imageFocusPoint.setX(0); + + if((m_webPage->viewportSize().height() - block.geometry().height()) > 0) + imageFocusPoint.setY((m_webPage->viewportSize().height() - block.geometry().height())/2); + else + imageFocusPoint.setY(0); + + scrollCurrentFrame(m_focusedBlockPt.x() - imageFocusPoint.x() , m_focusedBlockPt.y() - imageFocusPoint.y()); + } + + m_focusedBlockPt = QPoint(block.geometry().topLeft()) - m_frame->scrollPosition(); + } else { + //Get doc size and current bottom right view corner point in document + QSize viewSize = m_webPage->viewportSize(); + QSize contentSize = m_frame->contentsSize(); + QPoint documentViewPoint = QPoint(viewSize.width(),viewSize.height()) + m_frame->scrollPosition(); + QPoint docFocusPoint; + + if(block.tagName() != "IMG" && (block.styleProperty(QString("background-image"),QWebElement::InlineStyle) == "")) + docFocusPoint = KFocussPoint + m_frame->scrollPosition(); + else { + if((m_webPage->viewportSize().width() - block.geometry().width()) > 0) + imageFocusPoint.setX((m_webPage->viewportSize().width() - block.geometry().width())/2); + else + imageFocusPoint.setX(0); + + if((m_webPage->viewportSize().height() - block.geometry().height()) > 0) + imageFocusPoint.setY((m_webPage->viewportSize().height()- block.geometry().height())/2); + else + imageFocusPoint.setY(0); + + docFocusPoint = imageFocusPoint + m_frame->scrollPosition(); + } + + m_focusedBlockPt = QPoint(block.geometry().x(), block.geometry().y()); + m_scrollDistance.setX(m_focusedBlockPt.x() - docFocusPoint.x()); + m_scrollDistance.setY(m_focusedBlockPt.y() - docFocusPoint.y()); + + QPoint scrollableDistance(0, 0); + QPoint viewStartPoint = QPoint(0,0) + m_frame->scrollPosition(); + + if(m_scrollDistance.x() < 0) + scrollableDistance.setX(viewStartPoint.x()); + else + scrollableDistance.setX(contentSize.width() - documentViewPoint.x()); + + + if(m_scrollDistance.y() < 0) + scrollableDistance.setY(viewStartPoint.y()); + else + scrollableDistance.setY(contentSize.height() - documentViewPoint.y()); + + if(abs(m_scrollDistance.x()) > abs(scrollableDistance.x())){ + //m_scrollDistance.x() >= 0 means scroll from right to left + if(m_scrollDistance.x() >= 0) + m_focusedBlockPt.setX(m_focusedBlockPt.x() - abs(scrollableDistance.x())); + //m_scrollDistance.x() < 0 means scroll from left to right + else + m_focusedBlockPt.setX(m_focusedBlockPt.x() + abs(scrollableDistance.x())); + } else + m_focusedBlockPt.setX(docFocusPoint.x()); + + if (abs(m_scrollDistance.y()) > abs(scrollableDistance.y())){ + if(m_scrollDistance.y() >= 0) + m_focusedBlockPt.setY(m_focusedBlockPt.y() - abs(scrollableDistance.y())); + else + m_focusedBlockPt.setY(m_focusedBlockPt.y() + abs(scrollableDistance.y())); + } + else + m_focusedBlockPt.setY(docFocusPoint.y()); + + m_focusedBlockPt = m_focusedBlockPt - m_frame->scrollPosition(); + startScrollTimer(); + } + m_view->setBlockElement(block); + } +} + +/*! + Returns the next scrollable frame in the frame tree give the x,y position +*/ +QWebFrame* GWebTouchNavigation::getNextScrollableFrame(const QPoint& pos) +{ + QWebFrame* frame = m_frame; + while (frame) { + if (pos.x() > 0) { + if (frame->scrollBarValue(Qt::Horizontal) < frame->scrollBarMaximum(Qt::Horizontal)) + break; + } + else if (pos.x() < 0) { + if (frame->scrollBarValue(Qt::Horizontal) > frame->scrollBarMinimum(Qt::Horizontal)) + break; + } + + if (pos.y() > 0) { + if (frame->scrollBarValue(Qt::Vertical) < frame->scrollBarMaximum(Qt::Vertical)) + break; + } + else if (pos.y() < 0) { + if (frame->scrollBarValue(Qt::Vertical) > frame->scrollBarMinimum(Qt::Vertical)) + break; + } + frame = frame->parentFrame(); + } + return (frame)?frame:m_webPage->mainFrame(); +} + +/* +Starts the timer for scrolling smoothly to the destination location . +The timer will do the decelaration while scrolling +*/ +void GWebTouchNavigation::startScrollTimer() +{ + if(!m_scrollTimer) { + m_scrollTimer = new QTimer(this); + connect(m_scrollTimer,SIGNAL(timeout()),this,SLOT(scrollToEdge())); + } + m_scrollTimer->stop(); + m_scrollTimer->start(KAnimationTimeout); +} + +/* +Update the scroll distance for flick gesture. Update the scroll distance upto the edge of the page +*/ +void GWebTouchNavigation::updateFlickScrollDistance() +{ + m_initialSpeed = speed(); + m_initialSpeed.setX(qAbs(m_initialSpeed.x())); + m_initialSpeed.setY(qAbs(m_initialSpeed.y())); + m_flickDirection = findDirectionWithAngle(m_dragPoints.first().iPoint - m_dragPoints.last().iPoint); + m_scrollDistance.setX((m_initialSpeed.x() * m_initialSpeed.x())/( 2 * KDeccelaration)); + m_scrollDistance.setY((m_initialSpeed.y() * m_initialSpeed.y())/( 2 * KDeccelaration)); + + m_scrollDistance.setX(m_view->zoomFactor() * m_scrollDistance.x() / m_view->initialScale()); + m_scrollDistance.setY(m_view->zoomFactor() * m_scrollDistance.y() / m_view->initialScale()); + + QSize viewSize = m_webPage->viewportSize(); + QSize contentSize = m_frame->contentsSize(); + QPoint documentViewPoint = QPoint(viewSize.width(),viewSize.height()) + m_frame->scrollPosition(); + QPoint documentTouchPoint = m_touchPosition + m_frame->scrollPosition();; + + switch(m_flickDirection) + { + case DOWN : + m_scrollDistance.setX(0); + m_scrollDistance.setY(-(m_scrollDistance.y())); + + if(m_scrollDistance.y() < -(documentTouchPoint.y() - m_touchPosition.y())) + m_scrollDistance.setY(-(documentTouchPoint.y() - m_touchPosition.y())); + break; + case UP : + m_scrollDistance.setX(0); + if(m_scrollDistance.y() > (contentSize.height() - documentViewPoint.y())) + m_scrollDistance.setY(contentSize.height() - documentViewPoint.y()); + break; + case RIGHT: + m_scrollDistance.setX(-(m_scrollDistance.x())); + if(m_scrollDistance.x() < -(documentTouchPoint.x() - m_touchPosition.x())) + m_scrollDistance.setX(-(documentTouchPoint.x() - m_touchPosition.x())); + m_scrollDistance.setY(0); + break; + case LEFT: + m_scrollDistance.setY(0); + if(m_scrollDistance.x() > (contentSize.width() - documentViewPoint.x())) + m_scrollDistance.setX(contentSize.width() - documentViewPoint.x()); + break; + case BOTTOMRIGHT: + m_scrollDistance.setX(-(m_scrollDistance.x())); + m_scrollDistance.setY(-(m_scrollDistance.y())); + if(m_scrollDistance.x() < -(documentTouchPoint.x()-m_touchPosition.x())) + m_scrollDistance.setX(-(documentTouchPoint.x()-m_touchPosition.x())); + if(m_scrollDistance.y() < -((documentTouchPoint.y()-m_touchPosition.y())/2)) + m_scrollDistance.setY(-((documentTouchPoint.y()-m_touchPosition.y())/2)); + break; + case BOTTOMLEFT: + m_scrollDistance.setY(-(m_scrollDistance.y())); + if(m_scrollDistance.x() > contentSize.width()-documentViewPoint.x()) + m_scrollDistance.setX(contentSize.width()-documentViewPoint.x()); + if(m_scrollDistance.y() < (-((documentTouchPoint.y()-m_touchPosition.y())/2))) + m_scrollDistance.setY(-((documentTouchPoint.y()-m_touchPosition.y())/2)); + break; + case TOPLEFT: + if(m_scrollDistance.x() > (contentSize.width()-documentViewPoint.x())) + m_scrollDistance.setX(contentSize.width()-documentViewPoint.x()); + if(m_scrollDistance.y() > ((contentSize.height()-documentViewPoint.y())/2)) + m_scrollDistance.setY((contentSize.height()-documentViewPoint.y())/2); + break; + case TOPRIGHT: + m_scrollDistance.setX(-(m_scrollDistance.x())); + if(m_scrollDistance.x() < (-(documentTouchPoint.x()-m_touchPosition.x()))) + m_scrollDistance.setX(-(documentTouchPoint.x()-m_touchPosition.x())); + if(m_scrollDistance.y() > ((contentSize.height()-documentViewPoint.y())/2)) + m_scrollDistance.setY((contentSize.height()-documentViewPoint.y())/2); + break; + } +} + +/* +Distinguishes b/w swipe and flick +*/ +bool GWebTouchNavigation::isFlick() +{ + bool flick = false; + QPoint moveSpeed = speed(); + int xSpeed = moveSpeed.x(); + int ySpeed = moveSpeed.y(); + + flick = (qAbs(xSpeed) > KFlickSpeed || + qAbs(ySpeed) > KFlickSpeed); + + return flick; +} + +/* +Calculates the speed of te scroll along x-axis and y-axis +*/ +QPoint GWebTouchNavigation::speed() +{ + // Speed is only evaluated at the end of the swipe + QPoint dragSpeed(0,0); + qreal time = dragTime() / KMilliSecond; + if (time > 0) { + QPoint distance = currentPos() - previousPos(); + dragSpeed.setX((distance.x()) / time); + dragSpeed.setY((distance.y()) / time); + } + return dragSpeed; +} + +/* +Returns the last point in the m_dragPoints list +*/ +QPoint GWebTouchNavigation::currentPos() +{ + return m_dragPoints[m_dragPoints.size()-1].iPoint; +} + +/* +Returns the first point in the m_dragPoints list +*/ +QPoint GWebTouchNavigation::previousPos() +{ + return m_dragPoints[0].iPoint; +} + +/* +Finds the time difference b/w the first and last dragpoint +*/ +qreal GWebTouchNavigation::dragTime() const +{ + if(m_dragPoints.isEmpty()) + return 0.0; + else + return m_dragPoints[0].iTime.msecsTo(m_dragPoints[m_dragPoints.size()-1].iTime); +} + +/*! +Find the flick direction with respect to angle of flick +*/ +Direction GWebTouchNavigation::findDirectionWithAngle(const QPoint& diff) +{ + Direction direction; + int xDelta = -diff.x(); + int yDelta = -diff.y(); + + qreal angle = findAngle(xDelta, yDelta); + if(isNear(angle, 60.0, 120.0)) { + direction = DOWN; + } + else if(isNear(angle, 150.0, 210.0)) { + direction = LEFT; + } + else if(isNear(angle, 240.0, 300.0)) { + direction = UP; + } + else if(360.0 - KAngleTolerance <= angle || angle <= KAngleTolerance) { + direction = RIGHT; + } + else if(isNear(angle, 30.0, 60.0)) { + direction = BOTTOMRIGHT; + } + else if(isNear(angle, 120.0, 150.0)) { + direction = BOTTOMLEFT; + } + else if(isNear(angle, 210.0, 240.0)) { + direction = TOPLEFT; + } + else if(isNear(angle, 300.0, 330.0)) { + direction = TOPRIGHT; + } + + return direction; +} + +/* +Check the angle is in the range of aMinAngle and aMaxAngle +*/ +bool GWebTouchNavigation::isNear(qreal angleUnderTest, qreal minAngle, qreal maxAngle) +{ + return (minAngle < angleUnderTest) && (angleUnderTest <= maxAngle); +} + +/* +Find the angle from x and y displacement +*/ +qreal GWebTouchNavigation::findAngle(const int& xDelta,const int& yDelta) +{ + qreal angle = 0; + qreal hypotenuse = qSqrt(xDelta*xDelta + yDelta*yDelta); + + if(hypotenuse != 0) { + angle = qAcos(xDelta / hypotenuse); + + if(yDelta < 0) { + angle = (2 * PI) - angle; + } + } + + return (angle * 180) / PI; +} + +int GWebTouchNavigation::roundOff(qreal num) +{ + return (num + 0.5); +} + +/* +Finds out the enclosing block +*/ +QRect GWebTouchNavigation::findEnclosingBlock(QMouseEvent* ev) +{ + QWebHitTestResult htr = getHitTestResult(ev); + QRect rect = htr.enclosingBlockElement().geometry(); + return rect; +} + +/* +Gets the hitTestResult for a particular event +*/ +QWebHitTestResult GWebTouchNavigation::getHitTestResult(QMouseEvent* ev) +{ + QPoint pt = ev->pos(); + QWebFrame* frame = m_webPage->frameAt(pt); + frame = (frame) ? frame : m_webPage->mainFrame(); + QWebHitTestResult htr = frame->hitTestContent(pt); + return htr; +} + +/* +Finds out the scroll distance associated with a swipe +*/ +void GWebTouchNavigation::calculateActualScrollDistance() +{ + m_actualScrollDistance.setX(0); + m_actualScrollDistance.setY(0); + if (!m_dragPoints.isEmpty()) { + QPoint distance = previousPos() - currentPos(); + if (qAbs(distance.x()) > 0 || qAbs(distance.y()) > 0) { + m_actualScrollDistance = distance / m_dragPoints.size() * 2; + } + } +} + +/* +In the case of slow swipe, if the destination location lies within the threshold area, then +we need to align the block to te left edge of the mobile screen. This method finds out the +new scroll distance +*/ +void GWebTouchNavigation::setNewScrollDistance(QPoint blockCanvasPoint, int thresholdCheckVal) +{ + m_scrollDistance.setX(m_actualScrollDistance.x()); + m_scrollDistance.setY(m_actualScrollDistance.y()); + if(blockCanvasPoint.x() > 0) { + //Checks whether the block falls within the threshold after right to left swipe + if( (thresholdCheckVal < KThresholdForRightToLeftMotion) && (thresholdCheckVal > 0)) { + //if the block is within the threshold range already and the user tries + //to swipe from left to right , then let it move to right. Dont try to + //snap to the left edge . + if(m_actualScrollDistance.x() > 0) { + m_scrollDistance.setX(blockCanvasPoint.x() - XAlignVal); + } + } + } + else { + //Checks whether the block falls within the threshold after left to right swipe + if( (thresholdCheckVal > KThresholdForLeftToRightMotion) && (thresholdCheckVal < 0)) { + //if the block is within the threshold range already and the user tries + //to swipe from right to left , then let it move to left. Dont try to + //snap to the left edge . + if (m_actualScrollDistance.x() < 0) { + m_scrollDistance.setX(blockCanvasPoint.x() - XAlignVal); + } + } + + } +} + + +/* +SLOT associated with the timer to adjust the scroll to the edge +*/ +void GWebTouchNavigation::scrollToEdge() +{ + m_prevPoint.setX(m_scrollDistance.x()); + m_prevPoint.setY(m_scrollDistance.y()); + + m_scrollDistance.setX(m_scrollDistance.x() * KDecelerationFactor); + m_scrollDistance.setY(m_scrollDistance.y() * KDecelerationFactor); + + //round off the values + m_scrollDistance.setX(roundOff(m_scrollDistance.x())); + m_scrollDistance.setY(roundOff(m_scrollDistance.y())); + + int diffX = m_prevPoint.x() - m_scrollDistance.x(); + int diffY = m_prevPoint.y() - m_scrollDistance.y(); + + if (((m_scrollDistance.x() == 0) && (m_scrollDistance.y() == 0)) + || ((diffX == 0) && (diffY == 0))) { + scrollCurrentFrame(m_prevPoint.x(), m_prevPoint.y()); + m_scrollTimer->stop(); + return; + } + scrollCurrentFrame(diffX, diffY); +} +void GWebTouchNavigation::timerControl() +{ + stopTimer();// stop timer as soon as timeout + emit longPressEvent(); +} +void GWebTouchNavigation::startTimer() +{ + m_longPressTimer = new QTimer(this); + connect(m_longPressTimer,SIGNAL(timeout()),this,SLOT(timerControl())); + m_longPressTimer->start(LONG_PRESS_DURATION); +} +void GWebTouchNavigation::stopTimer() +{ + if(m_longPressTimer){ + m_longPressTimer->stop(); + delete m_longPressTimer; + m_longPressTimer = 0; + } +} +void GWebTouchNavigation::getFocusedElement() +{ + QWebHitTestResult htRes = m_frame->hitTestContent(m_touchPosition); + wrtBrowserDefs::BrowserElementType elType = wrtBrowserUtils::getTypeFromElement(htRes); + emit focusElementChanged(elType); +} + +void GWebTouchNavigation::scrollCurrentFrame (int dx, int dy) +{ + QPoint scrollPosition = m_frame->scrollPosition(); + if (m_frame == m_webPage->mainFrame() && + //scrollPosition.y() < m_chrome->getSnippetRect("UrlSearchChromeId").height()) { + scrollPosition.y() <=0 && + !m_isLoading && + !m_view->view()->currentPageIsSuperPage()) { + qreal slide = m_chrome->slideView(-dy); + dy+=slide; + } + + m_frame->scroll(dx, dy); +} + +void GWebTouchNavigation::setCurrentFrameScrollPosition (QPoint& pos) +{ + QPoint scrollPosition = m_frame->scrollPosition(); + QPoint finalPos = pos; + + + /* emit pageScrollPositionZero singal if it's mainFrame scrolling or scroll to top*/ + if (m_frame == m_webPage->mainFrame() && + //scrollPosition.y() < m_chrome->getSnippetRect("UrlSearchChromeId").height()) { + scrollPosition.y() <=0 && + !m_isLoading && + !m_view->view()->currentPageIsSuperPage()) { + + int dy = scrollPosition.y() - pos.y(); + qreal slide = m_chrome->slideView(-dy); + dy+=slide; + finalPos.setY(scrollPosition.y() - dy); + } + + m_frame->setScrollPosition(finalPos); +} + +void GWebTouchNavigation::onLoadStarted() +{ + m_isLoading = true; +} +void GWebTouchNavigation::onLoadFinished(bool ok) +{ + m_isLoading = false; +} +void GWebTouchNavigation::setPage( QWebPage * page) +{ + disconnect(m_webPage, SIGNAL(loadStarted()), this, SLOT(onLoadStarted())); + disconnect(m_webPage, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool))); + m_webPage = page ; + connect(m_webPage, SIGNAL(loadStarted()), this, SLOT(onLoadStarted())); + connect(m_webPage, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool))); +} +}