browsercore/core/webdirectionalnavigation.cpp
branchGCC_SURGE
changeset 8 2e16851ffecd
parent 2 bf4420e9fa4d
parent 6 1c3b8676e58c
equal deleted inserted replaced
2:bf4420e9fa4d 8:2e16851ffecd
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include "qmath.h"
       
    20 #include "qpainter.h"
       
    21 #include "qwebpage.h"
       
    22 #include "qwebview.h"
       
    23 #include "qwebframe.h"
       
    24 #include "qwebelement.h"
       
    25 #include "webdirectionalnavigation.h"
       
    26 
       
    27 namespace WRT {
       
    28 
       
    29 const int KInitialSize = 20;
       
    30 const int KNormalScrollRange = 40;
       
    31 const int KFullStep = 14;
       
    32 const double KMaxDistance = 1000000;
       
    33 
       
    34 /*!
       
    35     \class WebDirectionalNavigation
       
    36     \since cwrt 1.0
       
    37     \brief cwrt navigation.
       
    38 
       
    39     \sa WebNavigation, WebTouchNavigation, WebCursorNavigation, WebHtmlTabIndexedNavigation
       
    40 */
       
    41 WebDirectionalNavigation::WebDirectionalNavigation(QWebPage* webPage,QObject* view) :
       
    42     m_webPage(webPage)
       
    43 ,   m_view(view)
       
    44 {
       
    45     install();
       
    46 }
       
    47 
       
    48 WebDirectionalNavigation::~WebDirectionalNavigation()
       
    49 {
       
    50     uninstall();
       
    51 }
       
    52 
       
    53 void WebDirectionalNavigation::install()
       
    54 {
       
    55     m_view->installEventFilter(this);
       
    56     //initiallayoutCompleted();
       
    57 }
       
    58 
       
    59 void WebDirectionalNavigation::uninstall()
       
    60 {
       
    61     if (m_view)
       
    62         m_view->removeEventFilter(this);
       
    63 }
       
    64 
       
    65 void WebDirectionalNavigation::initiallayoutCompleted()
       
    66 {
       
    67     m_webElement = m_webPage->currentFrame()->findFirstElement(QString("a,input,select,textarea,object"));
       
    68     if (!m_webElement.isNull()) {
       
    69         m_webElement.setFocus();
       
    70         m_focusPoint = m_webElement.geometry().topLeft();
       
    71         setCurrentFrameScrollPosition(m_focusPoint);
       
    72     }
       
    73 }
       
    74 
       
    75 double WebDirectionalNavigation::calculateElementDistance(int direction, const QRect& possibleRect) 
       
    76 {
       
    77     // Roughly based on this algorithm http://www.w3.org/TR/WICD/#nav-distance-fkt 
       
    78     // Deviates in that the overlap is not calculated. Instead, if there is any overlap
       
    79     // selection is restricted to x or y direction only. This helps for differnt size elments
       
    80     // all lying on the same plane, but might mess up backtracking the naviation path.
       
    81 
       
    82     QRect focusedRect(0,0,1,1);
       
    83     if (!m_webElement.isNull())
       
    84         focusedRect = QRect(m_webElement.geometry());
       
    85 
       
    86     //calculate the next focuspoint
       
    87     switch (direction) {
       
    88     case Qt::Key_Up:
       
    89         if (m_focusPoint.x() < focusedRect.x())
       
    90             m_focusPoint.setX(focusedRect.x());
       
    91         else if (m_focusPoint.x() > focusedRect.right())
       
    92             m_focusPoint.setX(focusedRect.right());
       
    93         m_focusPoint.setY(focusedRect.y());
       
    94         break;
       
    95     case Qt::Key_Down:
       
    96         if (m_focusPoint.x() < focusedRect.x())
       
    97             m_focusPoint.setX(focusedRect.x());
       
    98         else if (m_focusPoint.x() > focusedRect.right())
       
    99             m_focusPoint.setX(focusedRect.right());
       
   100         m_focusPoint.setY(focusedRect.bottom());
       
   101         break;
       
   102     case Qt::Key_Right:
       
   103         m_focusPoint.setX(focusedRect.right());
       
   104         if (m_focusPoint.y() < focusedRect.y())
       
   105             m_focusPoint.setY(focusedRect.y());
       
   106         else if (m_focusPoint.y() > focusedRect.bottom())
       
   107             m_focusPoint.setY(focusedRect.bottom());
       
   108         break;
       
   109     case Qt::Key_Left:
       
   110         m_focusPoint.setX(focusedRect.x());
       
   111         if (m_focusPoint.y() < focusedRect.y())
       
   112             m_focusPoint.setY(focusedRect.y());
       
   113         else if (m_focusPoint.y() > focusedRect.bottom())
       
   114             m_focusPoint.setY(focusedRect.bottom());
       
   115         break;
       
   116     }
       
   117 
       
   118     // Make sure the rectangle falls within the search area
       
   119     if (direction == Qt::Key_Up && possibleRect.bottom() > m_focusPoint.y()
       
   120         || direction == Qt::Key_Down && possibleRect.y() < m_focusPoint.y()
       
   121         || direction == Qt::Key_Right && possibleRect.x() < m_focusPoint.x()
       
   122         || direction == Qt::Key_Left && possibleRect.right() > m_focusPoint.x())
       
   123         return KMaxDistance;
       
   124 
       
   125     // The absolute distance (dx or dy) on the navigation axis between the opposing edges of the currently focused
       
   126     // element and each of the candidates.
       
   127     double distanceX = 0;
       
   128     double distanceY = 0;
       
   129 
       
   130     if (direction == Qt::Key_Up) {
       
   131         // adjust the x distance based on the closest edge
       
   132         if (m_focusPoint.x() < possibleRect.x()) 
       
   133             distanceX = possibleRect.x() - m_focusPoint.x();
       
   134         else if (m_focusPoint.x() > possibleRect.right())
       
   135             distanceX = m_focusPoint.x() - possibleRect.right();
       
   136        distanceY = m_focusPoint.y() - possibleRect.bottom();
       
   137     } else if (direction == Qt::Key_Down) {
       
   138         // adjust the x distance based on the closest edge
       
   139         if (m_focusPoint.x() < possibleRect.x()) 
       
   140             distanceX = possibleRect.x() - m_focusPoint.x();
       
   141         else if (m_focusPoint.x() > possibleRect.right())
       
   142             distanceX = m_focusPoint.x() - possibleRect.right();
       
   143         distanceY = possibleRect.y() - m_focusPoint.y();
       
   144     } else if (direction == Qt::Key_Right) {
       
   145         distanceX = possibleRect.x() - m_focusPoint.x();
       
   146         // adjust the y distance based on the closest edge
       
   147         if (m_focusPoint.y() < possibleRect.y())
       
   148             distanceY = possibleRect.y() - m_focusPoint.y();
       
   149         else if (m_focusPoint.y() > possibleRect.bottom())
       
   150             distanceY = m_focusPoint.y() - possibleRect.bottom();
       
   151     } else if (direction == Qt::Key_Left) {
       
   152         distanceX = m_focusPoint.x() - possibleRect.right();
       
   153         // adjust the y distance based on the closest edge
       
   154         if (m_focusPoint.y() < possibleRect.y()) 
       
   155             distanceY = possibleRect.y() - m_focusPoint.y();
       
   156         else if (m_focusPoint.y() > possibleRect.bottom())
       
   157             distanceY = m_focusPoint.y() - possibleRect.bottom();
       
   158     }
       
   159 
       
   160     // The absolute distance on the axis orthogonal to the navigation axis between
       
   161     // the opposing edges of currently focused element and each of candidates
       
   162     double displacement = 0;
       
   163 
       
   164     // The euclidean distance
       
   165     double euclideanDist = sqrt(distanceX * distanceX + distanceY * distanceY);
       
   166 
       
   167     // Area of the document to search
       
   168     QRect searchRect(m_webPage->currentFrame()->scrollPosition(),m_webPage->viewportSize());
       
   169 
       
   170     if (direction == Qt::Key_Up || direction == Qt::Key_Down) {
       
   171         // if the rectangles are on the same plane set the euclideanDist to zero to favor this navigation
       
   172         if ((possibleRect.x() == focusedRect.x())
       
   173             || (possibleRect.right() > focusedRect.x() && possibleRect.right() < focusedRect.right())
       
   174             || (possibleRect.x() > focusedRect.x() && possibleRect.x() < focusedRect.right())
       
   175             || (possibleRect.x() > focusedRect.x() && possibleRect.right() < focusedRect.right())
       
   176             || (possibleRect.x() < focusedRect.x() && possibleRect.right() > focusedRect.right())) {
       
   177             euclideanDist = 0;
       
   178             distanceX = 0;
       
   179         } else {
       
   180             // displacement is added for being orthogonally far from the current rectangle.
       
   181             if (possibleRect.x() > focusedRect.right())
       
   182                 displacement = possibleRect.x() - focusedRect.right();
       
   183             if (possibleRect.right() < focusedRect.x())
       
   184                 displacement = focusedRect.x() - possibleRect.right();
       
   185 
       
   186             // This is a little sketchy, but if the rectangles are all 
       
   187             // touching set the displacment to 1 so rects on the same plane win
       
   188             if (displacement == 0)
       
   189                 displacement = 1;
       
   190 
       
   191             // if the focus is within the view limit the diagonal search by half the search rectangle
       
   192             if (searchRect.intersects(focusedRect) && (displacement > (searchRect.width()/2)))
       
   193                 return KMaxDistance;
       
   194  
       
   195         }
       
   196 
       
   197     } else if (direction == Qt::Key_Left || direction == Qt::Key_Right) {
       
   198 
       
   199         // if the rectangles are on the same plane set the euclideanDist to zero to favor this navigation
       
   200         if ((possibleRect.y() == focusedRect.y())
       
   201             || (possibleRect.bottom() > focusedRect.y() && possibleRect.bottom() < focusedRect.bottom())
       
   202             || (possibleRect.y() > focusedRect.y() && possibleRect.y() < focusedRect.bottom())
       
   203             || (possibleRect.y() > focusedRect.y() && possibleRect.bottom() < focusedRect.bottom())
       
   204             || (possibleRect.y() < focusedRect.y() && possibleRect.bottom() > focusedRect.bottom())) {
       
   205            euclideanDist = 0;
       
   206            distanceY = 0; 
       
   207         } else {
       
   208             // displacement is added for being orthogonally far from the current rectangle.
       
   209             if (possibleRect.y() > focusedRect.bottom())
       
   210                 displacement = possibleRect.y() - focusedRect.bottom();
       
   211             if (possibleRect.bottom() < focusedRect.y())
       
   212                 displacement = focusedRect.y() - possibleRect.bottom();
       
   213 
       
   214             // This is a little sketchy, but if the rectangles are all 
       
   215             // touching set the displacment to 1 so rects on the same plane win
       
   216             if (displacement == 0) 
       
   217                 displacement = 1;
       
   218 
       
   219             //  if the focus is within the view limit the diagonal search by half the search rectangle
       
   220             if (searchRect.intersects(focusedRect) && (displacement > (searchRect.height()/2)))
       
   221                 return KMaxDistance; 
       
   222         }
       
   223     }
       
   224 
       
   225     return euclideanDist + distanceX + distanceY + 2 * (displacement);
       
   226 }
       
   227 
       
   228 
       
   229 
       
   230 bool WebDirectionalNavigation::nextElementInDirection(int direction)
       
   231 {
       
   232     double bestDistance = KMaxDistance;
       
   233     QWebElement bestElement;
       
   234 #if QT_VERSION < 0x040600
       
   235     QList<QWebElement> elementList = m_webPage->currentFrame()->findAllElements(QString("a,input,select,textarea,object"));
       
   236 #else
       
   237     QList<QWebElement> elementList = m_webPage->currentFrame()->findAllElements(QString("a,input,select,textarea,object")).toList();
       
   238 #endif
       
   239     QList<QWebElement>::iterator it;
       
   240     for (it = elementList.begin(); it != elementList.end(); it++) {
       
   241         QWebElement el(*it);
       
   242         QRect nRect(el.geometry());
       
   243         if (nRect.isValid()) {
       
   244             if (QRect(m_webPage->currentFrame()->scrollPosition(),m_webPage->viewportSize()).intersects(nRect)) {
       
   245                 double distance = calculateElementDistance (direction, nRect);
       
   246                 if (bestDistance > distance) {
       
   247                     bestDistance = distance;
       
   248                     bestElement = el;
       
   249                 }
       
   250             }
       
   251         }
       
   252     }
       
   253 
       
   254     if (!bestElement.isNull() && bestElement != m_webElement) {
       
   255         m_webElement.setStyleProperty("outline", m_webElementStyle);    	  
       
   256         m_webElement = bestElement;
       
   257         m_webElement.setFocus();
       
   258         m_webElementStyle = m_webElement.styleProperty("outline", QWebElement::ComputedStyle);
       
   259         m_webElement.setStyleProperty("outline", "3px ridge rgb(140,140,255)");             
       
   260         scrollFrame(direction);
       
   261         return true;
       
   262     }
       
   263 
       
   264     scrollFrameOneDirection (direction, KNormalScrollRange);
       
   265     return false;
       
   266 }
       
   267 
       
   268 
       
   269 bool WebDirectionalNavigation::eventFilter(QObject *object, QEvent *event)
       
   270 {
       
   271     if (object == m_view) {
       
   272         switch (event->type()) {
       
   273             case QEvent::KeyPress: {
       
   274                 QKeyEvent* ev = static_cast<QKeyEvent*>(event);
       
   275                 if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down 
       
   276                 || ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right )
       
   277                     {
       
   278                     return true;
       
   279                     }
       
   280                 if (ev->key() ==  Qt::Key_Select )
       
   281                     {
       
   282                     QKeyEvent rockerEnterEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
       
   283                     m_webPage->event(&rockerEnterEvent); 
       
   284                     return true; 
       
   285                     }                
       
   286             }
       
   287             break; 
       
   288             case QEvent::KeyRelease: {
       
   289                 QKeyEvent* ev = static_cast<QKeyEvent*>(event);
       
   290                 if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down 
       
   291                 || ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right ) 
       
   292                     {
       
   293                     nextElementInDirection(ev->key());    
       
   294                     return true;
       
   295                     }
       
   296                 if (ev->key() == Qt::Key_Select )
       
   297                     {
       
   298                     QKeyEvent rockerEnterEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
       
   299                     m_webPage->event(&rockerEnterEvent); 
       
   300                     return true; 
       
   301                     }
       
   302             }
       
   303             break;
       
   304             default: 
       
   305                 return false; 
       
   306             break; 
       
   307         } // end of switch statement 
       
   308     }  // end of if statement 
       
   309     return false;
       
   310 }
       
   311 
       
   312 
       
   313 void WebDirectionalNavigation::scrollFrame(int direction)
       
   314 {
       
   315 
       
   316     int xadjust = m_webPage->viewportSize().width()/5;
       
   317     int yadjust = m_webPage->viewportSize().height()/5;
       
   318 
       
   319     switch (direction) {
       
   320         case Qt::Key_Left: {
       
   321             int distanceX =  xadjust - m_webElement.geometry().x();
       
   322             if (distanceX > 0)
       
   323                 scrollCurrentFrame(-distanceX,0);
       
   324         }
       
   325         break;
       
   326         case Qt::Key_Right: {
       
   327             int distanceX = m_webElement.geometry().right() - m_webPage->viewportSize().width()-xadjust;
       
   328             if (distanceX > 0)
       
   329                 scrollCurrentFrame(distanceX,0);
       
   330         }
       
   331         break;
       
   332         case Qt::Key_Up: {
       
   333             int distanceY = yadjust - m_webElement.geometry().y();
       
   334             if (distanceY > 0)
       
   335                 scrollCurrentFrame(0,-distanceY);
       
   336         }
       
   337         break;
       
   338         case Qt::Key_Down: {
       
   339             int distanceY = m_webElement.geometry().bottom() - m_webPage->viewportSize().height()-yadjust;
       
   340             if (distanceY > 0)
       
   341                 scrollCurrentFrame(0,distanceY);
       
   342         }
       
   343         break;
       
   344     }
       
   345 }
       
   346 
       
   347 void WebDirectionalNavigation::scrollFrameOneDirection (int direction, int distance)
       
   348 {
       
   349     int dx = 0;
       
   350     int dy = 0;
       
   351 
       
   352     switch (direction) {
       
   353         case Qt::Key_Up: {
       
   354             dy = -distance;
       
   355         }
       
   356         break;
       
   357         case Qt::Key_Down: {
       
   358             dy = +distance;
       
   359         }
       
   360         break;
       
   361         case Qt::Key_Left: {
       
   362             dx = -distance;
       
   363         }
       
   364         break;
       
   365         case Qt::Key_Right: {
       
   366             dx = distance;
       
   367         }
       
   368         break;
       
   369         default:
       
   370         break;
       
   371     }
       
   372 
       
   373     scrollCurrentFrame(dx, dy);
       
   374 }
       
   375 
       
   376 void WebDirectionalNavigation::scrollCurrentFrame (int dx, int dy)
       
   377 {
       
   378     QPoint scrollPosition = m_webPage->currentFrame()->scrollPosition();
       
   379     m_webPage->currentFrame()->scroll(dx, dy);
       
   380 
       
   381     /* emit pageScrollPositionZero singal if it's mainFrame scrolling or scroll to top*/
       
   382     if (m_webPage->currentFrame() == m_webPage->mainFrame()) {
       
   383         if (scrollPosition.y() == 0 || m_webPage->currentFrame()->scrollPosition().y() == 0) {
       
   384             emit pageScrollPositionZero();
       
   385         }
       
   386     }
       
   387 }
       
   388 
       
   389 void WebDirectionalNavigation::setCurrentFrameScrollPosition (QPoint& pos)
       
   390 {
       
   391     QPoint scrollPosition = m_webPage->currentFrame()->scrollPosition();
       
   392     m_webPage->currentFrame()->setScrollPosition(pos);
       
   393 
       
   394     /* emit pageScrollPositionZero singal if it's mainFrame scrolling or scroll to top*/
       
   395     if (m_webPage->currentFrame() == m_webPage->mainFrame()) {
       
   396         if (scrollPosition.y() == 0 || m_webPage->currentFrame()->scrollPosition().y() == 0) {
       
   397             emit pageScrollPositionZero();
       
   398         }
       
   399     }
       
   400 }
       
   401 
       
   402 }
       
   403