[SCM] WebKit Debian packaging branch, debian/unstable, updated. debian/1.1.15-1-40151-g37bb677

kocienda kocienda at 268f45cc-cd09-0410-ab3c-d52691b4dbfc
Sat Sep 26 08:24:34 UTC 2009


The following commit has been merged in the debian/unstable branch:
commit 6db1ada94645764bcc38d5c6081747561d0021d7
Author: kocienda <kocienda at 268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Thu Jan 29 00:22:29 2004 +0000

            Reviewed by Hyatt
    
    	A small collection of fixes and improvements to editing.
    
    	Added a forwarding header for dom_position.h
    	Added some debugging output to KHTMLSelection
    	Added support for left arrow navigation.
    	Greatly improved support for right arrow navigation.
    	Added include guards to dom_position.h. Oops!
    	Removed pruneEmptyNodes() function from htmlediting.cpp. That was crack.
    	Improved the text insert command so that it splits the text node being
    	    operated on only when necessary.
    	Added support for placing the caret in and arrowing into BR elements.
    	Moved precedesLineBreak/followsLineBreak from RenderText to RenderObject.
    
            * ForwardingHeaders/dom/dom_position.h: Added.
            * khtml/dom/dom_position.h:
            * khtml/editing/htmlediting.cpp:
            (InputTextCommand::apply):
            (DeleteTextCommand::apply):
            * khtml/khtml_selection.cpp:
            (KHTMLSelection::setSelection):
            (KHTMLSelection::alterSelection):
            (KHTMLSelection::update):
            (KHTMLSelection::previousCharacterPosition):
            (KHTMLSelection::nextCharacterPosition):
            (startAndEndLineNodesIncludingNode):
            (KHTMLSelection::debugRenderer):
            (KHTMLSelection::debugPosition):
            * khtml/khtml_selection.h:
            * khtml/rendering/render_br.cpp:
            (RenderBR::RenderBR):
            (RenderBR::position):
            (RenderBR::caretMaxOffset):
            (RenderBR::caretPos):
            * khtml/rendering/render_br.h:
            (khtml::RenderBR::xPos):
            (khtml::RenderBR::yPos):
            (khtml::RenderBR::height):
            * khtml/rendering/render_object.cpp:
            (RenderObject::precedesLineBreak):
            (RenderObject::followsLineBreak):
            (RenderObject::isEditable):
            * khtml/rendering/render_object.h:
            * khtml/rendering/render_text.cpp:
            (RenderText::caretPos):
            (RenderText::position):
            (RenderText::caretMaxOffset):
            * khtml/rendering/render_text.h:
            * khtml/xml/dom_elementimpl.cpp:
            (ElementImpl::defaultEventHandler):
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@6002 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/WebCore/ChangeLog-2005-08-23 b/WebCore/ChangeLog-2005-08-23
index 3547f3f..a5036dd 100644
--- a/WebCore/ChangeLog-2005-08-23
+++ b/WebCore/ChangeLog-2005-08-23
@@ -1,3 +1,57 @@
+2004-01-28  Ken Kocienda  <kocienda at apple.com>
+
+        Reviewed by Hyatt
+
+	A small collection of fixes and improvements to editing.
+
+	Added a forwarding header for dom_position.h
+	Added some debugging output to KHTMLSelection
+	Added support for left arrow navigation.
+	Greatly improved support for right arrow navigation.
+	Added include guards to dom_position.h. Oops!
+	Removed pruneEmptyNodes() function from htmlediting.cpp. That was crack.
+	Improved the text insert command so that it splits the text node being
+	    operated on only when necessary.
+	Added support for placing the caret in and arrowing into BR elements.
+	Moved precedesLineBreak/followsLineBreak from RenderText to RenderObject.	
+
+        * ForwardingHeaders/dom/dom_position.h: Added.
+        * khtml/dom/dom_position.h:
+        * khtml/editing/htmlediting.cpp:
+        (InputTextCommand::apply):
+        (DeleteTextCommand::apply):
+        * khtml/khtml_selection.cpp:
+        (KHTMLSelection::setSelection):
+        (KHTMLSelection::alterSelection):
+        (KHTMLSelection::update):
+        (KHTMLSelection::previousCharacterPosition):
+        (KHTMLSelection::nextCharacterPosition):
+        (startAndEndLineNodesIncludingNode):
+        (KHTMLSelection::debugRenderer):
+        (KHTMLSelection::debugPosition):
+        * khtml/khtml_selection.h:
+        * khtml/rendering/render_br.cpp:
+        (RenderBR::RenderBR):
+        (RenderBR::position):
+        (RenderBR::caretMaxOffset):
+        (RenderBR::caretPos):
+        * khtml/rendering/render_br.h:
+        (khtml::RenderBR::xPos):
+        (khtml::RenderBR::yPos):
+        (khtml::RenderBR::height):
+        * khtml/rendering/render_object.cpp:
+        (RenderObject::precedesLineBreak):
+        (RenderObject::followsLineBreak):
+        (RenderObject::isEditable):
+        * khtml/rendering/render_object.h:
+        * khtml/rendering/render_text.cpp:
+        (RenderText::caretPos):
+        (RenderText::position):
+        (RenderText::caretMaxOffset):
+        * khtml/rendering/render_text.h:
+        * khtml/xml/dom_elementimpl.cpp:
+        (ElementImpl::defaultEventHandler):
+
 2004-01-28  David Hyatt  <hyatt at apple.com>
 
 	Fix for 3537694, make blocks for parents of inline children be axobjects.
diff --git a/WebCore/ForwardingHeaders/dom/dom_position.h b/WebCore/ForwardingHeaders/dom/dom_position.h
new file mode 100644
index 0000000..144c1ce
--- /dev/null
+++ b/WebCore/ForwardingHeaders/dom/dom_position.h
@@ -0,0 +1 @@
+#include <dom_position.h>
diff --git a/WebCore/khtml/dom/dom_position.h b/WebCore/khtml/dom/dom_position.h
index 6fb6028..b38ac9c 100644
--- a/WebCore/khtml/dom/dom_position.h
+++ b/WebCore/khtml/dom/dom_position.h
@@ -23,6 +23,9 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
+#ifndef _DOM_Position_h_
+#define _DOM_Position_h_
+
 namespace DOM {
 
 class NodeImpl;
@@ -60,4 +63,6 @@ inline bool operator!=(const DOMPosition &a, const DOMPosition &b)
     return !(a == b);
 }
 
-}; // namespace DOM
\ No newline at end of file
+}; // namespace DOM
+
+#endif // _DOM_Position_h_
\ No newline at end of file
diff --git a/WebCore/khtml/editing/SelectionController.cpp b/WebCore/khtml/editing/SelectionController.cpp
index f54e555..c631e6d 100644
--- a/WebCore/khtml/editing/SelectionController.cpp
+++ b/WebCore/khtml/editing/SelectionController.cpp
@@ -38,17 +38,21 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
 
 #if APPLE_CHANGES
 #include <KWQAssertions.h>
 #include <CoreServices/CoreServices.h>
+
+#define EDIT_DEBUG 0
 #endif
 
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
 using DOM::Range;
@@ -132,6 +136,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *node, long offset)
 	setBaseOffset(offset);
 	setExtentOffset(offset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setSelection(const DOM::Range &r)
@@ -152,6 +159,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *baseNode, long baseOffset, DOM:
 	setBaseOffset(baseOffset);
 	setExtentOffset(extentOffset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setBase(DOM::NodeImpl *node, long offset)
@@ -187,6 +197,7 @@ bool KHTMLSelection::alterSelection(EAlter alter, EDirection dir, ETextElement e
         case BACKWARD:
             switch (elem) {
                 case CHARACTER:
+                    pos = previousCharacterPosition();
                     break;
                 case WORD:
                     break;
@@ -329,7 +340,7 @@ void KHTMLSelection::update()
     m_caretX = newX;
     m_caretY = newY;
     m_caretSize = newSize;
-
+    
     // paint the caret if it is visible
     if (m_visible && m_caretSize != 0) {
         m_caretPaint = true;
@@ -584,51 +595,130 @@ void KHTMLSelection::calculateStartAndEnd(ETextElement select)
     m_startEndValid = true;
 }
 
+DOMPosition KHTMLSelection::previousCharacterPosition()
+{
+    if (!startNode())
+        return DOMPosition();
+
+	NodeImpl *node = startNode();
+	long offset = startOffset() - 1;
+
+    //
+    // Look in this renderer
+    //
+    RenderObject *renderer = node->renderer();
+    if (renderer->isText()) {
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, return the start
+                    return DOMPosition(node, offset);
+                }
+            }
+        }
+    }
+    else {
+        // Offset is in this node, if:
+        // 1. greater than the min offset and less than or equal to the max caret offset
+        // 2. at the start of the editable content of the document
+        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
+            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
+            return DOMPosition(node, offset);
+    }
+
+    //
+    // Look in previous renderer(s)
+    //
+    renderer = renderer->previousEditable();
+    while (renderer) {
+        // Offset is in this node, if:
+        // 1. it is a BR which follows a line break
+        // 2. it is an element with content
+    	if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+    	}
+    	else {
+    		if (renderer->isText()) {
+    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+    			 if (!textRenderer->lastTextBox()) 
+    			 	continue;
+    		}
+            offset = renderer->caretMaxOffset();
+            if (!renderer->precedesLineBreak())
+                offset--;
+            assert(offset >= 0);
+            return DOMPosition(renderer->element(), offset);
+    	}
+        renderer = renderer->previousEditable();
+    }
+
+    // can't move the position
+    return DOMPosition(startNode(), startOffset());
+}
+
 DOMPosition KHTMLSelection::nextCharacterPosition()
 {
-    DOMPosition result;
+    if (!endNode())
+        return DOMPosition();
+
 	NodeImpl *node = endNode();
-	long offset = endOffset();
-    long desiredOffset = offset + 1;
+	long offset = endOffset() + 1;
 
-    if (!node)
-        return result;
-    
     //
     // Look in this renderer
     //
     RenderObject *renderer = node->renderer();
     if (renderer->isText()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            long start = box->m_start;
-            long end = box->m_start + box->m_len;
-            if (desiredOffset > end) {
-                // Skip this node.
-                // It is too early in the text runs to be involved.
-                continue;
-            }
-            else if (desiredOffset >= start && 
-                (desiredOffset < end || (desiredOffset == end && !box->nextTextBox() && !renderer->nextEditable())) ||
-                (desiredOffset == end && textRenderer->precedesLineBreak() && !textRenderer->followsLineBreak())) {
-                // Desired offset is in this node.
-                // Either it is:
-                // 1. at or after the start and before, but not at the end
-                // 2. at the end of a text run and is immediately followed by a line break
-                //    but does not precede a line break
-                // 3. at the end of the editable content of the document
-                return DOMPosition(renderer->element(), desiredOffset);
-            }
-            else if (desiredOffset <= start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in text that is
-                // not rendered. Just return the start of the node.
-                return DOMPosition(renderer->element(), start);
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, if:
+                    // Either it is:
+                    // 1. at or after the start and before, but not at the end
+                    // 2. at the end of a text run and is immediately followed by a line break
+                    // 3. at the end of the editable content of the document
+                    if (offset < end)
+                        return DOMPosition(node, offset);
+                    else if (offset == end && renderer->precedesLineBreak())
+                        return DOMPosition(node, offset);
+                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
+                        return DOMPosition(node, offset);
+                }
+                else if (offset < start) {
+                    // The offset we're looking for is before this node
+                    // this means the offset must be in content that is
+                    // not rendered. Just return the start of the node.
+                    return DOMPosition(node, start);
+                }
             }
         }
     }
-    else if (desiredOffset < renderer->caretMaxOffset() || (desiredOffset == renderer->caretMaxOffset() && !renderer->nextEditable())) {
-        return DOMPosition(node, desiredOffset);
+    else {
+        // Offset is in this node, if:
+        // 1. before the max caret offset
+        // 2. equal to the max caret offset and is immediately preceded by a line break
+        // 3. at the end of the editable content of the document
+        if (offset < renderer->caretMaxOffset() ||
+            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
+            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
+                return DOMPosition(node, offset);
     }
 
     //
@@ -636,7 +726,15 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
     //
     renderer = renderer->nextEditable();
     while (renderer) {
-        if (renderer->isText()) {
+		// Offset is in this node, if:
+		// 1. it is a BR which follows a line break
+		// 2. it is a text element with content
+        // 3. it is a non-text element with content
+		if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+		}
+		else if (renderer->isText()) {
             RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
             if (textRenderer->firstTextBox())
                 return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
@@ -647,8 +745,8 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
         renderer = renderer->nextEditable();
     }
 
-    result = DOMPosition(node, offset);
-    return result;
+    // can't move the position
+    return DOMPosition(endNode(), endOffset());
 }
 
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
@@ -855,4 +953,125 @@ static bool startAndEndLineNodesIncludingNode(DOM::NodeImpl *node, int offset, K
     return false;
 }
 
+void KHTMLSelection::debugRenderer(RenderObject *r, bool selected) const
+{
+    if (r->node()->isElementNode()) {
+        ElementImpl *element = static_cast<ElementImpl *>(r->node());
+        fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->tagName().string().latin1());
+    }
+    else if (r->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(r);
+        if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
+            fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
+            return;
+        }
+        
+        static const int max = 36;
+        QString text = DOMString(textRenderer->string()).string();
+        int textLength = text.length();
+        if (selected) {
+            int offset = 0;
+            if (r->node() == startNode())
+                offset = startOffset();
+            else if (r->node() == endNode())
+                offset = endOffset();
+                
+            int pos;
+            InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
+            text = text.mid(box->m_start, box->m_len);
+            
+            QString show;
+            int mid = max / 2;
+            int caret = 0;
+            
+            // text is shorter than max
+            if (textLength < max) {
+                show = text;
+                caret = pos;
+            }
+            
+            // too few characters to left
+            else if (pos - mid < 0) {
+                show = text.left(max - 3) + "...";
+                caret = pos;
+            }
+            
+            // enough characters on each side
+            else if (pos - mid >= 0 && pos + mid <= textLength) {
+                show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
+                caret = mid;
+            }
+            
+            // too few characters on right
+            else {
+                show = "..." + text.right(max - 3);
+                caret = pos - (textLength - show.length());
+            }
+            
+            show = show.replace("\n", " ");
+            show = show.replace("\r", " ");
+            fprintf(stderr, "==> #text : %s at %d\n", show.latin1(), pos);
+            fprintf(stderr, "           ");
+            for (int i = 0; i < caret; i++)
+                fprintf(stderr, " ");
+            fprintf(stderr, "^\n");
+        }
+        else {
+            if ((int)text.length() > max)
+                text = text.left(max - 3) + "...";
+            else
+                text = text.left(max);
+            fprintf(stderr, "    #text : %s\n", text.latin1());
+        }
+    }
+}
+
+void KHTMLSelection::debugPosition() const
+{
+    if (!startNode())
+        return;
+
+    static int context = 5;
+    
+    RenderObject *r = 0;
+
+    fprintf(stderr, "KHTMLSelection =================\n");
+    
+    int back = 0;
+    r = startNode()->renderer();
+    for (int i = 0; i < context; i++, back++) {
+        if (r->previousRenderer())
+            r = r->previousRenderer();
+        else
+            break;
+    }
+    for (int i = 0; i < back; i++) {
+        debugRenderer(r, false);
+        r = r->nextRenderer();
+    }
+
+
+    fprintf(stderr, "\n");
+
+    if (startNode() == endNode())
+        debugRenderer(startNode()->renderer(), true);
+    else
+        for (r = startNode()->renderer(); r && r != endNode()->renderer(); r = r->nextRenderer())
+            debugRenderer(r, true);
+    
+    fprintf(stderr, "\n");
+    
+    r = endNode()->renderer();
+    for (int i = 0; i < context; i++) {
+        if (r->nextRenderer()) {
+            r = r->nextRenderer();
+            debugRenderer(r, false);
+        }
+        else
+            break;
+    }
+
+    fprintf(stderr, "================================\n");
+}
+
 #endif
diff --git a/WebCore/khtml/editing/SelectionController.h b/WebCore/khtml/editing/SelectionController.h
index e073149..e888924 100644
--- a/WebCore/khtml/editing/SelectionController.h
+++ b/WebCore/khtml/editing/SelectionController.h
@@ -41,6 +41,10 @@ namespace DOM {
     class Range;
 };
 
+namespace khtml {
+    class RenderObject;
+}
+
 class KHTMLSelection : public QObject
 {
   Q_OBJECT
@@ -81,7 +85,10 @@ public:
 
     void setVisible(bool flag=true);
     bool visible() const { return m_visible; }
-    
+
+    DOM::DOMPosition previousCharacterPosition();
+    DOM::DOMPosition nextCharacterPosition();
+        
     void invalidate();
     
     bool isEmpty() const;
@@ -97,12 +104,9 @@ public:
     
     friend class KHTMLPart;
 
-    void dump() {
-        fprintf(stderr, "selection: %p:%d ; %p:%d (%p:%d ; %p:%d)\n", 
-            m_baseNode, m_baseOffset, m_extentNode, m_extentOffset,
-            startNode(), startOffset(), endNode(), endOffset());
-    }
-    
+    void debugPosition() const;
+    void debugRenderer(khtml::RenderObject *r, bool selected) const;
+
 private:
     void setPart(KHTMLPart *part);
 
@@ -127,8 +131,6 @@ private:
 
     void calculateStartAndEnd(ETextElement select=CHARACTER);
     
-    DOM::DOMPosition nextCharacterPosition();
-    
     KHTMLPart *m_part;            // part for this selection
 
     DOM::NodeImpl *m_baseNode;    // base node for the selection
diff --git a/WebCore/khtml/editing/htmlediting.cpp b/WebCore/khtml/editing/htmlediting.cpp
index c9dda8d..563e7b0 100644
--- a/WebCore/khtml/editing/htmlediting.cpp
+++ b/WebCore/khtml/editing/htmlediting.cpp
@@ -32,6 +32,7 @@
 #include "khtmlview.h"
 #include "khtml_part.h"
 #include "khtml_selection.h"
+#include "dom/dom_position.h"
 #include "rendering/render_object.h"
 #include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
@@ -81,52 +82,6 @@ void EditCommand::deleteSelection()
     m_document->clearSelection();
 }
 
-void EditCommand::pruneEmptyNodes() const
-{
-    return;
-
-#if 0
-    KHTMLView *view = document()->view();
-    if (!view)
-        return;
-
-    KHTMLPart *part = view->part();
-    if (!part)
-        return;
-
-    KHTMLSelection &selection = part->getKHTMLSelection();
-    
-    bool prunedNodes = false;
-    NodeImpl *node = selection.startNode();
-    while (1) {
-        if (node->isTextNode()) {
-            TextImpl *textNode = static_cast<TextImpl *>(node);
-            if (textNode->length() == 0) {
-                node = textNode->traversePreviousNode();
-                removeNode(textNode);
-                prunedNodes = true;
-            }
-            else {
-                break;
-            }
-        }
-        else if (!node->hasChildNodes()) {
-            NodeImpl *n = node;
-            node = node->traversePreviousNode();
-            removeNode(n);
-            prunedNodes = true;
-        }
-        else {
-            break;
-        }
-    }
-    
-    if (prunedNodes) {
-        selection.setSelection(node, node->caretMaxOffset());
-    }
-#endif
-}
-
 void EditCommand::removeNode(DOM::NodeImpl *node) const
 {
     if (!node)
@@ -189,23 +144,38 @@ bool InputTextCommand::apply()
     int exceptionCode;
     
     if (isLineBreak()) {
-        TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.startOffset(), exceptionCode));
-        textNode->deleteData(0, selection.startOffset(), exceptionCode);
         ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
-        textNode->parentNode()->insertBefore(textBeforeNode, textNode, exceptionCode);
-        textNode->parentNode()->insertBefore(breakNode, textNode, exceptionCode);
-        textBeforeNode->deref();
-        breakNode->deref();
         
-        // Set the cursor at the beginning of the node after the split.
-        selection.setSelection(textNode, 0);
+        bool atStart = selection.startOffset() == textNode->renderer()->caretMinOffset();
+        bool atEnd = selection.startOffset() == textNode->renderer()->caretMaxOffset();
+        if (atStart) {
+            textNode->parentNode()->insertBefore(breakNode, textNode, exceptionCode);
+            // Set the cursor at the beginning of text node now following the new BR.
+            selection.setSelection(textNode, 0);
+        }
+        else if (atEnd) {
+            if (textNode->parentNode()->lastChild() == textNode)
+                textNode->parentNode()->appendChild(breakNode, exceptionCode);
+            else
+                textNode->parentNode()->insertBefore(breakNode, textNode->nextSibling(), exceptionCode);
+            // Set the cursor at the beginning of the the BR.
+            selection.setSelection(selection.nextCharacterPosition());
+        }
+        else {
+            TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.startOffset(), exceptionCode));
+            textNode->deleteData(0, selection.startOffset(), exceptionCode);
+            textNode->parentNode()->insertBefore(textBeforeNode, textNode, exceptionCode);
+            textNode->parentNode()->insertBefore(breakNode, textNode, exceptionCode);
+            textBeforeNode->deref();
+            // Set the cursor at the beginning of the node after the BR.
+            selection.setSelection(textNode, 0);
+        }
+        
+        breakNode->deref();
     }
     else {
         textNode->insertData(selection.startOffset(), text(), exceptionCode);
-        // EDIT FIXME: this is a hack for now
-        // advance the cursor
-        int textLength = text().length();
-        selection.setSelection(selection.startNode(), selection.startOffset() + textLength);
+        selection.setSelection(selection.startNode(), selection.startOffset() + text().length());
     }
 
     return true;
@@ -259,7 +229,6 @@ bool DeleteTextCommand::apply()
             TextImpl *textNode = static_cast<TextImpl *>(caretNode);
             textNode->deleteData(offset, 1, exceptionCode);
             selection.setSelection(textNode, offset);
-            pruneEmptyNodes();
             return true;
         }
         
@@ -278,7 +247,6 @@ bool DeleteTextCommand::apply()
             offset = previousLeafNode->caretMaxOffset() - 1;
             textNode->deleteData(offset, 1, exceptionCode);
             selection.setSelection(textNode, offset);
-            pruneEmptyNodes();
             return true;
         }
     }
diff --git a/WebCore/khtml/editing/selection.cpp b/WebCore/khtml/editing/selection.cpp
index f54e555..c631e6d 100644
--- a/WebCore/khtml/editing/selection.cpp
+++ b/WebCore/khtml/editing/selection.cpp
@@ -38,17 +38,21 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
 
 #if APPLE_CHANGES
 #include <KWQAssertions.h>
 #include <CoreServices/CoreServices.h>
+
+#define EDIT_DEBUG 0
 #endif
 
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
 using DOM::Range;
@@ -132,6 +136,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *node, long offset)
 	setBaseOffset(offset);
 	setExtentOffset(offset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setSelection(const DOM::Range &r)
@@ -152,6 +159,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *baseNode, long baseOffset, DOM:
 	setBaseOffset(baseOffset);
 	setExtentOffset(extentOffset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setBase(DOM::NodeImpl *node, long offset)
@@ -187,6 +197,7 @@ bool KHTMLSelection::alterSelection(EAlter alter, EDirection dir, ETextElement e
         case BACKWARD:
             switch (elem) {
                 case CHARACTER:
+                    pos = previousCharacterPosition();
                     break;
                 case WORD:
                     break;
@@ -329,7 +340,7 @@ void KHTMLSelection::update()
     m_caretX = newX;
     m_caretY = newY;
     m_caretSize = newSize;
-
+    
     // paint the caret if it is visible
     if (m_visible && m_caretSize != 0) {
         m_caretPaint = true;
@@ -584,51 +595,130 @@ void KHTMLSelection::calculateStartAndEnd(ETextElement select)
     m_startEndValid = true;
 }
 
+DOMPosition KHTMLSelection::previousCharacterPosition()
+{
+    if (!startNode())
+        return DOMPosition();
+
+	NodeImpl *node = startNode();
+	long offset = startOffset() - 1;
+
+    //
+    // Look in this renderer
+    //
+    RenderObject *renderer = node->renderer();
+    if (renderer->isText()) {
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, return the start
+                    return DOMPosition(node, offset);
+                }
+            }
+        }
+    }
+    else {
+        // Offset is in this node, if:
+        // 1. greater than the min offset and less than or equal to the max caret offset
+        // 2. at the start of the editable content of the document
+        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
+            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
+            return DOMPosition(node, offset);
+    }
+
+    //
+    // Look in previous renderer(s)
+    //
+    renderer = renderer->previousEditable();
+    while (renderer) {
+        // Offset is in this node, if:
+        // 1. it is a BR which follows a line break
+        // 2. it is an element with content
+    	if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+    	}
+    	else {
+    		if (renderer->isText()) {
+    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+    			 if (!textRenderer->lastTextBox()) 
+    			 	continue;
+    		}
+            offset = renderer->caretMaxOffset();
+            if (!renderer->precedesLineBreak())
+                offset--;
+            assert(offset >= 0);
+            return DOMPosition(renderer->element(), offset);
+    	}
+        renderer = renderer->previousEditable();
+    }
+
+    // can't move the position
+    return DOMPosition(startNode(), startOffset());
+}
+
 DOMPosition KHTMLSelection::nextCharacterPosition()
 {
-    DOMPosition result;
+    if (!endNode())
+        return DOMPosition();
+
 	NodeImpl *node = endNode();
-	long offset = endOffset();
-    long desiredOffset = offset + 1;
+	long offset = endOffset() + 1;
 
-    if (!node)
-        return result;
-    
     //
     // Look in this renderer
     //
     RenderObject *renderer = node->renderer();
     if (renderer->isText()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            long start = box->m_start;
-            long end = box->m_start + box->m_len;
-            if (desiredOffset > end) {
-                // Skip this node.
-                // It is too early in the text runs to be involved.
-                continue;
-            }
-            else if (desiredOffset >= start && 
-                (desiredOffset < end || (desiredOffset == end && !box->nextTextBox() && !renderer->nextEditable())) ||
-                (desiredOffset == end && textRenderer->precedesLineBreak() && !textRenderer->followsLineBreak())) {
-                // Desired offset is in this node.
-                // Either it is:
-                // 1. at or after the start and before, but not at the end
-                // 2. at the end of a text run and is immediately followed by a line break
-                //    but does not precede a line break
-                // 3. at the end of the editable content of the document
-                return DOMPosition(renderer->element(), desiredOffset);
-            }
-            else if (desiredOffset <= start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in text that is
-                // not rendered. Just return the start of the node.
-                return DOMPosition(renderer->element(), start);
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, if:
+                    // Either it is:
+                    // 1. at or after the start and before, but not at the end
+                    // 2. at the end of a text run and is immediately followed by a line break
+                    // 3. at the end of the editable content of the document
+                    if (offset < end)
+                        return DOMPosition(node, offset);
+                    else if (offset == end && renderer->precedesLineBreak())
+                        return DOMPosition(node, offset);
+                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
+                        return DOMPosition(node, offset);
+                }
+                else if (offset < start) {
+                    // The offset we're looking for is before this node
+                    // this means the offset must be in content that is
+                    // not rendered. Just return the start of the node.
+                    return DOMPosition(node, start);
+                }
             }
         }
     }
-    else if (desiredOffset < renderer->caretMaxOffset() || (desiredOffset == renderer->caretMaxOffset() && !renderer->nextEditable())) {
-        return DOMPosition(node, desiredOffset);
+    else {
+        // Offset is in this node, if:
+        // 1. before the max caret offset
+        // 2. equal to the max caret offset and is immediately preceded by a line break
+        // 3. at the end of the editable content of the document
+        if (offset < renderer->caretMaxOffset() ||
+            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
+            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
+                return DOMPosition(node, offset);
     }
 
     //
@@ -636,7 +726,15 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
     //
     renderer = renderer->nextEditable();
     while (renderer) {
-        if (renderer->isText()) {
+		// Offset is in this node, if:
+		// 1. it is a BR which follows a line break
+		// 2. it is a text element with content
+        // 3. it is a non-text element with content
+		if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+		}
+		else if (renderer->isText()) {
             RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
             if (textRenderer->firstTextBox())
                 return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
@@ -647,8 +745,8 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
         renderer = renderer->nextEditable();
     }
 
-    result = DOMPosition(node, offset);
-    return result;
+    // can't move the position
+    return DOMPosition(endNode(), endOffset());
 }
 
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
@@ -855,4 +953,125 @@ static bool startAndEndLineNodesIncludingNode(DOM::NodeImpl *node, int offset, K
     return false;
 }
 
+void KHTMLSelection::debugRenderer(RenderObject *r, bool selected) const
+{
+    if (r->node()->isElementNode()) {
+        ElementImpl *element = static_cast<ElementImpl *>(r->node());
+        fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->tagName().string().latin1());
+    }
+    else if (r->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(r);
+        if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
+            fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
+            return;
+        }
+        
+        static const int max = 36;
+        QString text = DOMString(textRenderer->string()).string();
+        int textLength = text.length();
+        if (selected) {
+            int offset = 0;
+            if (r->node() == startNode())
+                offset = startOffset();
+            else if (r->node() == endNode())
+                offset = endOffset();
+                
+            int pos;
+            InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
+            text = text.mid(box->m_start, box->m_len);
+            
+            QString show;
+            int mid = max / 2;
+            int caret = 0;
+            
+            // text is shorter than max
+            if (textLength < max) {
+                show = text;
+                caret = pos;
+            }
+            
+            // too few characters to left
+            else if (pos - mid < 0) {
+                show = text.left(max - 3) + "...";
+                caret = pos;
+            }
+            
+            // enough characters on each side
+            else if (pos - mid >= 0 && pos + mid <= textLength) {
+                show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
+                caret = mid;
+            }
+            
+            // too few characters on right
+            else {
+                show = "..." + text.right(max - 3);
+                caret = pos - (textLength - show.length());
+            }
+            
+            show = show.replace("\n", " ");
+            show = show.replace("\r", " ");
+            fprintf(stderr, "==> #text : %s at %d\n", show.latin1(), pos);
+            fprintf(stderr, "           ");
+            for (int i = 0; i < caret; i++)
+                fprintf(stderr, " ");
+            fprintf(stderr, "^\n");
+        }
+        else {
+            if ((int)text.length() > max)
+                text = text.left(max - 3) + "...";
+            else
+                text = text.left(max);
+            fprintf(stderr, "    #text : %s\n", text.latin1());
+        }
+    }
+}
+
+void KHTMLSelection::debugPosition() const
+{
+    if (!startNode())
+        return;
+
+    static int context = 5;
+    
+    RenderObject *r = 0;
+
+    fprintf(stderr, "KHTMLSelection =================\n");
+    
+    int back = 0;
+    r = startNode()->renderer();
+    for (int i = 0; i < context; i++, back++) {
+        if (r->previousRenderer())
+            r = r->previousRenderer();
+        else
+            break;
+    }
+    for (int i = 0; i < back; i++) {
+        debugRenderer(r, false);
+        r = r->nextRenderer();
+    }
+
+
+    fprintf(stderr, "\n");
+
+    if (startNode() == endNode())
+        debugRenderer(startNode()->renderer(), true);
+    else
+        for (r = startNode()->renderer(); r && r != endNode()->renderer(); r = r->nextRenderer())
+            debugRenderer(r, true);
+    
+    fprintf(stderr, "\n");
+    
+    r = endNode()->renderer();
+    for (int i = 0; i < context; i++) {
+        if (r->nextRenderer()) {
+            r = r->nextRenderer();
+            debugRenderer(r, false);
+        }
+        else
+            break;
+    }
+
+    fprintf(stderr, "================================\n");
+}
+
 #endif
diff --git a/WebCore/khtml/editing/selection.h b/WebCore/khtml/editing/selection.h
index e073149..e888924 100644
--- a/WebCore/khtml/editing/selection.h
+++ b/WebCore/khtml/editing/selection.h
@@ -41,6 +41,10 @@ namespace DOM {
     class Range;
 };
 
+namespace khtml {
+    class RenderObject;
+}
+
 class KHTMLSelection : public QObject
 {
   Q_OBJECT
@@ -81,7 +85,10 @@ public:
 
     void setVisible(bool flag=true);
     bool visible() const { return m_visible; }
-    
+
+    DOM::DOMPosition previousCharacterPosition();
+    DOM::DOMPosition nextCharacterPosition();
+        
     void invalidate();
     
     bool isEmpty() const;
@@ -97,12 +104,9 @@ public:
     
     friend class KHTMLPart;
 
-    void dump() {
-        fprintf(stderr, "selection: %p:%d ; %p:%d (%p:%d ; %p:%d)\n", 
-            m_baseNode, m_baseOffset, m_extentNode, m_extentOffset,
-            startNode(), startOffset(), endNode(), endOffset());
-    }
-    
+    void debugPosition() const;
+    void debugRenderer(khtml::RenderObject *r, bool selected) const;
+
 private:
     void setPart(KHTMLPart *part);
 
@@ -127,8 +131,6 @@ private:
 
     void calculateStartAndEnd(ETextElement select=CHARACTER);
     
-    DOM::DOMPosition nextCharacterPosition();
-    
     KHTMLPart *m_part;            // part for this selection
 
     DOM::NodeImpl *m_baseNode;    // base node for the selection
diff --git a/WebCore/khtml/khtml_selection.cpp b/WebCore/khtml/khtml_selection.cpp
index f54e555..c631e6d 100644
--- a/WebCore/khtml/khtml_selection.cpp
+++ b/WebCore/khtml/khtml_selection.cpp
@@ -38,17 +38,21 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
 
 #if APPLE_CHANGES
 #include <KWQAssertions.h>
 #include <CoreServices/CoreServices.h>
+
+#define EDIT_DEBUG 0
 #endif
 
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
 using DOM::Range;
@@ -132,6 +136,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *node, long offset)
 	setBaseOffset(offset);
 	setExtentOffset(offset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setSelection(const DOM::Range &r)
@@ -152,6 +159,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *baseNode, long baseOffset, DOM:
 	setBaseOffset(baseOffset);
 	setExtentOffset(extentOffset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setBase(DOM::NodeImpl *node, long offset)
@@ -187,6 +197,7 @@ bool KHTMLSelection::alterSelection(EAlter alter, EDirection dir, ETextElement e
         case BACKWARD:
             switch (elem) {
                 case CHARACTER:
+                    pos = previousCharacterPosition();
                     break;
                 case WORD:
                     break;
@@ -329,7 +340,7 @@ void KHTMLSelection::update()
     m_caretX = newX;
     m_caretY = newY;
     m_caretSize = newSize;
-
+    
     // paint the caret if it is visible
     if (m_visible && m_caretSize != 0) {
         m_caretPaint = true;
@@ -584,51 +595,130 @@ void KHTMLSelection::calculateStartAndEnd(ETextElement select)
     m_startEndValid = true;
 }
 
+DOMPosition KHTMLSelection::previousCharacterPosition()
+{
+    if (!startNode())
+        return DOMPosition();
+
+	NodeImpl *node = startNode();
+	long offset = startOffset() - 1;
+
+    //
+    // Look in this renderer
+    //
+    RenderObject *renderer = node->renderer();
+    if (renderer->isText()) {
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, return the start
+                    return DOMPosition(node, offset);
+                }
+            }
+        }
+    }
+    else {
+        // Offset is in this node, if:
+        // 1. greater than the min offset and less than or equal to the max caret offset
+        // 2. at the start of the editable content of the document
+        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
+            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
+            return DOMPosition(node, offset);
+    }
+
+    //
+    // Look in previous renderer(s)
+    //
+    renderer = renderer->previousEditable();
+    while (renderer) {
+        // Offset is in this node, if:
+        // 1. it is a BR which follows a line break
+        // 2. it is an element with content
+    	if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+    	}
+    	else {
+    		if (renderer->isText()) {
+    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+    			 if (!textRenderer->lastTextBox()) 
+    			 	continue;
+    		}
+            offset = renderer->caretMaxOffset();
+            if (!renderer->precedesLineBreak())
+                offset--;
+            assert(offset >= 0);
+            return DOMPosition(renderer->element(), offset);
+    	}
+        renderer = renderer->previousEditable();
+    }
+
+    // can't move the position
+    return DOMPosition(startNode(), startOffset());
+}
+
 DOMPosition KHTMLSelection::nextCharacterPosition()
 {
-    DOMPosition result;
+    if (!endNode())
+        return DOMPosition();
+
 	NodeImpl *node = endNode();
-	long offset = endOffset();
-    long desiredOffset = offset + 1;
+	long offset = endOffset() + 1;
 
-    if (!node)
-        return result;
-    
     //
     // Look in this renderer
     //
     RenderObject *renderer = node->renderer();
     if (renderer->isText()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            long start = box->m_start;
-            long end = box->m_start + box->m_len;
-            if (desiredOffset > end) {
-                // Skip this node.
-                // It is too early in the text runs to be involved.
-                continue;
-            }
-            else if (desiredOffset >= start && 
-                (desiredOffset < end || (desiredOffset == end && !box->nextTextBox() && !renderer->nextEditable())) ||
-                (desiredOffset == end && textRenderer->precedesLineBreak() && !textRenderer->followsLineBreak())) {
-                // Desired offset is in this node.
-                // Either it is:
-                // 1. at or after the start and before, but not at the end
-                // 2. at the end of a text run and is immediately followed by a line break
-                //    but does not precede a line break
-                // 3. at the end of the editable content of the document
-                return DOMPosition(renderer->element(), desiredOffset);
-            }
-            else if (desiredOffset <= start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in text that is
-                // not rendered. Just return the start of the node.
-                return DOMPosition(renderer->element(), start);
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, if:
+                    // Either it is:
+                    // 1. at or after the start and before, but not at the end
+                    // 2. at the end of a text run and is immediately followed by a line break
+                    // 3. at the end of the editable content of the document
+                    if (offset < end)
+                        return DOMPosition(node, offset);
+                    else if (offset == end && renderer->precedesLineBreak())
+                        return DOMPosition(node, offset);
+                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
+                        return DOMPosition(node, offset);
+                }
+                else if (offset < start) {
+                    // The offset we're looking for is before this node
+                    // this means the offset must be in content that is
+                    // not rendered. Just return the start of the node.
+                    return DOMPosition(node, start);
+                }
             }
         }
     }
-    else if (desiredOffset < renderer->caretMaxOffset() || (desiredOffset == renderer->caretMaxOffset() && !renderer->nextEditable())) {
-        return DOMPosition(node, desiredOffset);
+    else {
+        // Offset is in this node, if:
+        // 1. before the max caret offset
+        // 2. equal to the max caret offset and is immediately preceded by a line break
+        // 3. at the end of the editable content of the document
+        if (offset < renderer->caretMaxOffset() ||
+            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
+            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
+                return DOMPosition(node, offset);
     }
 
     //
@@ -636,7 +726,15 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
     //
     renderer = renderer->nextEditable();
     while (renderer) {
-        if (renderer->isText()) {
+		// Offset is in this node, if:
+		// 1. it is a BR which follows a line break
+		// 2. it is a text element with content
+        // 3. it is a non-text element with content
+		if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+		}
+		else if (renderer->isText()) {
             RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
             if (textRenderer->firstTextBox())
                 return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
@@ -647,8 +745,8 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
         renderer = renderer->nextEditable();
     }
 
-    result = DOMPosition(node, offset);
-    return result;
+    // can't move the position
+    return DOMPosition(endNode(), endOffset());
 }
 
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
@@ -855,4 +953,125 @@ static bool startAndEndLineNodesIncludingNode(DOM::NodeImpl *node, int offset, K
     return false;
 }
 
+void KHTMLSelection::debugRenderer(RenderObject *r, bool selected) const
+{
+    if (r->node()->isElementNode()) {
+        ElementImpl *element = static_cast<ElementImpl *>(r->node());
+        fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->tagName().string().latin1());
+    }
+    else if (r->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(r);
+        if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
+            fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
+            return;
+        }
+        
+        static const int max = 36;
+        QString text = DOMString(textRenderer->string()).string();
+        int textLength = text.length();
+        if (selected) {
+            int offset = 0;
+            if (r->node() == startNode())
+                offset = startOffset();
+            else if (r->node() == endNode())
+                offset = endOffset();
+                
+            int pos;
+            InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
+            text = text.mid(box->m_start, box->m_len);
+            
+            QString show;
+            int mid = max / 2;
+            int caret = 0;
+            
+            // text is shorter than max
+            if (textLength < max) {
+                show = text;
+                caret = pos;
+            }
+            
+            // too few characters to left
+            else if (pos - mid < 0) {
+                show = text.left(max - 3) + "...";
+                caret = pos;
+            }
+            
+            // enough characters on each side
+            else if (pos - mid >= 0 && pos + mid <= textLength) {
+                show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
+                caret = mid;
+            }
+            
+            // too few characters on right
+            else {
+                show = "..." + text.right(max - 3);
+                caret = pos - (textLength - show.length());
+            }
+            
+            show = show.replace("\n", " ");
+            show = show.replace("\r", " ");
+            fprintf(stderr, "==> #text : %s at %d\n", show.latin1(), pos);
+            fprintf(stderr, "           ");
+            for (int i = 0; i < caret; i++)
+                fprintf(stderr, " ");
+            fprintf(stderr, "^\n");
+        }
+        else {
+            if ((int)text.length() > max)
+                text = text.left(max - 3) + "...";
+            else
+                text = text.left(max);
+            fprintf(stderr, "    #text : %s\n", text.latin1());
+        }
+    }
+}
+
+void KHTMLSelection::debugPosition() const
+{
+    if (!startNode())
+        return;
+
+    static int context = 5;
+    
+    RenderObject *r = 0;
+
+    fprintf(stderr, "KHTMLSelection =================\n");
+    
+    int back = 0;
+    r = startNode()->renderer();
+    for (int i = 0; i < context; i++, back++) {
+        if (r->previousRenderer())
+            r = r->previousRenderer();
+        else
+            break;
+    }
+    for (int i = 0; i < back; i++) {
+        debugRenderer(r, false);
+        r = r->nextRenderer();
+    }
+
+
+    fprintf(stderr, "\n");
+
+    if (startNode() == endNode())
+        debugRenderer(startNode()->renderer(), true);
+    else
+        for (r = startNode()->renderer(); r && r != endNode()->renderer(); r = r->nextRenderer())
+            debugRenderer(r, true);
+    
+    fprintf(stderr, "\n");
+    
+    r = endNode()->renderer();
+    for (int i = 0; i < context; i++) {
+        if (r->nextRenderer()) {
+            r = r->nextRenderer();
+            debugRenderer(r, false);
+        }
+        else
+            break;
+    }
+
+    fprintf(stderr, "================================\n");
+}
+
 #endif
diff --git a/WebCore/khtml/khtml_selection.h b/WebCore/khtml/khtml_selection.h
index e073149..e888924 100644
--- a/WebCore/khtml/khtml_selection.h
+++ b/WebCore/khtml/khtml_selection.h
@@ -41,6 +41,10 @@ namespace DOM {
     class Range;
 };
 
+namespace khtml {
+    class RenderObject;
+}
+
 class KHTMLSelection : public QObject
 {
   Q_OBJECT
@@ -81,7 +85,10 @@ public:
 
     void setVisible(bool flag=true);
     bool visible() const { return m_visible; }
-    
+
+    DOM::DOMPosition previousCharacterPosition();
+    DOM::DOMPosition nextCharacterPosition();
+        
     void invalidate();
     
     bool isEmpty() const;
@@ -97,12 +104,9 @@ public:
     
     friend class KHTMLPart;
 
-    void dump() {
-        fprintf(stderr, "selection: %p:%d ; %p:%d (%p:%d ; %p:%d)\n", 
-            m_baseNode, m_baseOffset, m_extentNode, m_extentOffset,
-            startNode(), startOffset(), endNode(), endOffset());
-    }
-    
+    void debugPosition() const;
+    void debugRenderer(khtml::RenderObject *r, bool selected) const;
+
 private:
     void setPart(KHTMLPart *part);
 
@@ -127,8 +131,6 @@ private:
 
     void calculateStartAndEnd(ETextElement select=CHARACTER);
     
-    DOM::DOMPosition nextCharacterPosition();
-    
     KHTMLPart *m_part;            // part for this selection
 
     DOM::NodeImpl *m_baseNode;    // base node for the selection
diff --git a/WebCore/khtml/rendering/render_br.cpp b/WebCore/khtml/rendering/render_br.cpp
index f7c2e95..74aa850 100644
--- a/WebCore/khtml/rendering/render_br.cpp
+++ b/WebCore/khtml/rendering/render_br.cpp
@@ -25,7 +25,7 @@ using namespace khtml;
 
 
 RenderBR::RenderBR(DOM::NodeImpl* node)
-    : RenderText(node, new DOM::DOMStringImpl(QChar('\n')))
+    : RenderText(node, new DOM::DOMStringImpl(QChar('\n'))), m_x(0), m_y(0), m_height(0)
 {
 }
 
@@ -33,6 +33,20 @@ RenderBR::~RenderBR()
 {
 }
 
+void RenderBR::position(InlineBox* box, int from, int len, bool reverse)
+{
+    InlineTextBox *s = static_cast<InlineTextBox*>(box);
+    
+    // We want the box to be destroyed, but get the position of it first.
+    m_x = s->xPos();
+    m_y = s->yPos();
+    m_height = s->height();
+    
+    s->remove();
+    s->detach(renderArena());
+    m_firstTextBox = m_lastTextBox = 0;
+}
+
 FindSelectionResult RenderBR::checkSelectionPointIgnoringContinuations(int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int &offset)
 {
     FindSelectionResult result = RenderText::checkSelectionPointIgnoringContinuations(_x, _y, _tx, _ty, node, offset);
@@ -55,5 +69,19 @@ long RenderBR::caretMinOffset() const
 
 long RenderBR::caretMaxOffset() const 
 { 
-    return 0; 
+    return 1; 
+}
+
+void RenderBR::caretPos(int offset, bool override, int &_x, int &_y, int &_w, int &_h)
+{
+    // EDIT FIXME: This does not work yet. Some other changes are need before
+    // an accurate position can be determined.
+    _h = height();
+    _x = xPos();
+    _y = yPos();
+
+    int absx, absy;
+    absolutePosition(absx,absy);
+    _x += absx;
+    _y += absy;
 }
diff --git a/WebCore/khtml/rendering/render_br.h b/WebCore/khtml/rendering/render_br.h
index 43ea1c7..a2b3ea5 100644
--- a/WebCore/khtml/rendering/render_br.h
+++ b/WebCore/khtml/rendering/render_br.h
@@ -41,23 +41,32 @@ public:
     virtual void paint( QPainter *, int, int, int, int, int, int, int) {}
     virtual void paintObject( QPainter *, int, int, int, int, int, int, int) {}
 
-    virtual void position(int, int, int, int, int, bool, bool, int) {}
+    virtual void position(InlineBox* box, int from, int len, bool reverse);
     virtual unsigned int width(unsigned int, unsigned int, const Font *) const { return 0; }
     virtual unsigned int width( unsigned int, unsigned int, bool) const { return 0; }
 
-    virtual int height() const { return 0; }
-
     // overrides
     virtual void calcMinMaxWidth() {}
     virtual short minWidth() const { return 0; }
     virtual short maxWidth() const { return 0; }
 
+    virtual int xPos() const { return m_x; }
+    virtual int yPos() const { return m_y; }
+    virtual int height() const { return m_height; }
+
     virtual bool isBR() const { return true; }
 
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
     
+    virtual void caretPos(int offset, bool override, int &_x, int &_y, int &_w, int &_h);
+    
     virtual FindSelectionResult checkSelectionPointIgnoringContinuations(int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int &offset);
+
+private:
+    int m_x;
+    int m_y;
+    int m_height;
 };
 
 }
diff --git a/WebCore/khtml/rendering/render_object.cpp b/WebCore/khtml/rendering/render_object.cpp
index 9e1e446..bed34ce 100644
--- a/WebCore/khtml/rendering/render_object.cpp
+++ b/WebCore/khtml/rendering/render_object.cpp
@@ -222,6 +222,34 @@ void RenderObject::insertChildNode(RenderObject*, RenderObject*)
     KHTMLAssert(0);
 }
 
+bool RenderObject::precedesLineBreak() const
+{
+    RenderObject *r = nextRenderer();
+    while (r) {
+        if (r->isBR() || r->isRenderBlock())
+            return true;
+        if (r->isText() || r->isReplaced())
+            return false;
+        r = r->nextRenderer();
+    }
+    
+    return false;
+}
+
+bool RenderObject::followsLineBreak() const
+{
+    RenderObject *r = previousRenderer();
+    while (r) {
+        if (r->isBR() || r->isRenderBlock())
+            return true;
+        if (r->isText() || r->isReplaced())
+            return false;
+        r = r->previousRenderer();
+    }
+    
+    return false;
+}
+
 RenderObject *RenderObject::nextRenderer() const
 {
     if (firstChild())
@@ -263,7 +291,7 @@ bool RenderObject::isEditable() const
 
     return style()->visibility() == VISIBLE && 
         element() && element()->isContentEditable() &&
-        (isReplaced() || (textRenderer && textRenderer->firstTextBox()));
+        (isReplaced() || isBR() || (textRenderer && textRenderer->firstTextBox()));
 }
 
 RenderObject *RenderObject::nextEditable() const
diff --git a/WebCore/khtml/rendering/render_object.h b/WebCore/khtml/rendering/render_object.h
index ad1962e..99d7dd1 100644
--- a/WebCore/khtml/rendering/render_object.h
+++ b/WebCore/khtml/rendering/render_object.h
@@ -702,6 +702,9 @@ public:
     virtual long caretMinOffset() const { return 0; }
     virtual long caretMaxOffset() const { return 0; }
 
+    bool precedesLineBreak() const;
+    bool followsLineBreak() const;
+            
     virtual void setPixmap(const QPixmap&, const QRect&, CachedImage *);
 
 protected:
diff --git a/WebCore/khtml/rendering/render_text.cpp b/WebCore/khtml/rendering/render_text.cpp
index 62b84cf..59d8926 100644
--- a/WebCore/khtml/rendering/render_text.cpp
+++ b/WebCore/khtml/rendering/render_text.cpp
@@ -440,7 +440,7 @@ FindSelectionResult RenderText::checkSelectionPointIgnoringContinuations(int _x,
 
 void RenderText::caretPos(int offset, bool override, int &_x, int &_y, int &width, int &height)
 {
-    if (!firstTextBox()) {
+    if (!firstTextBox() || stringLength() == 0) {
         _x = _y = height = -1;
         return;
     }
@@ -1211,9 +1211,8 @@ void RenderText::position(InlineBox* box, int from, int len, bool reverse)
     InlineTextBox *s = static_cast<InlineTextBox*>(box);
     
     // ### should not be needed!!!
-    if (len == 0 || isBR()) {
-        // We want the box to be destroyed.  This is a <br>, and we don't
-        // need <br>s to be included.
+    if (len == 0) {
+        // We want the box to be destroyed.
         s->remove();
         s->detach(renderArena());
         m_firstTextBox = m_lastTextBox = 0;
@@ -1327,34 +1326,6 @@ long RenderText::caretMaxOffset() const
     return lastTextBox()->m_start + lastTextBox()->m_len;
 }
 
-bool RenderText::precedesLineBreak() const
-{
-    RenderObject *r = nextRenderer();
-    while (r) {
-        if (r->isBR() || r->isRenderBlock())
-            return true;
-        if (r->isText() || r->isReplaced())
-            return false;
-        r = r->nextRenderer();
-    }
-    
-    return false;
-}
-
-bool RenderText::followsLineBreak() const
-{
-    RenderObject *r = previousRenderer();
-    while (r) {
-        if (r->isBR() || r->isRenderBlock())
-            return true;
-        if (r->isText() || r->isReplaced())
-            return false;
-        r = r->previousRenderer();
-    }
-    
-    return false;
-}
-
 RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str,
                                        int startOffset, int endOffset)
 :RenderText(_node, _str->substring(startOffset, endOffset)), 
diff --git a/WebCore/khtml/rendering/render_text.h b/WebCore/khtml/rendering/render_text.h
index 1c11ce0..bda68ad 100644
--- a/WebCore/khtml/rendering/render_text.h
+++ b/WebCore/khtml/rendering/render_text.h
@@ -211,9 +211,6 @@ public:
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
     
-    bool precedesLineBreak() const;
-    bool followsLineBreak() const;
-            
 #if APPLE_CHANGES
 public:
 #endif
diff --git a/WebCore/khtml/xml/dom_elementimpl.cpp b/WebCore/khtml/xml/dom_elementimpl.cpp
index 298e175..eb79c22 100644
--- a/WebCore/khtml/xml/dom_elementimpl.cpp
+++ b/WebCore/khtml/xml/dom_elementimpl.cpp
@@ -382,7 +382,11 @@ void ElementImpl::defaultEventHandler(EventImpl *evt)
             }
         }
         else if (k->keyIdentifier() == "Left") {
-            // EDIT FIXME: unimplemented
+            KHTMLPart *part = getDocument()->part();
+            if (part) {
+                part->getKHTMLSelection().alterSelection(KHTMLSelection::MOVE, KHTMLSelection::BACKWARD, KHTMLSelection::CHARACTER);
+                evt->setDefaultHandled();
+            }
         }
         else if (k->keyIdentifier() == "Up") {
             // EDIT FIXME: unimplemented
diff --git a/WebCore/khtml/xml/dom_selection.cpp b/WebCore/khtml/xml/dom_selection.cpp
index f54e555..c631e6d 100644
--- a/WebCore/khtml/xml/dom_selection.cpp
+++ b/WebCore/khtml/xml/dom_selection.cpp
@@ -38,17 +38,21 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
 
 #if APPLE_CHANGES
 #include <KWQAssertions.h>
 #include <CoreServices/CoreServices.h>
+
+#define EDIT_DEBUG 0
 #endif
 
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
 using DOM::Range;
@@ -132,6 +136,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *node, long offset)
 	setBaseOffset(offset);
 	setExtentOffset(offset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setSelection(const DOM::Range &r)
@@ -152,6 +159,9 @@ void KHTMLSelection::setSelection(DOM::NodeImpl *baseNode, long baseOffset, DOM:
 	setBaseOffset(baseOffset);
 	setExtentOffset(extentOffset);
 	update();
+#if EDIT_DEBUG
+    debugPosition();
+#endif
 }
 
 void KHTMLSelection::setBase(DOM::NodeImpl *node, long offset)
@@ -187,6 +197,7 @@ bool KHTMLSelection::alterSelection(EAlter alter, EDirection dir, ETextElement e
         case BACKWARD:
             switch (elem) {
                 case CHARACTER:
+                    pos = previousCharacterPosition();
                     break;
                 case WORD:
                     break;
@@ -329,7 +340,7 @@ void KHTMLSelection::update()
     m_caretX = newX;
     m_caretY = newY;
     m_caretSize = newSize;
-
+    
     // paint the caret if it is visible
     if (m_visible && m_caretSize != 0) {
         m_caretPaint = true;
@@ -584,51 +595,130 @@ void KHTMLSelection::calculateStartAndEnd(ETextElement select)
     m_startEndValid = true;
 }
 
+DOMPosition KHTMLSelection::previousCharacterPosition()
+{
+    if (!startNode())
+        return DOMPosition();
+
+	NodeImpl *node = startNode();
+	long offset = startOffset() - 1;
+
+    //
+    // Look in this renderer
+    //
+    RenderObject *renderer = node->renderer();
+    if (renderer->isText()) {
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, return the start
+                    return DOMPosition(node, offset);
+                }
+            }
+        }
+    }
+    else {
+        // Offset is in this node, if:
+        // 1. greater than the min offset and less than or equal to the max caret offset
+        // 2. at the start of the editable content of the document
+        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
+            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
+            return DOMPosition(node, offset);
+    }
+
+    //
+    // Look in previous renderer(s)
+    //
+    renderer = renderer->previousEditable();
+    while (renderer) {
+        // Offset is in this node, if:
+        // 1. it is a BR which follows a line break
+        // 2. it is an element with content
+    	if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+    	}
+    	else {
+    		if (renderer->isText()) {
+    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+    			 if (!textRenderer->lastTextBox()) 
+    			 	continue;
+    		}
+            offset = renderer->caretMaxOffset();
+            if (!renderer->precedesLineBreak())
+                offset--;
+            assert(offset >= 0);
+            return DOMPosition(renderer->element(), offset);
+    	}
+        renderer = renderer->previousEditable();
+    }
+
+    // can't move the position
+    return DOMPosition(startNode(), startOffset());
+}
+
 DOMPosition KHTMLSelection::nextCharacterPosition()
 {
-    DOMPosition result;
+    if (!endNode())
+        return DOMPosition();
+
 	NodeImpl *node = endNode();
-	long offset = endOffset();
-    long desiredOffset = offset + 1;
+	long offset = endOffset() + 1;
 
-    if (!node)
-        return result;
-    
     //
     // Look in this renderer
     //
     RenderObject *renderer = node->renderer();
     if (renderer->isText()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            long start = box->m_start;
-            long end = box->m_start + box->m_len;
-            if (desiredOffset > end) {
-                // Skip this node.
-                // It is too early in the text runs to be involved.
-                continue;
-            }
-            else if (desiredOffset >= start && 
-                (desiredOffset < end || (desiredOffset == end && !box->nextTextBox() && !renderer->nextEditable())) ||
-                (desiredOffset == end && textRenderer->precedesLineBreak() && !textRenderer->followsLineBreak())) {
-                // Desired offset is in this node.
-                // Either it is:
-                // 1. at or after the start and before, but not at the end
-                // 2. at the end of a text run and is immediately followed by a line break
-                //    but does not precede a line break
-                // 3. at the end of the editable content of the document
-                return DOMPosition(renderer->element(), desiredOffset);
-            }
-            else if (desiredOffset <= start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in text that is
-                // not rendered. Just return the start of the node.
-                return DOMPosition(renderer->element(), start);
+        if (!renderer->isBR()) {
+            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+                long start = box->m_start;
+                long end = box->m_start + box->m_len;
+                if (offset > end) {
+                    // Skip this node.
+                    // It is too early in the text runs to be involved.
+                    continue;
+                }
+                else if (offset >= start) {
+                    // Offset is in this node, if:
+                    // Either it is:
+                    // 1. at or after the start and before, but not at the end
+                    // 2. at the end of a text run and is immediately followed by a line break
+                    // 3. at the end of the editable content of the document
+                    if (offset < end)
+                        return DOMPosition(node, offset);
+                    else if (offset == end && renderer->precedesLineBreak())
+                        return DOMPosition(node, offset);
+                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
+                        return DOMPosition(node, offset);
+                }
+                else if (offset < start) {
+                    // The offset we're looking for is before this node
+                    // this means the offset must be in content that is
+                    // not rendered. Just return the start of the node.
+                    return DOMPosition(node, start);
+                }
             }
         }
     }
-    else if (desiredOffset < renderer->caretMaxOffset() || (desiredOffset == renderer->caretMaxOffset() && !renderer->nextEditable())) {
-        return DOMPosition(node, desiredOffset);
+    else {
+        // Offset is in this node, if:
+        // 1. before the max caret offset
+        // 2. equal to the max caret offset and is immediately preceded by a line break
+        // 3. at the end of the editable content of the document
+        if (offset < renderer->caretMaxOffset() ||
+            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
+            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
+                return DOMPosition(node, offset);
     }
 
     //
@@ -636,7 +726,15 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
     //
     renderer = renderer->nextEditable();
     while (renderer) {
-        if (renderer->isText()) {
+		// Offset is in this node, if:
+		// 1. it is a BR which follows a line break
+		// 2. it is a text element with content
+        // 3. it is a non-text element with content
+		if (renderer->isBR()) {
+			if (renderer->followsLineBreak())
+				return DOMPosition(renderer->element(), renderer->caretMinOffset());
+		}
+		else if (renderer->isText()) {
             RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
             if (textRenderer->firstTextBox())
                 return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
@@ -647,8 +745,8 @@ DOMPosition KHTMLSelection::nextCharacterPosition()
         renderer = renderer->nextEditable();
     }
 
-    result = DOMPosition(node, offset);
-    return result;
+    // can't move the position
+    return DOMPosition(endNode(), endOffset());
 }
 
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
@@ -855,4 +953,125 @@ static bool startAndEndLineNodesIncludingNode(DOM::NodeImpl *node, int offset, K
     return false;
 }
 
+void KHTMLSelection::debugRenderer(RenderObject *r, bool selected) const
+{
+    if (r->node()->isElementNode()) {
+        ElementImpl *element = static_cast<ElementImpl *>(r->node());
+        fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->tagName().string().latin1());
+    }
+    else if (r->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(r);
+        if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
+            fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
+            return;
+        }
+        
+        static const int max = 36;
+        QString text = DOMString(textRenderer->string()).string();
+        int textLength = text.length();
+        if (selected) {
+            int offset = 0;
+            if (r->node() == startNode())
+                offset = startOffset();
+            else if (r->node() == endNode())
+                offset = endOffset();
+                
+            int pos;
+            InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
+            text = text.mid(box->m_start, box->m_len);
+            
+            QString show;
+            int mid = max / 2;
+            int caret = 0;
+            
+            // text is shorter than max
+            if (textLength < max) {
+                show = text;
+                caret = pos;
+            }
+            
+            // too few characters to left
+            else if (pos - mid < 0) {
+                show = text.left(max - 3) + "...";
+                caret = pos;
+            }
+            
+            // enough characters on each side
+            else if (pos - mid >= 0 && pos + mid <= textLength) {
+                show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
+                caret = mid;
+            }
+            
+            // too few characters on right
+            else {
+                show = "..." + text.right(max - 3);
+                caret = pos - (textLength - show.length());
+            }
+            
+            show = show.replace("\n", " ");
+            show = show.replace("\r", " ");
+            fprintf(stderr, "==> #text : %s at %d\n", show.latin1(), pos);
+            fprintf(stderr, "           ");
+            for (int i = 0; i < caret; i++)
+                fprintf(stderr, " ");
+            fprintf(stderr, "^\n");
+        }
+        else {
+            if ((int)text.length() > max)
+                text = text.left(max - 3) + "...";
+            else
+                text = text.left(max);
+            fprintf(stderr, "    #text : %s\n", text.latin1());
+        }
+    }
+}
+
+void KHTMLSelection::debugPosition() const
+{
+    if (!startNode())
+        return;
+
+    static int context = 5;
+    
+    RenderObject *r = 0;
+
+    fprintf(stderr, "KHTMLSelection =================\n");
+    
+    int back = 0;
+    r = startNode()->renderer();
+    for (int i = 0; i < context; i++, back++) {
+        if (r->previousRenderer())
+            r = r->previousRenderer();
+        else
+            break;
+    }
+    for (int i = 0; i < back; i++) {
+        debugRenderer(r, false);
+        r = r->nextRenderer();
+    }
+
+
+    fprintf(stderr, "\n");
+
+    if (startNode() == endNode())
+        debugRenderer(startNode()->renderer(), true);
+    else
+        for (r = startNode()->renderer(); r && r != endNode()->renderer(); r = r->nextRenderer())
+            debugRenderer(r, true);
+    
+    fprintf(stderr, "\n");
+    
+    r = endNode()->renderer();
+    for (int i = 0; i < context; i++) {
+        if (r->nextRenderer()) {
+            r = r->nextRenderer();
+            debugRenderer(r, false);
+        }
+        else
+            break;
+    }
+
+    fprintf(stderr, "================================\n");
+}
+
 #endif
diff --git a/WebCore/khtml/xml/dom_selection.h b/WebCore/khtml/xml/dom_selection.h
index e073149..e888924 100644
--- a/WebCore/khtml/xml/dom_selection.h
+++ b/WebCore/khtml/xml/dom_selection.h
@@ -41,6 +41,10 @@ namespace DOM {
     class Range;
 };
 
+namespace khtml {
+    class RenderObject;
+}
+
 class KHTMLSelection : public QObject
 {
   Q_OBJECT
@@ -81,7 +85,10 @@ public:
 
     void setVisible(bool flag=true);
     bool visible() const { return m_visible; }
-    
+
+    DOM::DOMPosition previousCharacterPosition();
+    DOM::DOMPosition nextCharacterPosition();
+        
     void invalidate();
     
     bool isEmpty() const;
@@ -97,12 +104,9 @@ public:
     
     friend class KHTMLPart;
 
-    void dump() {
-        fprintf(stderr, "selection: %p:%d ; %p:%d (%p:%d ; %p:%d)\n", 
-            m_baseNode, m_baseOffset, m_extentNode, m_extentOffset,
-            startNode(), startOffset(), endNode(), endOffset());
-    }
-    
+    void debugPosition() const;
+    void debugRenderer(khtml::RenderObject *r, bool selected) const;
+
 private:
     void setPart(KHTMLPart *part);
 
@@ -127,8 +131,6 @@ private:
 
     void calculateStartAndEnd(ETextElement select=CHARACTER);
     
-    DOM::DOMPosition nextCharacterPosition();
-    
     KHTMLPart *m_part;            // part for this selection
 
     DOM::NodeImpl *m_baseNode;    // base node for the selection

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list