[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-devel
Subject:    [PATCH] konqueror keyboard link navigation
From:       Robert Ewald <robewald () gmx ! net>
Date:       2004-12-01 11:49:30
Message-ID: 200412011249.31073.robewald () gmx ! net
[Download RAW message or body]

Hi,

I made a patch implementing my idea of efficient link navigation in an html 
view. 

It works as follows:

In an HTML view you hit 'd' and a tooltips over each visible link is 
displayed. The tooltip contains a keysequence to access that link. Entering 
the keysequence focusses the link and upon hitting enter the link is followed 
(although that does not work reliably yet).

Implementation details:

Upon hitting 'D' displayLinkNavTooltips() is called. Each Element in the 
HTMLTree is traversed and tested for HREF. If this Element is also visible, 
it is added to the list linkNavElements.

To fill the tooltips the 26 characters in the alphabet are seen as a number, 
with a=0 and z=26. Converting the position of the element in the list to 
base26 yields a keysequence, which is displayed in the tooltip.

linkNavActivated is set to true and on further keypresses, 
dispatchKeyEventHelper fills a search string, which is converted to a decimal 
position in the element list. This Element is then focussed.

Thats what I think I have implemented. At the moment the element is only 
focussed. I would rather have, that the keypresses are enough so no enter is 
needed. Therefore I have to determine the number of characters needed in the 
tooltips. For this task i would need to include "math.h" (or anything 
implementing a log or ln  function). I am reluctant to add a dependency, 
thats why I would like some advice.

Thanks

Robert Ewald

["patch_khtmlview.cpp" (text/x-diff)]

--- ../CVS/kdelibs/khtml/khtmlview.cpp	2004-10-23 15:12:28.000000000 +0200
+++ khtml/khtmlview.cpp	2004-12-01 10:12:26.000000000 +0100
@@ -105,6 +105,47 @@
 
 #ifndef QT_NO_TOOLTIP
 
+static int Base26ToDec(QString s) 
+{
+// this function converts a string to an integer
+// digits are represented *only* by characters
+// the least significant char is left
+// on error -1 is returned
+int num = 0;
+uint i = 0;
+int pot = 1;
+while (i<s.length()){
+    if ((s[i]>='a') && (s[i]<='z')) {        
+            num +=int(s[i].latin1()-'a')*pot;
+    }
+    else
+    {
+        num = -1;
+        break;
+    }
+    ++i;
+    pot = pot*26;
+}
+return num;
+};
+
+static QString DecToBase26(int num) 
+{
+// this function converts an integer to a base26 number
+// digits are represented by *only* by characters (good for keyboard interaction)
+// the least significant char is left
+// i can't imagine any error conditions, so no checks
+QString s;
+int i = 0;
+while(num != 0) {
+    s[i] = 'a'+(char)(num%26);
+    num = num / 26;
+    ++i;
+}
+return s;
+};
+
+
 class KHTMLToolTip : public QToolTip
 {
 public:
@@ -236,6 +277,8 @@
 	accessKeysActivated = false;
 	accessKeysPreActivate = false;
         emitCompletedAfterRepaint = CSNone;
+        linkNavActivated = false;
+        linkNavElements.clear();
     }
     void newScrollTimer(QWidget *view, int tid)
     {
@@ -353,6 +396,9 @@
     bool accessKeysActivated;
     bool accessKeysPreActivate;
     CompletedState emitCompletedAfterRepaint;
+    
+    bool linkNavActivated;
+    QValueList<ElementImpl*> linkNavElements;
 };
 
 #ifndef QT_NO_TOOLTIP
@@ -511,6 +557,8 @@
         findTimeout();
     if (d->accessKeysActivated)
         accessKeysTimeout();
+    if (d->linkNavActivated)
+        linkNavTimeout();
     d->reset();
     killTimers();
     emit cleared();
@@ -746,6 +794,10 @@
             emit hideAccessKeys();
             displayAccessKeys();
         }
+        if (d->linkNavActivated) {
+            emit hideLinkNavTooltips();
+            displayLinkNavTooltips();
+        }
         //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
     }
     else
@@ -1243,6 +1295,44 @@
 	}
 #endif // KHTML_NO_TYPE_AHEAD_FIND
 
+        if(d->linkNavActivated)
+        {                
+                qDebug("linkNavActivated\n");
+                if(_ke->key() == Key_Escape) {
+                    qDebug("ESC pressed");
+                    linkNavTimeout();
+                    _ke->accept();
+                    return;
+                }
+                if(_ke->text().isEmpty() == false) {
+                    d->findString += _ke->text();
+                    qDebug("FindString: %s\n",d->findString.latin1());
+                    int pos = Base26ToDec(d->findString);
+                    if (pos>-1) // Error condition of Base26ToDec
+                    {
+                        qDebug("Position: %d; LinkNavCount: \
%d",pos,d->linkNavElements.count()); +                        NodeImpl* node = \
d->linkNavElements[pos]; +                        if (!node) qDebug("no node \
found\n"); +                        else m_part->xmlDocImpl()->setFocusNode(node);
+                    }
+                    else 
+                        linkNavTimeout();
+                    d->timer.start(3000,true);
+                    _ke->accept();
+                    return;
+                }
+        }
+        else if ((_ke->key() == 'd') || (_ke->key() == 'D'))
+        {   
+            m_part->setStatusBarText(i18n("Link navigation activated -- type text of \
tooltips to navigate"), +                                     \
KHTMLPart::BarDefaultText); +            displayLinkNavTooltips();
+	    d->linkNavActivated = true;
+            d->timer.start(3000,true);
+            grabKeyboard();
+            _ke->accept();
+        }
+
     int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
     if (_ke->state() & Qt::ShiftButton)
       switch(_ke->key())
@@ -3728,6 +3818,60 @@
   moveCaretToLineBoundary(true);
 }
 
+void KHTMLView::displayLinkNavTooltips( )
+{
+        d->linkNavElements.clear();
+        for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = \
n->traverseNextNode()) { +        if( n->isElementNode()) {
+            ElementImpl* en = static_cast< ElementImpl* >( n );
+            DOMString s = en->getAttribute( ATTR_HREF );
+            QRect rec=en->getRect();
+            // find out if this element is visible
+            // the clipper is the parent of viewport, and is the one really visible
+            QPoint tl=viewport()->mapToParent(contentsToViewport(rec.topLeft()));
+            QPoint br=viewport()->mapToParent(contentsToViewport(rec.bottomRight()));
 +            if ((br.x() > 0) && (br.y() > 0)
+            && (tl.x()<visibleWidth()) && (tl.y()<visibleHeight())) {
+                // element is visible
+                d->linkNavElements.append(en);
+            }
+        }
+        }
+
+        QValueList<ElementImpl*>::iterator it;
+        uint i = 0;
+        qDebug("visible Rectangles: %d\n", d->linkNavElements.count());
+        // FIXME: include math.h and then you can calculate how long the string has \
to be +        // float k=(float)log(visibleRects.count())/log(26);
+
+        for (it = d->linkNavElements.begin(); it!=d->linkNavElements.end(); ++it) {
+                    ++i;
+                  QString s = DecToBase26(i);
+		  QLabel *lab=new QLabel(s,viewport(),0,Qt::WDestructiveClose);
+		  connect( this, SIGNAL(hideLinkNavTooltips()), lab, SLOT(close()) );
+		  connect( this, SIGNAL(repaintLinkNavTooltips()), lab, SLOT(repaint()));
+		  lab->setPalette(QToolTip::palette());
+		  lab->setLineWidth(2);
+		  lab->setFrameStyle(QFrame::Box | QFrame::Plain);
+		  lab->setMargin(3);
+		  lab->adjustSize();
+		  addChild(lab,
+			   KMIN((*it)->getRect().left()+(*it)->getRect().width()/2, contentsWidth() - \
lab->width()), +			   KMIN((*it)->getRect().top()+(*it)->getRect().height()/2, \
contentsHeight() - lab->height())); +		  showChild(lab);
+        }
+}
+
+void KHTMLView::linkNavTimeout( )
+{
+    qDebug("linkNavTimeout");
+    d->findString = "";
+    d->linkNavActivated=false;
+    d->linkNavElements.clear();
+    m_part->setStatusBarText(QString::null, KHTMLPart::BarOverrideText);
+    emit hideLinkNavTooltips();
+}
+
 #endif // KHTML_NO_CARET
 
 #undef DEBUG_CARETMODE


["patch_khtmlview.h" (text/x-diff)]

--- ../CVS/kdelibs/khtml/khtmlview.h	2004-10-15 05:47:28.000000000 +0200
+++ khtml/khtmlview.h	2004-12-01 11:31:14.000000000 +0100
@@ -166,6 +166,10 @@
     void displayAccessKeys();
     
     
+    /**
+     * Display keysequences for link navigation in small tooltips 
+     */
+    void displayLinkNavTooltips();
 
 signals:
     void finishedLayout();
@@ -173,6 +177,8 @@
     void zoomView( int );
     void hideAccessKeys();
     void repaintAccessKeys();
+    void hideLinkNavTooltips();
+    void repaintLinkNavTooltips();
 
 protected:
     void clear();
@@ -214,6 +220,7 @@
     void findTimeout();
 #endif // KHTML_NO_TYPE_AHEAD_FIND
     void accessKeysTimeout();
+    void linkNavTimeout();
     
 private:
 


>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic