browsercore/core/webcursornavigation.cpp
changeset 0 1450b09d0cfd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browsercore/core/webcursornavigation.cpp	Tue May 04 12:39:35 2010 +0300
@@ -0,0 +1,313 @@
+/*
+* 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 "qwidget.h"
+#include "qcursor.h"
+#include "qwebpage.h"
+#include "qwebview.h"
+#include "qwebframe.h"
+#include "qwebelement.h"
+#include "webcursornavigation.h"
+#include "webcontentview.h"
+
+namespace WRT {
+
+const int KPageViewScrollRange = 60;
+const int KNormalScrollRange = 40;
+const int KFlipAdjust = 10;
+const int KMiddleStep = 5;
+const int KFullStep = 14;
+const int KMinimumParagraphHeight = 30;
+const int KHitTestDistance = 100;
+
+/*!
+    \class WebCursorNavigation
+    \since cwrt 1.0
+    \brief cwrt navigation.
+
+    \sa WebNavigation, WebTouchNavigation, WebDirectionalNavigation, WebHtmlTabIndexedNavigation
+*/
+WebCursorNavigation::WebCursorNavigation(QWebPage* webPage,QObject* view)
+  : m_webPage(webPage)
+  , m_view(view)
+  , m_cursorPosition(0,0)
+  , m_flipcounter(1)
+  , m_direction(0)
+  , m_lastdirection(0)
+{
+    install();
+}
+
+/*!
+*/
+WebCursorNavigation::~WebCursorNavigation()
+{
+    uninstall();
+}
+
+void WebCursorNavigation::install()
+{
+    m_view->installEventFilter(this);
+    connect(&m_scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimeout()));
+    connect(&m_keypressTimer, SIGNAL(timeout()), this, SLOT(keypressTimeout()));
+    connect(m_webPage, SIGNAL(linkHovered(const QString&, const QString&, const QString &)),
+            this, SLOT(showLinkHover(const QString&, const QString&)));
+}
+
+void WebCursorNavigation::uninstall()
+{
+    disconnect(&m_scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimeout()));
+    disconnect(&m_keypressTimer, SIGNAL(timeout()), this, SLOT(keypressTimeout()));
+    disconnect(m_webPage, SIGNAL(linkHovered(const QString&, const QString&, const QString &)),
+                this, SLOT(showLinkHover(const QString&, const QString&)));
+    m_view->removeEventFilter(this);
+}
+
+bool WebCursorNavigation::eventFilter(QObject *object, QEvent *event)
+{
+    if (object == m_view) {
+        switch (event->type()) {
+            case QEvent::KeyPress: {
+                QKeyEvent* ev = static_cast<QKeyEvent*>(event);
+                keyPressEvent(ev);
+                return ev->isAccepted();
+            }
+            case QEvent::KeyRelease: {
+                QKeyEvent* ev = static_cast<QKeyEvent*>(event);
+                keyReleaseEvent(ev);
+                return ev->isAccepted();
+            }
+            default:
+                break;
+        }
+    }
+    return false;
+}
+
+/*!
+    Timeout for long keypress.
+    \sa scrollTimeout()
+*/
+void WebCursorNavigation::keypressTimeout()
+{
+    if (!m_scrollTimer.isActive())
+      scrollTimeout(); 
+}
+
+/*!
+    Timeout for scroller. Scrolls the page on a timer every 50 miliseconds
+    \sa keypressTimeout()
+*/
+void WebCursorNavigation::scrollTimeout()
+{
+    if (!scroll(m_direction))
+        moveCursor(m_direction);
+
+    if (!m_scrollTimer.isActive())
+        m_scrollTimer.start(50);
+}
+
+/*!
+    If the key is directional, starts the keyPress timer. The cursor navigation is processed on keyReleaseEvent.
+    If the key is <Select> or <Return>, eat the event; we'll do that action once on keyRelease.
+    Otherwise, send the keyPress event onwards to QWebPage.
+*/
+void WebCursorNavigation::keyPressEvent(QKeyEvent* ev)
+{
+    //stop fast scrolling timers
+    m_keypressTimer.stop();
+    m_scrollTimer.stop();
+
+    if (ev->key() == Qt::Key_Up || 
+        ev->key() == Qt::Key_Down || 
+        ev->key() == Qt::Key_Left || 
+        ev->key() == Qt::Key_Right ) {
+
+        if (!m_keypressTimer.isActive())
+            m_keypressTimer.start(300);
+
+        m_direction = ev->key();
+    }
+
+    if (ev->key() == Qt::Key_Return
+        || ev->key() == Qt::Key_Enter
+        || ev->key() == Qt::Key_Select) {
+            Qt::KeyboardModifier modifier = Qt::NoModifier;
+            QWebFrame* webFrame = m_webPage->frameAt(m_cursorPosition);
+            webFrame = (webFrame) ? webFrame : m_webPage->currentFrame();
+            QWebHitTestResult htr = webFrame->hitTestContent(m_cursorPosition);
+            if (htr.element().tagName().toLower().compare("select")==0  && htr.element().hasAttribute("multiple"))
+                modifier = Qt::ControlModifier;
+
+            QMouseEvent evpress(QEvent::MouseButtonPress, m_cursorPosition, Qt::LeftButton, Qt::NoButton, modifier);
+            m_webPage->event(&evpress);
+    }
+
+}
+
+/*!
+    If the key is directional the cursor navigation is processed.
+    If the key is a <Select> or <Return>, send a left button mouse press and release to QWebPage.
+    Otherwise just send the keyRelease event onwards to QWebPage.
+*/
+void WebCursorNavigation::keyReleaseEvent(QKeyEvent* ev)
+{
+    //stop fast scrolling timers
+    m_keypressTimer.stop();
+    m_scrollTimer.stop();
+
+    if (ev->key() == Qt::Key_Up
+        || ev->key() == Qt::Key_Down
+        || ev->key() == Qt::Key_Left
+        || ev->key() == Qt::Key_Right ) {
+
+        if (!scroll(ev->key())) {
+            moveCursor(ev->key());
+            QMouseEvent evmm(QEvent::MouseMove, m_cursorPosition, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
+            m_webPage->event(&evmm);
+        }
+    }
+
+    if (ev->key() == Qt::Key_Return
+        || ev->key() == Qt::Key_Enter
+        || ev->key() == Qt::Key_Select) {
+            Qt::KeyboardModifier modifier = Qt::NoModifier;
+            QWebFrame* webFrame = m_webPage->frameAt(m_cursorPosition);
+            webFrame = (webFrame) ? webFrame : m_webPage->currentFrame();
+            QWebHitTestResult htr = webFrame->hitTestContent(m_cursorPosition);
+            if (htr.element().tagName().toLower().compare("select")==0  && htr.element().hasAttribute("multiple"))
+                modifier = Qt::ControlModifier;
+
+            QMouseEvent evrel(QEvent::MouseButtonRelease, m_cursorPosition, Qt::LeftButton, Qt::NoButton, modifier);
+            m_webPage->event(&evrel);
+    }
+}
+
+/*!
+    Returns true if the cursor is over a editable area
+*/
+bool WebCursorNavigation::isContentEditable()
+{
+    QWebFrame* webFrame = m_webPage->frameAt(m_cursorPosition);
+    webFrame = (webFrame) ? webFrame : m_webPage->currentFrame();
+    QWebHitTestResult htr = webFrame->hitTestContent(m_cursorPosition);
+    return htr.isContentEditable();
+}
+
+/*!
+    Moves the cursor a fixed interval in the given direction
+*/
+void WebCursorNavigation::moveCursor(int direction)
+{
+    QRect rect(0, 0,
+               m_webPage->viewportSize().width(),
+               m_webPage->viewportSize().height());
+
+    switch (direction) {
+        case Qt::Key_Left: {
+            m_flipcounter = (m_lastdirection == Qt::Key_Right) ? ++m_flipcounter : 1;
+            int dx = m_cursorPosition.x() - (KFullStep / m_flipcounter);
+            m_cursorPosition.setX((dx > 0) ? dx : 0);
+        }
+        break;
+        case Qt::Key_Right: {
+            m_flipcounter = (m_lastdirection == Qt::Key_Left) ? ++m_flipcounter : 1;
+            int dx = m_cursorPosition.x() + (KFullStep / m_flipcounter);
+            m_cursorPosition.setX((dx < rect.width() - KFullStep) ? dx : rect.width() - KFullStep);
+        }
+        break;
+        case Qt::Key_Up: {
+            m_flipcounter = (m_lastdirection == Qt::Key_Down) ? ++m_flipcounter : 1;
+            int dy = m_cursorPosition.y() - (KFullStep / m_flipcounter);
+            m_cursorPosition.setY((dy > 0) ? dy : 0);
+        }
+        break;
+        case Qt::Key_Down : {
+            m_flipcounter = (m_lastdirection == Qt::Key_Up) ? ++m_flipcounter : 1;
+            int dy = m_cursorPosition.y() + (KFullStep / m_flipcounter);
+            m_cursorPosition.setY((dy < rect.height() - KFullStep) ? dy : rect.height() - KFullStep);
+        }
+        break;
+    }
+    m_lastdirection = direction;
+    QCursor::setPos(static_cast<WebContentWidget*>(m_view)->mapToGlobal(m_cursorPosition).toPoint());
+}
+
+/*!
+    Scrolls QWebFrame a fixed interval in a given direction.
+*/
+bool WebCursorNavigation::scroll(int direction)
+{
+    QWebFrame* webFrame = m_webPage->frameAt(m_cursorPosition);
+    webFrame = (webFrame) ? webFrame : m_webPage->currentFrame();
+
+    QPoint scrollPosition = webFrame->scrollPosition();
+    QRect rect(QPoint(0,0),m_webPage->viewportSize());
+    int xmargin = 2 * rect.width() / 5;
+    int ymargin = 2 * rect.height() / 5;
+
+    switch (direction) {
+        case Qt::Key_Left :
+            if (m_cursorPosition.x() < rect.x() + xmargin)
+                webFrame->scroll(-KNormalScrollRange, 0);
+        break;
+        case Qt::Key_Right:
+            if (m_cursorPosition.x() > (rect.right() - xmargin))
+                webFrame->scroll(KNormalScrollRange, 0);
+        break;
+        case Qt::Key_Up:
+            if (m_cursorPosition.y() < rect.y() + ymargin)
+                webFrame->scroll(0, -KNormalScrollRange);
+        break;
+        case Qt::Key_Down:
+            if (m_cursorPosition.y() > (rect.bottom() - ymargin))
+                webFrame->scroll(0, KNormalScrollRange);
+        break;
+    }
+
+    if (scrollPosition.y() == 0 || webFrame->scrollPosition().y() == 0) {
+        emit pageScrollPositionZero();
+    }
+    return scrollPosition != webFrame->scrollPosition();
+}
+
+
+/*!
+    Returns the distance to scroll to the nearest edge of a text paragraph.
+*/
+int WebCursorNavigation::getNearestEdge(int scrollRange, int direction)
+{
+    QSize size = m_webPage->viewportSize();
+    //Identify the number of hit tests needed
+    int hitTestCount = size.height() / KHitTestDistance;
+    int x = direction == Qt::Key_Right ? scrollRange : 0;
+    QPoint pos(x,KHitTestDistance);
+    for(int i=0;i<hitTestCount;i++)  {
+        QWebHitTestResult htr = m_webPage->mainFrame()->hitTestContent(pos);
+        QRect rect = htr.boundingRect();
+        QPoint scrollPosition = m_webPage->mainFrame()->scrollPosition();
+        int d = direction == Qt::Key_Right ? rect.x() - scrollPosition.x() : pos.x() - rect.x();
+        if(d > 0 && scrollRange > d && rect.height() > KMinimumParagraphHeight)
+           scrollRange = d;
+        pos = QPoint(pos.x(), pos.y() + (i + 1) * KHitTestDistance);
+    }
+
+    return scrollRange;
+}
+}
+