Reviewed by Hyatt
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Aug 2004 21:53:45 +0000 (21:53 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Aug 2004 21:53:45 +0000 (21:53 +0000)
        Rewrite of the command that deletes a selection. I deleted great
        big swaths of bug-ridden code to accomplish this and replaced it
        with code that is much cleaner and smarter.

        Also, renamed equivalentUpstreamPosition and equivalentDownstreamPosition to
        upstream to downstream, respectively.

        Added a couple of new helper methods.

        * khtml/editing/htmlediting.cpp: DeleteCollapsibleWhitespaceCommand and
        RemoveNodeAndPruneCommand now obsolete. A huge win.
        * khtml/editing/htmlediting.h: Ditto.
        * khtml/editing/htmlediting_impl.cpp:
        (khtml::debugPosition): Fix printf which had a placeholder, but no argument passed in the varargs.
        (khtml::CompositeEditCommandImpl::deleteUnrenderedText): New helper. Much simplified and cleaner
        version of
        (khtml::ApplyStyleCommandImpl::doApply): upstream/downstream name change
        (khtml::ApplyStyleCommandImpl::nodeFullySelected):  upstream/downstream name change
        (khtml::DeleteSelectionCommandImpl::doApply):  upstream/downstream name change
        (khtml::DeleteTextCommandImpl::DeleteTextCommandImpl): Add an assert to check that the
        passed offset is less than the length of the text node.
        (khtml::InputNewlineCommandImpl::insertNodeAfterPosition): upstream/downstream name change
        (khtml::InputNewlineCommandImpl::insertNodeBeforePosition): upstream/downstream name change
        (khtml::InputNewlineCommandImpl::doApply): upstream/downstream name change
        (khtml::InputTextCommandImpl::prepareForTextInsertion): upstream/downstream name change
        (khtml::InputTextCommandImpl::execute): upstream/downstream name change
        (khtml::InputTextCommandImpl::insertSpace): upstream/downstream name change
        (khtml::ReplaceSelectionCommandImpl::doApply): upstream/downstream name change
        (khtml::TypingCommandImpl::issueCommandForDeleteKey): upstream/downstream name change
        (khtml::TypingCommandImpl::deleteKeyPressed):
        * khtml/editing/htmlediting_impl.h:
        * khtml/xml/dom_position.cpp:
        (DOM::Position::previousWordBoundary):
        (DOM::Position::nextWordBoundary):
        (DOM::Position::upstream):
        (DOM::Position::downstream):
        (DOM::Position::inRenderedText): Add null check.
        (DOM::Position::isRenderedCharacter): New helper.
        (DOM::isWS): New helper in this file.
        (DOM::Position::leadingWhitespacePosition): New helper. Factored out from htmlediting_impl.cpp.
        (DOM::Position::trailingWhitespacePosition): Ditto.
        (DOM::Position::debugPosition): Add null check.
        * khtml/xml/dom_position.h:
        * khtml/xml/dom_selection.cpp:
        (DOM::Selection::toRange): upstream/downstream name change
        (DOM::Selection::validate): upstream/downstream name change
        (DOM::Selection::debugPosition): upstream/downstream name change
        * layout-tests/editing/deleting/delete-block-contents-003-expected.txt: Updated tests with new expected results.
        * layout-tests/editing/deleting/delete-contiguous-ws-001-expected.txt: Ditto.
        * layout-tests/editing/deleting/delete-selection-001-expected.txt: Ditto.
        * layout-tests/editing/deleting/delete-trailing-ws-001-expected.txt: Ditto.
        * layout-tests/editing/inserting/insert-br-case1-expected.txt: Ditto.
        * layout-tests/editing/inserting/insert-br-case2-expected.txt: Ditto.
        * layout-tests/editing/style/style-3681552-fix-002-expected.txt: Ditto.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@7277 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/editing/deleting/delete-block-contents-003-expected.txt
LayoutTests/editing/deleting/delete-contiguous-ws-001-expected.txt
LayoutTests/editing/deleting/delete-selection-001-expected.txt
LayoutTests/editing/deleting/delete-trailing-ws-001-expected.txt
LayoutTests/editing/inserting/insert-br-case1-expected.txt
LayoutTests/editing/inserting/insert-br-case2-expected.txt
LayoutTests/editing/style/style-3681552-fix-002-expected.txt
WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/SelectionController.cpp
WebCore/khtml/editing/htmlediting.cpp
WebCore/khtml/editing/htmlediting.h
WebCore/khtml/editing/htmlediting_impl.cpp
WebCore/khtml/editing/htmlediting_impl.h
WebCore/khtml/editing/selection.cpp
WebCore/khtml/xml/dom_position.cpp
WebCore/khtml/xml/dom_position.h
WebCore/khtml/xml/dom_selection.cpp
WebCore/kwq/KWQRenderTreeDebug.cpp

index 0dff54253db87912c02210de620d832d2163bea3..73be1f5aa54b8e47e5ddeadb79c40286b87c912d 100644 (file)
@@ -11,7 +11,8 @@ layer at (0,0) size 800x600
             text run at (89,14) width 34: "baz"
         RenderText {TEXT} at (0,0) size 0x0
       RenderBlock {DIV} at (0,56) size 784x56 [border: (2px solid #FF0000)]
+        RenderInline {SPAN} at (0,0) size 0x0
 selection is CARET:
-start:      position 0 of child 3 {DIV} of child 2 {BODY} of child 1 {HTML} of root {}
-upstream:   position 0 of child 3 {DIV} of child 2 {BODY} of child 1 {HTML} of root {}
-downstream: position 0 of child 3 {DIV} of child 2 {BODY} of child 1 {HTML} of root {}
+start:      position 0 of child 1 {SPAN} of child 3 {DIV} of child 2 {BODY} of child 1 {HTML} of root {}
+upstream:   position 0 of child 1 {SPAN} of child 3 {DIV} of child 2 {BODY} of child 1 {HTML} of root {}
+downstream: position 1 of child 1 {SPAN} of child 3 {DIV} of child 2 {BODY} of child 1 {HTML} of root {}
index 8490f1513aa32bd443dfe89825d337a264dafaba..92d643ad929c44a11341e831beb53a824f5eaa6d 100644 (file)
@@ -6,10 +6,10 @@ layer at (0,0) size 800x600
       RenderBlock {DIV} at (0,0) size 784x56 [border: (2px solid #FF0000)]
         RenderInline {SPAN} at (0,0) size 78x28
           RenderText {TEXT} at (14,14) size 78x28
-            text run at (14,14) width 44: "foo  "
-            text run at (58,14) width 34: "baz"
+            text run at (14,14) width 38: "foo "
+            text run at (52,14) width 40: " baz"
         RenderText {TEXT} at (0,0) size 0x0
 selection is CARET:
-start:      position 4 of child 1 {TEXT} of child 2 {SPAN} of root {DIV}
+start:      position 8 of child 1 {TEXT} of child 2 {SPAN} of root {DIV}
 upstream:   position 4 of child 1 {TEXT} of child 2 {SPAN} of root {DIV}
-downstream: position 4 of child 1 {TEXT} of child 2 {SPAN} of root {DIV}
+downstream: position 8 of child 1 {TEXT} of child 2 {SPAN} of root {DIV}
index 6ab4b3d4971201402ce1980619c298a2e49ffedd..4e9addea2d8b93000920041771cd8219d97a5518 100644 (file)
@@ -10,6 +10,6 @@ layer at (0,0) size 800x600
             text run at (57,14) width 34: "baz"
         RenderText {TEXT} at (0,0) size 0x0
 selection is CARET:
-start:      position 0 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
+start:      position 2 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
 upstream:   position 0 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
-downstream: position 0 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
+downstream: position 2 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
index 6ab4b3d4971201402ce1980619c298a2e49ffedd..4e9addea2d8b93000920041771cd8219d97a5518 100644 (file)
@@ -10,6 +10,6 @@ layer at (0,0) size 800x600
             text run at (57,14) width 34: "baz"
         RenderText {TEXT} at (0,0) size 0x0
 selection is CARET:
-start:      position 0 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
+start:      position 2 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
 upstream:   position 0 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
-downstream: position 0 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
+downstream: position 2 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
index 9e625d6ba95d841414461c439e747b9698a5ed62..495a8bb1008324fba1836965619ebf5499575381 100644 (file)
@@ -4,16 +4,14 @@ layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
       RenderBlock {DIV} at (0,0) size 784x84 [border: (2px solid #FF0000)]
-        RenderInline {SPAN} at (0,0) size 34x28
+        RenderInline {SPAN} at (0,0) size 34x56
           RenderText {TEXT} at (14,14) size 34x28
             text run at (14,14) width 34: "test"
-        RenderText {TEXT} at (48,14) size 6x28
-          text run at (48,14) width 6: " "
-        RenderBR {BR} at (0,0) size 0x0
-        RenderText {TEXT} at (14,42) size 12x28
-          text run at (14,42) width 12: "x"
-        RenderBR {BR} at (14,42) size 0x28
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (14,42) size 12x28
+            text run at (14,42) width 12: "x"
+          RenderBR {BR} at (14,42) size 0x28
 selection is CARET:
-start:      position 1 of child 5 {TEXT} of root {DIV}
-upstream:   position 1 of child 5 {TEXT} of root {DIV}
-downstream: position 0 of child 6 {BR} of root {DIV}
+start:      position 1 of child 3 {TEXT} of child 2 {SPAN} of root {DIV}
+upstream:   position 1 of child 3 {TEXT} of child 2 {SPAN} of root {DIV}
+downstream: position 0 of child 4 {BR} of child 2 {SPAN} of root {DIV}
index 5bcbae403950dc42d54a0553f59fe17080062fca..0382d5e0ac2e3f793f06d913a28d350fb5fb2ada 100644 (file)
@@ -4,17 +4,16 @@ layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
       RenderBlock {DIV} at (0,0) size 784x112 [border: (2px solid #FF0000)]
-        RenderInline {SPAN} at (0,0) size 34x84
+        RenderInline {SPAN} at (0,0) size 46x84
           RenderText {TEXT} at (14,14) size 34x28
             text run at (14,14) width 34: "test"
           RenderBR {BR} at (0,0) size 0x0
-          RenderText {TEXT} at (14,42) size 12x28
-            text run at (14,42) width 12: "x"
-          RenderBR {BR} at (14,42) size 0x28
-          RenderText {TEXT} at (14,70) size 34x28
-            text run at (14,70) width 34: "test"
+          RenderText {TEXT} at (0,0) size 0x0
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (14,70) size 46x28
+            text run at (14,70) width 46: "xtest"
         RenderText {TEXT} at (0,0) size 0x0
 selection is CARET:
-start:      position 1 of child 3 {TEXT} of child 2 {SPAN} of root {DIV}
-upstream:   position 1 of child 3 {TEXT} of child 2 {SPAN} of root {DIV}
-downstream: position 0 of child 4 {BR} of child 2 {SPAN} of root {DIV}
+start:      position 1 of child 5 {TEXT} of child 2 {SPAN} of root {DIV}
+upstream:   position 1 of child 5 {TEXT} of child 2 {SPAN} of root {DIV}
+downstream: position 1 of child 5 {TEXT} of child 2 {SPAN} of root {DIV}
index bd693cdea3db91a82c4a07d4586539cb09f78a8f..94702e6137f0d646b08e6cbf94877b3d80a01d23 100644 (file)
@@ -4,13 +4,16 @@ layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
       RenderBlock {DIV} at (0,0) size 784x56 [border: (2px solid #FF0000)]
-        RenderInline {SPAN} at (0,0) size 161x28
+        RenderInline {SPAN} at (0,0) size 167x28
           RenderText {TEXT} at (14,14) size 118x28
             text run at (14,14) width 118: "here is xxxx"
-          RenderText {TEXT} at (132,14) size 43x28
-            text run at (132,14) width 43: " text"
+          RenderInline {I} at (0,0) size 6x28
+            RenderText {TEXT} at (132,14) size 6x28
+              text run at (132,14) width 6: " "
+          RenderText {TEXT} at (138,14) size 43x28
+            text run at (138,14) width 43: " text"
         RenderText {TEXT} at (0,0) size 0x0
 selection is CARET:
 start:      position 12 of child 1 {TEXT} of child 2 {SPAN} of root {DIV}
 upstream:   position 12 of child 1 {TEXT} of child 2 {SPAN} of root {DIV}
-downstream: position 0 of child 2 {TEXT} of child 2 {SPAN} of root {DIV}
+downstream: position 0 of child 1 {TEXT} of child 2 {I} of child 2 {SPAN} of root {DIV}
index a1ec18535991ffcd11cd0840644c5e9235ba5288..e53014ad9c6e64d32aa638f7dffec4a64f6fd5b3 100644 (file)
@@ -1,3 +1,62 @@
+2004-08-17  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by Hyatt
+
+        Rewrite of the command that deletes a selection. I deleted great 
+        big swaths of bug-ridden code to accomplish this and replaced it
+        with code that is much cleaner and smarter.
+        
+        Also, renamed equivalentUpstreamPosition and equivalentDownstreamPosition to
+        upstream to downstream, respectively.
+        
+        Added a couple of new helper methods.
+        
+        * khtml/editing/htmlediting.cpp: DeleteCollapsibleWhitespaceCommand and
+        RemoveNodeAndPruneCommand now obsolete. A huge win.
+        * khtml/editing/htmlediting.h: Ditto.
+        * khtml/editing/htmlediting_impl.cpp:
+        (khtml::debugPosition): Fix printf which had a placeholder, but no argument passed in the varargs.
+        (khtml::CompositeEditCommandImpl::deleteUnrenderedText): New helper. Much simplified and cleaner
+        version of 
+        (khtml::ApplyStyleCommandImpl::doApply): upstream/downstream name change
+        (khtml::ApplyStyleCommandImpl::nodeFullySelected):  upstream/downstream name change
+        (khtml::DeleteSelectionCommandImpl::doApply):  upstream/downstream name change
+        (khtml::DeleteTextCommandImpl::DeleteTextCommandImpl): Add an assert to check that the
+        passed offset is less than the length of the text node.
+        (khtml::InputNewlineCommandImpl::insertNodeAfterPosition): upstream/downstream name change
+        (khtml::InputNewlineCommandImpl::insertNodeBeforePosition): upstream/downstream name change
+        (khtml::InputNewlineCommandImpl::doApply): upstream/downstream name change
+        (khtml::InputTextCommandImpl::prepareForTextInsertion): upstream/downstream name change
+        (khtml::InputTextCommandImpl::execute): upstream/downstream name change
+        (khtml::InputTextCommandImpl::insertSpace): upstream/downstream name change
+        (khtml::ReplaceSelectionCommandImpl::doApply): upstream/downstream name change
+        (khtml::TypingCommandImpl::issueCommandForDeleteKey): upstream/downstream name change
+        (khtml::TypingCommandImpl::deleteKeyPressed):
+        * khtml/editing/htmlediting_impl.h:
+        * khtml/xml/dom_position.cpp:
+        (DOM::Position::previousWordBoundary):
+        (DOM::Position::nextWordBoundary):
+        (DOM::Position::upstream):
+        (DOM::Position::downstream):
+        (DOM::Position::inRenderedText): Add null check.
+        (DOM::Position::isRenderedCharacter): New helper.
+        (DOM::isWS): New helper in this file.
+        (DOM::Position::leadingWhitespacePosition): New helper. Factored out from htmlediting_impl.cpp.
+        (DOM::Position::trailingWhitespacePosition): Ditto.
+        (DOM::Position::debugPosition): Add null check.
+        * khtml/xml/dom_position.h:
+        * khtml/xml/dom_selection.cpp:
+        (DOM::Selection::toRange): upstream/downstream name change
+        (DOM::Selection::validate): upstream/downstream name change
+        (DOM::Selection::debugPosition): upstream/downstream name change
+        * layout-tests/editing/deleting/delete-block-contents-003-expected.txt: Updated tests with new expected results.
+        * layout-tests/editing/deleting/delete-contiguous-ws-001-expected.txt: Ditto.
+        * layout-tests/editing/deleting/delete-selection-001-expected.txt: Ditto.
+        * layout-tests/editing/deleting/delete-trailing-ws-001-expected.txt: Ditto.
+        * layout-tests/editing/inserting/insert-br-case1-expected.txt: Ditto.
+        * layout-tests/editing/inserting/insert-br-case2-expected.txt: Ditto.
+        * layout-tests/editing/style/style-3681552-fix-002-expected.txt: Ditto.
+
 2004-08-17  Trey Matteson  <trey@apple.com>
 
        Various spelling fixes.
index 47d135a495f498c6c2cc5c3032295eb2a3d1a6f3..c2f144f7ff55408256c2e262175da30aaf4cf83b 100644 (file)
@@ -474,7 +474,7 @@ Range Selection::toRange() const
         // If the selection is a caret, move the range start upstream. This helps us match
         // the conventions of text editors tested, which make style determinations based
         // on the character before the caret, if any. 
-        s = start().equivalentUpstreamPosition().equivalentRangeCompliantPosition();
+        s = start().upstream().equivalentRangeCompliantPosition();
         e = s;
     }
     else {
@@ -490,8 +490,8 @@ Range Selection::toRange() const
         //                       ^ selected
         //
         ASSERT(state() == RANGE);
-        s = start().equivalentDownstreamPosition();
-        e = end().equivalentUpstreamPosition();
+        s = start().downstream();
+        e = end().upstream();
         if ((s.node() == e.node() && s.offset() > e.offset()) || !nodeIsBeforeNode(s.node(), e.node())) {
             // Make sure the start is before the end.
             // The end can wind up before the start if collapsed whitespace is the only thing selected.
@@ -719,7 +719,7 @@ void Selection::validate(ETextGranularity granularity)
     if (start().isEmpty() && end().isEmpty()) {
         m_state = NONE;
     }
-    else if (start() == end() || start().equivalentUpstreamPosition() == end().equivalentUpstreamPosition()) {
+    else if (start() == end() || start().upstream() == end().upstream()) {
         m_state = CARET;
     }
     else {
@@ -730,8 +730,8 @@ void Selection::validate(ETextGranularity granularity)
         // purposes of comparing selections). This is an ideal point of the code
         // to do this operation, since all selection changes that result in a RANGE 
         // come through here before anyone uses it.
-        assignStart(start().equivalentDownstreamPosition());
-        assignEnd(end().equivalentUpstreamPosition());
+        assignStart(start().downstream());
+        assignEnd(end().upstream());
     }
 
     m_needsCaretLayout = true;
@@ -1009,23 +1009,23 @@ void Selection::debugPosition() const
 
     if (start() == end()) {
         Position pos = start();
-        Position upstream = pos.equivalentUpstreamPosition();
-        Position downstream = pos.equivalentDownstreamPosition();
+        Position upstream = pos.upstream();
+        Position downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
     }
     else {
         Position pos = start();
-        Position upstream = pos.equivalentUpstreamPosition();
-        Position downstream = pos.equivalentDownstreamPosition();
+        Position upstream = pos.upstream();
+        Position downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
         fprintf(stderr, "-----------------------------------\n");
         pos = end();
-        upstream = pos.equivalentUpstreamPosition();
-        downstream = pos.equivalentDownstreamPosition();
+        upstream = pos.upstream();
+        downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
index b856a21bc8b11c5b674c85a83edc144aa3fecae4..5eb036a6b86a46cd3bcb194dff64b1949e17e87e 100644 (file)
@@ -259,28 +259,6 @@ CSSStyleDeclarationImpl *ApplyStyleCommand::style() const
     return impl()->style();
 }
 
-//------------------------------------------------------------------------------------------
-// DeleteCollapsibleWhitespaceCommand
-
-DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand(DocumentImpl *document)
-    : CompositeEditCommand(new DeleteCollapsibleWhitespaceCommandImpl(document))
-{
-}
-
-DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand(DocumentImpl *document, const Selection &selection)
-    : CompositeEditCommand(new DeleteCollapsibleWhitespaceCommandImpl(document, selection))
-{
-}
-
-DeleteCollapsibleWhitespaceCommand::~DeleteCollapsibleWhitespaceCommand()
-{
-}
-       
-DeleteCollapsibleWhitespaceCommandImpl *DeleteCollapsibleWhitespaceCommand::impl() const
-{
-    return static_cast<DeleteCollapsibleWhitespaceCommandImpl *>(get());
-}
-
 //------------------------------------------------------------------------------------------
 // DeleteSelectionCommand
 
@@ -612,35 +590,6 @@ NodeImpl *RemoveNodeCommand::node() const
     return impl()->node();
 }
 
-//------------------------------------------------------------------------------------------
-// RemoveNodeAndPruneCommand
-
-RemoveNodeAndPruneCommand::RemoveNodeAndPruneCommand(DocumentImpl *document, NodeImpl *pruneNode, NodeImpl *stopNode)
-    : CompositeEditCommand(new RemoveNodeAndPruneCommandImpl(document, pruneNode, stopNode))
-{
-}
-
-RemoveNodeAndPruneCommand::~RemoveNodeAndPruneCommand()
-{
-}
-
-RemoveNodeAndPruneCommandImpl *RemoveNodeAndPruneCommand::impl() const
-{
-    return static_cast<RemoveNodeAndPruneCommandImpl *>(get());
-}
-
-NodeImpl *RemoveNodeAndPruneCommand::pruneNode() const
-{
-    IF_IMPL_NULL_RETURN_ARG(0);
-    return impl()->pruneNode();
-}
-
-NodeImpl *RemoveNodeAndPruneCommand::stopNode() const
-{
-    IF_IMPL_NULL_RETURN_ARG(0);
-    return impl()->stopNode();
-}
-
 //------------------------------------------------------------------------------------------
 // RemoveNodePreservingChildrenCommand
 
index 9a4006344de73147ab05aca497749708422fbe65..cb2099a56c335166389f4b824b801a3a7e953489 100644 (file)
@@ -49,7 +49,6 @@ namespace khtml {
 class AppendNodeCommandImpl;
 class ApplyStyleCommandImpl;
 class CompositeEditCommandImpl;
-class DeleteCollapsibleWhitespaceCommandImpl;
 class DeleteSelectionCommandImpl;
 class DeleteTextCommandImpl;
 class EditCommand;
@@ -64,7 +63,6 @@ class ReplaceSelectionCommandImpl;
 class RemoveCSSPropertyCommandImpl;
 class RemoveNodeAttributeCommandImpl;
 class RemoveNodeCommandImpl;
-class RemoveNodeAndPruneCommandImpl;
 class RemoveNodePreservingChildrenCommandImpl;
 class SetNodeAttributeCommandImpl;
 class SplitTextNodeCommandImpl;
@@ -78,7 +76,6 @@ enum ECommandID {
     AppendNodeCommandID,
     ApplyStyleCommandID,
     CompositeEditCommandID,
-    DeleteCollapsibleWhitespaceCommandID,
     DeleteSelectionCommandID,
     DeleteTextCommandID,
     InputNewlineCommandID,
@@ -91,7 +88,6 @@ enum ECommandID {
     RemoveCSSPropertyCommandID,
     RemoveNodeAttributeCommandID,
     RemoveNodeCommandID,
-    RemoveNodeAndPruneCommandID,
     RemoveNodePreservingChildrenCommandID,
     SetNodeAttributeCommandID,
     SplitTextNodeCommandID,
@@ -210,21 +206,6 @@ private:
     inline ApplyStyleCommandImpl *impl() const;
 };
 
-//------------------------------------------------------------------------------------------
-// DeleteCollapsibleWhitespaceCommand
-
-class DeleteCollapsibleWhitespaceCommand : public CompositeEditCommand
-{
-public:
-       DeleteCollapsibleWhitespaceCommand(DOM::DocumentImpl *document);
-       DeleteCollapsibleWhitespaceCommand(DOM::DocumentImpl *document, const DOM::Selection &selection);
-    
-       virtual ~DeleteCollapsibleWhitespaceCommand();
-
-private:
-    inline DeleteCollapsibleWhitespaceCommandImpl *impl() const;
-};
-
 //------------------------------------------------------------------------------------------
 // DeleteSelectionCommand
 
@@ -412,22 +393,6 @@ private:
     inline RemoveNodeCommandImpl *impl() const;
 };
 
-//------------------------------------------------------------------------------------------
-// RemoveNodeAndPruneCommand
-
-class RemoveNodeAndPruneCommand : public CompositeEditCommand
-{
-public:
-       RemoveNodeAndPruneCommand(DOM::DocumentImpl *, DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0);
-       virtual ~RemoveNodeAndPruneCommand();
-
-    DOM::NodeImpl *pruneNode() const;
-    DOM::NodeImpl *stopNode() const;
-    
-private:
-    inline RemoveNodeAndPruneCommandImpl *impl() const;
-};
-
 //------------------------------------------------------------------------------------------
 // RemoveNodePreservingChildrenCommand
 
index e9fe07f5072a031af0424cda3f9f4965faace20f..870f99009d8aba2d766245d3d1b16af920daf572 100644 (file)
@@ -77,6 +77,7 @@ using DOM::Position;
 using DOM::Range;
 using DOM::RangeImpl;
 using DOM::Selection;
+using DOM::StayInBlock;
 using DOM::TextImpl;
 using DOM::TreeWalkerImpl;
 
@@ -124,90 +125,6 @@ static inline bool isWS(const Position &pos)
     return isWS(string[pos.offset()]);
 }
 
-static bool shouldPruneNode(NodeImpl *node)
-{
-    if (!node)
-        return false;
-
-    RenderObject *renderer = node->renderer();
-    if (!renderer)
-        return true;
-
-    if (node->hasChildNodes())
-        return false;
-        
-    if (node->rootEditableElement() == node)
-        return false;
-        
-    if (renderer->isBR() || renderer->isReplaced())
-        return false;
-        
-    if (node->isTextNode()) {
-        TextImpl *text = static_cast<TextImpl *>(node);
-        if (text->length() == 0)
-            return true;
-        return false;
-    }
-    
-    if (!node->isHTMLElement() && !node->isXMLElementNode())
-        return false;
-    
-    if (node->id() == ID_BODY)
-        return false;
-            
-    if (!node->isContentEditable())
-        return false;
-            
-    return true;
-}
-
-static Position leadingWhitespacePosition(const Position &pos)
-{
-    ASSERT(pos.notEmpty());
-
-    Selection selection(pos);
-    Position prev = pos.previousCharacterPosition();
-    if (prev != pos && prev.node()->inSameContainingBlockFlowElement(pos.node()) && prev.node()->isTextNode()) {
-        DOMString string = static_cast<TextImpl *>(prev.node())->data();
-        if (isWS(string[prev.offset()]))
-            return prev;
-    }
-
-    return Position();
-}
-
-static Position trailingWhitespacePosition(const Position &pos)
-{
-    ASSERT(pos.notEmpty());
-
-    if (pos.node()->isTextNode()) {
-        TextImpl *textNode = static_cast<TextImpl *>(pos.node());
-        if (pos.offset() >= (long)textNode->length()) {
-            Position next = pos.nextCharacterPosition();
-            if (next != pos && next.node()->inSameContainingBlockFlowElement(pos.node()) && next.node()->isTextNode()) {
-                DOMString string = static_cast<TextImpl *>(next.node())->data();
-                if (isWS(string[0]))
-                    return next;
-            }
-        }
-        else {
-            DOMString string = static_cast<TextImpl *>(pos.node())->data();
-            if (isWS(string[pos.offset()]))
-                return pos;
-        }
-    }
-
-    return Position();
-}
-
-static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
-{
-    ASSERT(text1);
-    ASSERT(text2);
-    
-    return (text1->nextSibling() == text2);
-}
-
 static DOMString &nonBreakingSpaceString()
 {
     static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
@@ -222,7 +139,12 @@ static DOMString &styleSpanClassString()
 
 static void debugPosition(const char *prefix, const Position &pos)
 {
-    LOG(Editing, "%s%s %p : %d", prefix, getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+    if (!prefix)
+        prefix = "";
+    if (pos.isEmpty())
+        LOG(Editing, "%s <empty>", prefix);
+    else
+        LOG(Editing, "%s%s %p : %d", prefix, getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
 }
 
 //------------------------------------------------------------------------------------------
@@ -311,7 +233,7 @@ void EditCommandImpl::apply()
     ASSERT(m_document);
     ASSERT(m_document->part());
     ASSERT(state() == NotApplied);
-    
     doApply();
     
     m_state = Applied;
@@ -499,12 +421,6 @@ void CompositeEditCommandImpl::removeNode(NodeImpl *removeChild)
     applyCommandToComposite(cmd);
 }
 
-void CompositeEditCommandImpl::removeNodeAndPrune(NodeImpl *pruneNode, NodeImpl *stopNode)
-{
-    RemoveNodeAndPruneCommand cmd(document(), pruneNode, stopNode);
-    applyCommandToComposite(cmd);
-}
-
 void CompositeEditCommandImpl::removeNodePreservingChildren(NodeImpl *removeChild)
 {
     RemoveNodePreservingChildrenCommand cmd(document(), removeChild);
@@ -566,18 +482,6 @@ void CompositeEditCommandImpl::deleteSelection(const Selection &selection)
     }
 }
 
-void CompositeEditCommandImpl::deleteCollapsibleWhitespace()
-{
-    DeleteCollapsibleWhitespaceCommand cmd(document());
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommandImpl::deleteCollapsibleWhitespace(const Selection &selection)
-{
-    DeleteCollapsibleWhitespaceCommand cmd(document(), selection);
-    applyCommandToComposite(cmd);
-}
-
 void CompositeEditCommandImpl::removeCSSProperty(CSSStyleDeclarationImpl *decl, int property)
 {
     RemoveCSSPropertyCommand cmd(document(), decl, property);
@@ -640,6 +544,39 @@ ElementImpl *CompositeEditCommandImpl::applyTypingStyle(NodeImpl *child) const
     return element;
 }
 
+void CompositeEditCommandImpl::deleteUnrenderedText(const Position &pos)
+{
+    Position ending = pos.upstream();
+    Position upstream = pos.upstream(StayInBlock);
+    Position downstream = pos.downstream(StayInBlock);
+    
+    NodeImpl *node = upstream.node();
+    while (node != downstream.node()) {
+        NodeImpl *next = node->traverseNextNode();
+        if (node->isTextNode()) {
+            if (!node->renderer() || !static_cast<RenderText *>(node->renderer())->firstTextBox())
+                removeNode(node);
+            else {
+                TextImpl *text = static_cast<TextImpl *>(node);
+                if ((int)text->length() > text->caretMaxOffset())
+                    deleteText(text, text->caretMaxOffset(), text->length() - text->caretMaxOffset());
+            }
+        }
+        node = next;
+    }
+    if (downstream.node()->isTextNode()) {
+        if (!node->renderer() || !static_cast<RenderText *>(node->renderer())->firstTextBox())
+            removeNode(downstream.node());
+        else {
+            TextImpl *text = static_cast<TextImpl *>(downstream.node());
+            if (text->caretMinOffset() > 0)
+                deleteText(text, 0, text->caretMinOffset());
+        }
+    }
+    setEndingSelection(ending);
+}
+
+
 //==========================================================================================
 // Concrete commands
 //------------------------------------------------------------------------------------------
@@ -716,15 +653,15 @@ void ApplyStyleCommandImpl::doApply()
         return;
 
     // adjust to the positions we want to use for applying style
-    Position start(endingSelection().start().equivalentDownstreamPosition().equivalentRangeCompliantPosition());
-    Position end(endingSelection().end().equivalentUpstreamPosition());
+    Position start(endingSelection().start().downstream().equivalentRangeCompliantPosition());
+    Position end(endingSelection().end().upstream(StayInBlock));
 
     // Remove style from the selection.
     // Use the upstream position of the start for removing style.
     // This will ensure we remove all traces of the relevant styles from the selection
     // and prevent us from adding redundant ones, as described in:
     // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
-    removeStyle(start.equivalentUpstreamPosition(), end);
+    removeStyle(start.upstream(StayInBlock), end);
     
     bool splitStart = splitTextAtStartIfNeeded(start, end); 
     if (splitStart) {
@@ -844,7 +781,7 @@ bool ApplyStyleCommandImpl::nodeFullySelected(const NodeImpl *node) const
 {
     ASSERT(node);
 
-    Position end(endingSelection().end().equivalentUpstreamPosition());
+    Position end(endingSelection().end().upstream(StayInBlock));
     
     if (node == end.node())
         return end.offset() >= node->caretMaxOffset();
@@ -980,166 +917,6 @@ Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos)
     return pos;
 }
 
-//------------------------------------------------------------------------------------------
-// DeleteCollapsibleWhitespaceCommandImpl
-
-DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
-    : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false)
-{
-}
-
-DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const Selection &selection)
-    : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
-{
-}
-
-DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
-{
-}
-
-int DeleteCollapsibleWhitespaceCommandImpl::commandID() const
-{
-    return DeleteCollapsibleWhitespaceCommandID;
-}
-
-static bool shouldDeleteUpstreamPosition(const Position &pos)
-{
-    if (!pos.node()->isTextNode())
-        return false;
-        
-    RenderObject *renderer = pos.node()->renderer();
-    if (!renderer)
-        return true;
-        
-    TextImpl *textNode = static_cast<TextImpl *>(pos.node());
-    if (pos.offset() >= (long)textNode->length())
-        return false;
-
-    if (pos.isLastRenderedPositionInEditableBlock())
-        return false;
-
-    if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
-        return false;
-
-    RenderText *textRenderer = static_cast<RenderText *>(renderer);
-
-    for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-        if (pos.offset() < box->m_start) {
-            return true;
-        }
-        if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len)
-            return false;
-    }
-    
-    return true;
-}
-
-Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const Position &pos)
-{
-    Position upstream = pos.equivalentUpstreamPosition();
-    Position downstream = pos.equivalentDownstreamPosition();
-    
-    bool del = shouldDeleteUpstreamPosition(upstream);
-
-    LOG(Editing, "pos:        %s [%p:%d]\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
-    if (upstream == downstream) {
-        LOG(Editing, "same:       %s [%p:%d]\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
-    }
-    else {
-        LOG(Editing, "upstream:   %s %s [%p:%d]\n", del ? "DELETE" : "SKIP", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
-        PositionIterator it(upstream);
-        for (it.next(); it.current() != downstream; it.next()) {
-            if (it.current().node()->isTextNode() && (long)static_cast<TextImpl *>(it.current().node())->length() == it.current().offset())
-                LOG(Editing, "   node:    AT END %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
-            else
-                LOG(Editing, "   node:    DELETE %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
-        }
-        LOG(Editing, "downstream: %s [%p:%d]\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
-    }
-
-    if (upstream == downstream)
-        return upstream;
-        
-    PositionIterator it(upstream);
-    Position deleteStart = upstream;
-    if (!del) {
-        deleteStart = it.peekNext();
-        if (deleteStart == downstream)
-            return upstream;
-    }
-    
-    Position endingPosition = upstream;
-    
-    while (it.current() != downstream) {
-        Position next = it.peekNext();
-        if (next.node() != deleteStart.node()) {
-            if (deleteStart.node()->isTextNode()) {
-                TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
-                unsigned long count = it.current().offset() - deleteStart.offset();
-                if (count == textNode->length()) {
-                    LOG(Editing, "   removeNodeAndPrune 1: [%p]\n", textNode);
-                    if (textNode == endingPosition.node())
-                        endingPosition = Position(next.node(), next.node()->caretMinOffset());
-                    removeNodeAndPrune(textNode);
-                }
-                else {
-                    LOG(Editing, "   deleteText 1: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), it.current().offset() - deleteStart.offset());
-                    deleteText(textNode, deleteStart.offset(), count);
-                }
-            }
-            deleteStart = next;
-        }
-        else if (next == downstream) {
-            if (downstream.node()->isTextNode()) {
-                ASSERT(deleteStart.node() == downstream.node());
-                TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
-                unsigned long count = downstream.offset() - deleteStart.offset();
-                ASSERT(count <= textNode->length());
-                if (count == textNode->length()) {
-                    LOG(Editing, "   removeNodeAndPrune 2: [%p]\n", textNode);
-                    removeNodeAndPrune(textNode);
-                }
-                else {
-                    LOG(Editing, "   deleteText 2: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), count);
-                    deleteText(textNode, deleteStart.offset(), count);
-                    m_charactersDeleted = count;
-                    endingPosition = Position(downstream.node(), downstream.offset() - m_charactersDeleted);
-                }
-            }
-        }
-        
-        it.setPosition(next);
-    }
-    
-    return endingPosition;
-}
-
-void DeleteCollapsibleWhitespaceCommandImpl::doApply()
-{
-    // If selection has not been set to a custom selection when the command was created,
-    // use the current ending selection.
-    if (!m_hasSelectionToCollapse)
-        m_selectionToCollapse = endingSelection();
-    int state = m_selectionToCollapse.state();
-    if (state == Selection::CARET) {
-        Position endPosition = deleteWhitespace(m_selectionToCollapse.start());
-        setEndingSelection(endPosition);
-        LOG(Editing, "-----------------------------------------------------\n");
-    }
-    else if (state == Selection::RANGE) {
-        Position startPosition = deleteWhitespace(m_selectionToCollapse.start());
-        LOG(Editing, "-----------------------------------------------------\n");
-        Position endPosition = m_selectionToCollapse.end();
-        if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
-            LOG(Editing, "adjust end position by %d\n", m_charactersDeleted);
-            endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted);
-        }
-        endPosition = deleteWhitespace(endPosition);
-        setEndingSelection(Selection(startPosition, endPosition));
-        LOG(Editing, "=====================================================\n");
-    }
-}
-
 //------------------------------------------------------------------------------------------
 // DeleteSelectionCommandImpl
 
@@ -1162,71 +939,6 @@ int DeleteSelectionCommandImpl::commandID() const
     return DeleteSelectionCommandID;
 }
 
-void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
-{
-    Selection selection = endingSelection();
-
-    if (selection.state() != Selection::CARET)
-        return;
-
-    Position pos(selection.start());
-    
-    if (!pos.node()->isTextNode())
-        return;
-
-    TextImpl *textNode = static_cast<TextImpl *>(pos.node());
-    
-    if (pos.offset() == 0) {
-        PositionIterator it(pos);
-        Position prev = it.previous();
-        if (prev == pos)
-            return;
-        if (prev.node()->isTextNode()) {
-            TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
-            if (textNodesAreJoinable(prevTextNode, textNode)) {
-                joinTextNodes(prevTextNode, textNode);
-                setEndingSelection(Position(textNode, prevTextNode->length()));
-                LOG(Editing, "joinTextNodesWithSameStyle [1]\n");
-            }
-        }
-    }
-    else if (pos.offset() == (long)textNode->length()) {
-        PositionIterator it(pos);
-        Position next = it.next();
-        if (next == pos)
-            return;
-        if (next.node()->isTextNode()) {
-            TextImpl *nextTextNode = static_cast<TextImpl *>(next.node());
-            if (textNodesAreJoinable(textNode, nextTextNode)) {
-                joinTextNodes(textNode, nextTextNode);
-                setEndingSelection(Position(nextTextNode, pos.offset()));
-                LOG(Editing, "joinTextNodesWithSameStyle [2]\n");
-            }
-        }
-    }
-}
-
-bool DeleteSelectionCommandImpl::containsOnlyWhitespace(const Position &start, const Position &end)
-{
-    // Returns whether the range contains only whitespace characters.
-    // This is inclusive of the start, but not of the end.
-    PositionIterator it(start);
-    while (!it.atEnd()) {
-        if (!it.current().node()->isTextNode())
-            return false;
-        const DOMString &text = static_cast<TextImpl *>(it.current().node())->data();
-        // EDIT FIXME: signed/unsigned mismatch
-        if (text.length() > INT_MAX)
-            return false;
-        if (it.current().offset() < (int)text.length() && !isWS(text[it.current().offset()]))
-            return false;
-        it.next();
-        if (it.current() == end)
-            break;
-    }
-    return true;
-}
-
 CSSStyleDeclarationImpl *DeleteSelectionCommandImpl::computeTypingStyle(const Position &pos) const
 {
     ElementImpl *element = pos.element();
@@ -1282,20 +994,26 @@ void DeleteSelectionCommandImpl::doApply()
     if (m_selectionToDelete.state() != Selection::RANGE)
         return;
 
-    deleteCollapsibleWhitespace(m_selectionToDelete);
-    Selection selection = endingSelection();
-
-    Position upstreamStart(selection.start().equivalentUpstreamPosition());
-    Position downstreamStart(selection.start().equivalentDownstreamPosition());
-    Position upstreamEnd(selection.end().equivalentUpstreamPosition());
-    Position downstreamEnd(selection.end().equivalentDownstreamPosition());
-
-    if (upstreamStart == downstreamEnd)
-        // after collapsing whitespace, selection is empty...no work to do
-        return;
+    Position upstreamStart(m_selectionToDelete.start().upstream(StayInBlock));
+    Position downstreamStart(m_selectionToDelete.start().downstream());
+    Position upstreamEnd(m_selectionToDelete.end().upstream(StayInBlock));
+    Position downstreamEnd(m_selectionToDelete.end().downstream());
+    Position endingPosition;
 
-    NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
-    NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
+    // Save away whitespace situation before doing any deletions
+    Position leading = upstreamStart.leadingWhitespacePosition();
+    Position trailing = downstreamEnd.trailingWhitespacePosition();
+    bool trailingValid = true;
+    
+    debugPosition("upstreamStart    ", upstreamStart);
+    debugPosition("downstreamStart  ", downstreamStart);
+    debugPosition("upstreamEnd      ", upstreamEnd);
+    debugPosition("downstreamEnd    ", downstreamEnd);
+    debugPosition("leading          ", leading);
+    debugPosition("trailing         ", trailing);
+    
+    NodeImpl *startBlock = downstreamStart.node()->enclosingBlockFlowElement();
+    NodeImpl *endBlock = upstreamEnd.node()->enclosingBlockFlowElement();
     if (!startBlock || !endBlock)
         // Can't figure out what blocks we're in. This can happen if
         // the document structure is not what we are expecting, like if
@@ -1303,93 +1021,6 @@ void DeleteSelectionCommandImpl::doApply()
         // has been changed to display: inline. Some day it might
         // be nice to be able to deal with this, but for now, bail.
         return;
-    bool startBlockEndBlockAreSiblings = startBlock->parentNode() == endBlock->parentNode();
-
-    Position endingPosition;
-    bool adjustEndingPositionDownstream = false;
-
-    bool onlyWhitespace = containsOnlyWhitespace(upstreamStart, downstreamEnd);
-    bool startCompletelySelected = !onlyWhitespace &&
-        (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
-        ((downstreamStart.node() != upstreamEnd.node()) ||
-         (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset())));
-
-    bool endCompletelySelected = !onlyWhitespace &&
-        (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
-        ((downstreamStart.node() != upstreamEnd.node()) ||
-         (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset())));
-
-    unsigned long startRenderedOffset = downstreamStart.renderedOffset();
-    
-    bool startAtStartOfRootEditableElement = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableElement();
-    bool startAtStartOfBlock = startAtStartOfRootEditableElement || 
-        (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
-    bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
-
-    debugPosition("upstreamStart:       ", upstreamStart);
-    debugPosition("downstreamStart:     ", downstreamStart);
-    debugPosition("upstreamEnd:         ", upstreamEnd);
-    debugPosition("downstreamEnd:       ", downstreamEnd);
-    LOG(Editing,  "start selected:      %s", startCompletelySelected ? "YES" : "NO");
-    LOG(Editing,  "at start block:      %s", startAtStartOfBlock ? "YES" : "NO");
-    LOG(Editing,  "at start root block: %s", startAtStartOfRootEditableElement ? "YES" : "NO");
-    LOG(Editing,  "at end block:        %s", endAtEndOfBlock ? "YES" : "NO");
-    LOG(Editing,  "only whitespace:     %s", onlyWhitespace ? "YES" : "NO");
-
-    // Determine where to put the caret after the deletion
-    if (startAtStartOfBlock) {
-        LOG(Editing,  "ending position case 1");
-        endingPosition = Position(startBlock, 0);
-        adjustEndingPositionDownstream = true;
-    }
-    else if (!startCompletelySelected) {
-        LOG(Editing,  "ending position case 2");
-        endingPosition = upstreamStart;
-        if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
-            adjustEndingPositionDownstream = true;
-    }
-    else if (upstreamStart != downstreamStart) {
-        LOG(Editing,  "ending position case 3");
-        endingPosition = upstreamStart;
-        if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
-            adjustEndingPositionDownstream = true;
-    }
-   
-    //
-    // Figure out the whitespace conversions to do
-    //
-    if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
-        // convert trailing whitespace
-        Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
-        if (trailing.notEmpty()) {
-            debugPosition("convertTrailingWhitespace: ", trailing);
-            Position collapse = trailing.nextCharacterPosition();
-            if (collapse != trailing)
-                deleteCollapsibleWhitespace(collapse);
-            TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
-            replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
-        }
-    }
-    else if (!startAtStartOfBlock && endAtEndOfBlock) {
-        // convert leading whitespace
-        Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
-        if (leading.notEmpty()) {
-            debugPosition("convertLeadingWhitespace:  ", leading);
-            TextImpl *textNode = static_cast<TextImpl *>(leading.node());
-            replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
-        }
-    }
-    else if (!startAtStartOfBlock && !endAtEndOfBlock) {
-        // convert contiguous whitespace
-        Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
-        Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
-        if (leading.notEmpty() && trailing.notEmpty()) {
-            debugPosition("convertLeadingWhitespace [contiguous]:  ", leading);
-            TextImpl *textNode = static_cast<TextImpl *>(leading.node());
-            replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
-        }
-    }
 
     //
     // Figure out the typing style and set it on the part.
@@ -1399,88 +1030,78 @@ void DeleteSelectionCommandImpl::doApply()
     // cut command.
     //
     document()->part()->setTypingStyle(computeTypingStyle(downstreamStart));
-
-    //
-    // Do the delete
-    //
-    NodeImpl *n = downstreamStart.node()->traverseNextNode();
-
-    // work on start node
-    if (startCompletelySelected) {
-        LOG(Editing,  "start node delete case 1");
-        removeNodeAndPrune(downstreamStart.node(), startBlock);
-        // Fix up ending position if the prune operation removed the position's node from the document
-        if (!endingPosition.node()->inDocument())
-            endingPosition = upstreamStart;
-    }
-    else if (onlyWhitespace && isWS(upstreamStart)) {
-        // Selection only contains whitespace. This is really a special-case to 
-        // handle significant whitespace that is collapsed at the end of a line,
-        // but also handles deleting a space in mid-line.
-        LOG(Editing,  "start node delete case 2");
-        ASSERT(upstreamStart.node()->isTextNode());
-        TextImpl *text = static_cast<TextImpl *>(upstreamStart.node());
-        int offset = upstreamStart.offset();
-        // EDIT FIXME: Signed/unsigned mismatch
-        int length = text->length();
-        if (length == upstreamStart.offset())
-            offset--;
-        deleteText(text, offset, 1);
-    }
-    else if (downstreamStart.node()->isTextNode()) {
-        LOG(Editing,  "start node delete case 3");
-        TextImpl *text = static_cast<TextImpl *>(downstreamStart.node());
-        int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->length();
-        if (endOffset > downstreamStart.offset()) {
-            deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
+    
+    NodeImpl *startNode = upstreamStart.node();
+    int startOffset = upstreamStart.offset();
+    if (startOffset >= startNode->caretMaxOffset()) {
+        // None of the first node is to be deleted, so move to next.
+        startNode = startNode->traverseNextNode();
+        startOffset = 0;
+    }
+
+    if (startNode == downstreamEnd.node()) {
+        // handle delete in one node
+        if (!startNode->renderer() || 
+            (startOffset <= startNode->caretMinOffset() && downstreamEnd.offset() >= startNode->caretMaxOffset())) {
+            // just delete
+            removeNode(startNode);
+        }
+        else {
+            // in a text node that needs to be trimmed
+            TextImpl *text = static_cast<TextImpl *>(startNode);
+            deleteText(text, startOffset, downstreamEnd.offset() - startOffset);
+            trailingValid = false;
         }
     }
     else {
-        // we have clipped the end of a non-text element
-        // the offset must be 1 here. if it is, do nothing and move on.
-        LOG(Editing,  "start node delete case 4");
-        ASSERT(downstreamStart.offset() == 1);
-    }
-
-    if (!onlyWhitespace && downstreamStart.node() != upstreamEnd.node()) {
-        // work on intermediate nodes
-        while (n != upstreamEnd.node()) {
-            NodeImpl *d = n;
-            n = n->nextNodeConsideringAtomicNodes();
-            if (d->renderer() && d->renderer()->isEditable())
-                removeNodeAndPrune(d, startBlock);
-            // Fix up ending position if the prune operation removed the position's node from the document
-            if (!endingPosition.node()->inDocument())
-                endingPosition = Position(n, n->caretMinOffset());
+        NodeImpl *node = startNode;
+        
+        if (startOffset > 0) {
+            // in a text node that needs to be trimmed
+            TextImpl *text = static_cast<TextImpl *>(node);
+            deleteText(text, startOffset, text->length() - startOffset);
+            node = node->traverseNextNode();
         }
         
-        // work on end node
-        ASSERT(n == upstreamEnd.node());
-        if (endCompletelySelected) {
-            removeNodeAndPrune(upstreamEnd.node(), startBlock);
-            // Fix up ending position if the prune operation removed the position's node from the document
-            if (!endingPosition.node()->inDocument())
-                endingPosition = downstreamEnd;
+        // handle deleting all nodes that are completely selected
+        while (node && node != downstreamEnd.node()) {
+            if (!downstreamEnd.node()->isAncestor(node)) {
+                NodeImpl *nextNode = node->traverseNextSibling();
+                removeNode(node);
+                node = nextNode;
+            }
+            else {
+                NodeImpl *n = node->lastChild();
+                while (n && n->lastChild())
+                    n = n->lastChild();
+                if (n == downstreamEnd.node() && downstreamEnd.offset() >= downstreamEnd.node()->caretMaxOffset()) {
+                    NodeImpl *nextNode = node->traverseNextSibling();
+                    removeNode(node);
+                    node = nextNode;
+                } 
+                else {
+                    node = node->traverseNextNode();
+                }
+            }
         }
-        else if (upstreamEnd.node()->isTextNode()) {
+
+        if (upstreamEnd.node() != startNode && upstreamEnd.node()->inDocument() && upstreamEnd.offset() >= upstreamEnd.node()->caretMinOffset()) {
+            // in a text node that needs to be trimmed
+            // offset must be less than the max offset, otherwise it would have been deleted in the while
+            // loop just above.
+            ASSERT(upstreamEnd.offset() < upstreamEnd.node()->caretMaxOffset());
+            TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
             if (upstreamEnd.offset() > 0) {
-                TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
                 deleteText(text, 0, upstreamEnd.offset());
+                trailingValid = false;
             }
-        }
-        else {
-            // we have clipped the beginning of a non-text element
-            // the offset must be 0 here. if it is, do nothing and move on.
-            ASSERT(downstreamStart.offset() == 0);
+            if (!upstreamStart.node()->inDocument())
+                endingPosition = Position(text, 0);
         }
     }
-
-    // Do block merge if start and end of selection are in different blocks
-    // and the blocks are siblings. This is a first cut at this rule arrived
-    // at by doing a bunch of edits and settling on the behavior that made
-    // the most sense. This could change in the future as we get more
-    // experience with how this should behave.
-    if (startBlock != endBlock && startBlockEndBlockAreSiblings) {
+    
+    // Do block merge if start and end of selection are in different blocks.
+    if (endBlock != startBlock && endBlock->inDocument()) {
         LOG(Editing,  "merging content to start block");
         NodeImpl *node = endBlock->firstChild();
         while (node) {
@@ -1489,20 +1110,66 @@ void DeleteSelectionCommandImpl::doApply()
             removeNode(moveNode);
             appendNode(moveNode, startBlock);
         }
+        removeNode(endBlock);
     }
+      
+    // Figure out where the end position should be
+    if (endingPosition.notEmpty())
+        goto FixupWhitespace;
+
+    endingPosition = upstreamStart;
+    if (endingPosition.node()->inDocument())
+        goto FixupWhitespace;
+    
+    endingPosition = downstreamEnd;
+    if (endingPosition.node()->inDocument())
+        goto FixupWhitespace;
+
+    endingPosition = Position(startBlock, 0);
+    if (endingPosition.node()->inDocument())
+        goto FixupWhitespace;
+
+    endingPosition = Position(endBlock, 0);
+    if (endingPosition.node()->inDocument())
+        goto FixupWhitespace;
 
-    if (adjustEndingPositionDownstream) {
-        LOG(Editing,  "adjust ending position downstream");
-        endingPosition = endingPosition.equivalentDownstreamPosition();
+    endingPosition = Position(document()->documentElement(), 0);
+
+    // Perform whitespace fixup
+    FixupWhitespace:
+
+    if (leading.notEmpty() || trailing.notEmpty())
+        document()->updateLayout();
+
+    debugPosition("endingPosition   ", endingPosition);
+    
+    if (leading.notEmpty() && !leading.isRenderedCharacter()) {
+        LOG(Editing, "replace leading");
+        TextImpl *textNode = static_cast<TextImpl *>(leading.node());
+        replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
+    }
+
+    if (trailing.notEmpty()) {
+        if (trailingValid) {
+            if (!trailing.isRenderedCharacter()) {
+                LOG(Editing, "replace trailing [valid]");
+                TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
+                replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
+            }
+        }
+        else {
+            Position pos = endingPosition.downstream(StayInBlock);
+            pos = Position(pos.node(), pos.offset() - 1);
+            if (isWS(pos) && !pos.isRenderedCharacter()) {
+                LOG(Editing, "replace trailing [invalid]");
+                TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+                replaceText(textNode, pos.offset(), 1, nonBreakingSpaceString());
+                endingPosition = pos;
+            }
+        }
     }
 
-    // Fall back to the start block if the ending position is not in the document.
-    if (!endingPosition.node()->inDocument())
-        endingPosition = Position(startBlock, 0);
-    debugPosition("ending position:     ", endingPosition);
     setEndingSelection(endingPosition);
-
-    LOG(Editing, "-----------------------------------------------------\n");
 }
 
 //------------------------------------------------------------------------------------------
@@ -1513,6 +1180,7 @@ DeleteTextCommandImpl::DeleteTextCommandImpl(DocumentImpl *document, TextImpl *n
 {
     ASSERT(m_node);
     ASSERT(m_offset >= 0);
+    ASSERT(m_offset < (long)m_node->length());
     ASSERT(m_count >= 0);
     
     m_node->ref();
@@ -1573,7 +1241,7 @@ void InputNewlineCommandImpl::insertNodeAfterPosition(NodeImpl *node, const Posi
     // Insert the BR after the caret position. In the case the
     // position is a block, do an append. We don't want to insert
     // the BR *after* the block.
-    Position upstream(pos.equivalentUpstreamPosition());
+    Position upstream(pos.upstream(StayInBlock));
     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
     if (cb == pos.node())
         appendNode(node, cb);
@@ -1586,7 +1254,7 @@ void InputNewlineCommandImpl::insertNodeBeforePosition(NodeImpl *node, const Pos
     // Insert the BR after the caret position. In the case the
     // position is a block, do an append. We don't want to insert
     // the BR *before* the block.
-    Position upstream(pos.equivalentUpstreamPosition());
+    Position upstream(pos.upstream(StayInBlock));
     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
     if (cb == pos.node())
         appendNode(node, cb);
@@ -1597,6 +1265,8 @@ void InputNewlineCommandImpl::insertNodeBeforePosition(NodeImpl *node, const Pos
 void InputNewlineCommandImpl::doApply()
 {
     deleteSelection();
+    deleteUnrenderedText(endingSelection().start());
+    
     Selection selection = endingSelection();
 
     int exceptionCode = 0;
@@ -1610,7 +1280,7 @@ void InputNewlineCommandImpl::doApply()
     if (typingStyle && typingStyle->length() > 0)
         nodeToInsert = applyTypingStyle(breakNode);
     
-    Position pos(selection.start().equivalentDownstreamPosition());
+    Position pos(selection.start().upstream(StayInBlock));
     bool atStart = pos.offset() <= pos.node()->caretMinOffset();
     bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
     
@@ -1638,12 +1308,28 @@ void InputNewlineCommandImpl::doApply()
         // Split a text node
         LOG(Editing, "input newline case 3");
         ASSERT(pos.node()->isTextNode());
+        
+        // See if there is trailing whitespace we need to consider
+        // Note: leading whitespace just works. Blame the web.
+        Position trailing = pos.downstream(StayInBlock).trailingWhitespacePosition();
+
+        // Do the split
         TextImpl *textNode = static_cast<TextImpl *>(pos.node());
         TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
-        deleteText(textNode, 0, selection.start().offset());
+        deleteText(textNode, 0, pos.offset());
         insertNodeBefore(textBeforeNode, textNode);
         insertNodeBefore(nodeToInsert, textNode);
-        setEndingSelection(Position(textNode, 0));
+        Position endingPosition = Position(textNode, 0);
+        
+        // Handle whitespace that occurs after the split
+        document()->updateLayout();
+        if (trailing.notEmpty() && !endingPosition.isRenderedCharacter()) {
+            // Clear out all whitespace and insert one non-breaking space
+            deleteUnrenderedText(endingPosition);
+            insertText(textNode, 0, nonBreakingSpaceString());
+        }
+        
+        setEndingSelection(endingPosition);
     }
 }
 
@@ -1703,9 +1389,9 @@ Position InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
     
     Position pos = selection.start();
     if (adjustDownstream)
-        pos = pos.equivalentDownstreamPosition();
+        pos = pos.downstream(StayInBlock);
     else
-        pos = pos.equivalentUpstreamPosition();
+        pos = pos.upstream(StayInBlock);
     
     if (!pos.node()->isTextNode()) {
         NodeImpl *textNode = document()->createEditingTextNode("");
@@ -1751,7 +1437,7 @@ Position InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
             }
             
             TextImpl *editingTextNode = document()->createEditingTextNode("");
-            NodeImpl *node = endingSelection().start().equivalentUpstreamPosition().node();
+            NodeImpl *node = endingSelection().start().upstream(StayInBlock).node();
             insertNodeAfter(applyTypingStyle(editingTextNode), node);
             pos = Position(editingTextNode, 0);
         }
@@ -1762,13 +1448,13 @@ Position InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
 void InputTextCommandImpl::execute(const DOMString &text)
 {
     Selection selection = endingSelection();
-    bool adjustDownstream = selection.start().isFirstRenderedPositionOnLine();
+    bool adjustDownstream = selection.start().downstream(StayInBlock).isFirstRenderedPositionOnLine();
 
     // Delete the current selection, or collapse whitespace, as needed
     if (selection.state() == Selection::RANGE)
         deleteSelection();
-    else
-        deleteCollapsibleWhitespace();
+    
+    deleteUnrenderedText(endingSelection().start());
     
     // Make sure the document is set up to receive text
     Position pos = prepareForTextInsertion(adjustDownstream);
@@ -1818,7 +1504,7 @@ void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
         // By checking the character at the downstream position, we can
         // check if there is a rendered WS at the caret
         Position pos(textNode, offset);
-        Position downstream = pos.equivalentDownstreamPosition();
+        Position downstream = pos.downstream();
         if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
             count--; // leave this WS in
         if (count > 0)
@@ -2030,8 +1716,6 @@ void ReplaceSelectionCommandImpl::doApply()
     // Delete the current selection, or collapse whitespace, as needed
     if (selection.state() == Selection::RANGE)
         deleteSelection();
-    else
-        deleteCollapsibleWhitespace();
     
     selection = endingSelection();
     ASSERT(!selection.isEmpty());
@@ -2041,7 +1725,7 @@ void ReplaceSelectionCommandImpl::doApply()
         ASSERT(!lastChild);
     } else if (firstChild == lastChild && firstChild->isTextNode()) {
         // Simple text paste. Treat as if the text were typed.
-        Position upstreamStart(selection.start().equivalentUpstreamPosition());
+        Position upstreamStart(selection.start().upstream(StayInBlock));
         inputText(static_cast<TextImpl *>(firstChild)->data());
         if (m_selectReplacement) {
             // Select what was inserted.
@@ -2288,45 +1972,6 @@ void RemoveNodeCommandImpl::doUnapply()
     ASSERT(exceptionCode == 0);
 }
 
-//------------------------------------------------------------------------------------------
-// RemoveNodeAndPruneCommandImpl
-
-RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *pruneNode, NodeImpl *stopNode)
-    : CompositeEditCommandImpl(document), m_pruneNode(pruneNode), m_stopNode(stopNode)
-{
-    ASSERT(m_pruneNode);
-    m_pruneNode->ref();
-    if (m_stopNode)
-        m_stopNode->ref();
-}
-
-RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
-{
-    m_pruneNode->deref();
-    if (m_stopNode)
-        m_stopNode->deref();
-}
-
-int RemoveNodeAndPruneCommandImpl::commandID() const
-{
-    return RemoveNodeAndPruneCommandID;
-}
-
-void RemoveNodeAndPruneCommandImpl::doApply()
-{
-    NodeImpl *editableBlock = m_pruneNode->enclosingBlockFlowElement();
-    NodeImpl *pruneNode = m_pruneNode;
-    NodeImpl *node = pruneNode->previousNodeConsideringAtomicNodes();
-    removeNode(pruneNode);
-    while (1) {
-        if (node == m_stopNode || editableBlock != node->enclosingBlockFlowElement() || !shouldPruneNode(node))
-            break;
-        pruneNode = node;
-        node = node->previousNodeConsideringAtomicNodes();
-        removeNode(pruneNode);
-    }
-}
-
 //------------------------------------------------------------------------------------------
 // RemoveNodePreservingChildrenCommandImpl
 
@@ -2571,7 +2216,7 @@ void TypingCommandImpl::issueCommandForDeleteKey()
         if (pos.inRenderedContent())
             selectionToDelete = Selection(pos.previousCharacterPosition(), pos);
         else
-            selectionToDelete = Selection(pos.equivalentUpstreamPosition().previousCharacterPosition(), pos.equivalentDownstreamPosition());
+            selectionToDelete = Selection(pos.upstream(StayInBlock).previousCharacterPosition(), pos.downstream());
     }
     deleteSelection(selectionToDelete);
     typingAddedToOpenCommand();
@@ -2588,7 +2233,6 @@ void TypingCommandImpl::deleteKeyPressed()
 // right thing, but less efficiently and with the cost of more
 // objects.
     issueCommandForDeleteKey();
-    typingAddedToOpenCommand();
 #if 0    
     if (m_cmds.count() == 0) {
         issueCommandForDeleteKey();
index fa08f8379b602c5e34e6d94d7212d8fa37ef23f9..68c0e01e56df70922b5b5cd8d3d42080e8489e7b 100644 (file)
@@ -142,8 +142,6 @@ protected:
     //
     void appendNode(DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
     void applyCommandToComposite(EditCommand &);
-    void deleteCollapsibleWhitespace();
-    void deleteCollapsibleWhitespace(const DOM::Selection &selection);
     void deleteKeyPressed();
     void deleteSelection();
     void deleteSelection(const DOM::Selection &selection);
@@ -157,13 +155,13 @@ protected:
     void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property);
     void removeNodeAttribute(DOM::ElementImpl *, int attribute);
     void removeNode(DOM::NodeImpl *removeChild);
-    void removeNodeAndPrune(DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0);
     void removeNodePreservingChildren(DOM::NodeImpl *node);
     void replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText);
     void setNodeAttribute(DOM::ElementImpl *, int attribute, const DOM::DOMString &);
     void splitTextNode(DOM::TextImpl *text, long offset);
 
     DOM::ElementImpl *applyTypingStyle(DOM::NodeImpl *) const;
+    void deleteUnrenderedText(const DOM::Position &pos);
 
     QValueList<EditCommand> m_cmds;
 };
@@ -225,29 +223,6 @@ private:
     DOM::CSSStyleDeclarationImpl *m_style;
 };
 
-//------------------------------------------------------------------------------------------
-// DeleteCollapsibleWhitespaceCommandImpl
-
-class DeleteCollapsibleWhitespaceCommandImpl : public CompositeEditCommandImpl
-{ 
-public:
-       DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document);
-       DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document, const DOM::Selection &selection);
-    
-       virtual ~DeleteCollapsibleWhitespaceCommandImpl();
-       
-    virtual int commandID() const;
-
-       virtual void doApply();
-
-private:
-    DOM::Position deleteWhitespace(const DOM::Position &pos);
-
-    unsigned long m_charactersDeleted;
-    DOM::Selection m_selectionToCollapse;
-    bool m_hasSelectionToCollapse;
-};
-
 //------------------------------------------------------------------------------------------
 // DeleteSelectionCommandImpl
 
@@ -266,7 +241,6 @@ public:
 private:
     void deleteDownstreamWS(const DOM::Position &start);
     bool containsOnlyWhitespace(const DOM::Position &start, const DOM::Position &end);
-    void joinTextNodesWithSameStyle();
     DOM::CSSStyleDeclarationImpl *computeTypingStyle(const DOM::Position &pos) const;
 
     DOM::Selection m_selectionToDelete;
@@ -516,27 +490,6 @@ private:
     DOM::NodeImpl *m_refChild;    
 };
 
-//------------------------------------------------------------------------------------------
-// RemoveNodeAndPruneCommandImpl
-
-class RemoveNodeAndPruneCommandImpl : public CompositeEditCommandImpl
-{
-public:
-       RemoveNodeAndPruneCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0);
-       virtual ~RemoveNodeAndPruneCommandImpl();
-       
-    virtual int commandID() const;
-
-       virtual void doApply();
-
-    DOM::NodeImpl *pruneNode() const { return m_pruneNode; }
-    DOM::NodeImpl *stopNode() const { return m_stopNode; }
-
-private:
-    DOM::NodeImpl *m_pruneNode;
-    DOM::NodeImpl *m_stopNode;
-};
-
 //------------------------------------------------------------------------------------------
 // RemoveNodePreservingChildrenCommandImpl
 
index 47d135a495f498c6c2cc5c3032295eb2a3d1a6f3..c2f144f7ff55408256c2e262175da30aaf4cf83b 100644 (file)
@@ -474,7 +474,7 @@ Range Selection::toRange() const
         // If the selection is a caret, move the range start upstream. This helps us match
         // the conventions of text editors tested, which make style determinations based
         // on the character before the caret, if any. 
-        s = start().equivalentUpstreamPosition().equivalentRangeCompliantPosition();
+        s = start().upstream().equivalentRangeCompliantPosition();
         e = s;
     }
     else {
@@ -490,8 +490,8 @@ Range Selection::toRange() const
         //                       ^ selected
         //
         ASSERT(state() == RANGE);
-        s = start().equivalentDownstreamPosition();
-        e = end().equivalentUpstreamPosition();
+        s = start().downstream();
+        e = end().upstream();
         if ((s.node() == e.node() && s.offset() > e.offset()) || !nodeIsBeforeNode(s.node(), e.node())) {
             // Make sure the start is before the end.
             // The end can wind up before the start if collapsed whitespace is the only thing selected.
@@ -719,7 +719,7 @@ void Selection::validate(ETextGranularity granularity)
     if (start().isEmpty() && end().isEmpty()) {
         m_state = NONE;
     }
-    else if (start() == end() || start().equivalentUpstreamPosition() == end().equivalentUpstreamPosition()) {
+    else if (start() == end() || start().upstream() == end().upstream()) {
         m_state = CARET;
     }
     else {
@@ -730,8 +730,8 @@ void Selection::validate(ETextGranularity granularity)
         // purposes of comparing selections). This is an ideal point of the code
         // to do this operation, since all selection changes that result in a RANGE 
         // come through here before anyone uses it.
-        assignStart(start().equivalentDownstreamPosition());
-        assignEnd(end().equivalentUpstreamPosition());
+        assignStart(start().downstream());
+        assignEnd(end().upstream());
     }
 
     m_needsCaretLayout = true;
@@ -1009,23 +1009,23 @@ void Selection::debugPosition() const
 
     if (start() == end()) {
         Position pos = start();
-        Position upstream = pos.equivalentUpstreamPosition();
-        Position downstream = pos.equivalentDownstreamPosition();
+        Position upstream = pos.upstream();
+        Position downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
     }
     else {
         Position pos = start();
-        Position upstream = pos.equivalentUpstreamPosition();
-        Position downstream = pos.equivalentDownstreamPosition();
+        Position upstream = pos.upstream();
+        Position downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
         fprintf(stderr, "-----------------------------------\n");
         pos = end();
-        upstream = pos.equivalentUpstreamPosition();
-        downstream = pos.equivalentDownstreamPosition();
+        upstream = pos.upstream();
+        downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
index 69cc29aa644c317e032a96dd57c18f4c40c66309..68910ba7cf8191451a654ccc11aad3f601c8190e 100644 (file)
@@ -312,6 +312,8 @@ Position Position::previousWordBoundary() const
     while (tries < 2) {
         if (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE) {
             DOMString t = pos.node()->nodeValue();
+            if (t.isEmpty())
+                return *this;
             QChar *chars = t.unicode();
             uint len = t.length();
             int start, end;
@@ -348,6 +350,8 @@ Position Position::nextWordBoundary() const
     while (tries < 2) {
         if (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE) {
             DOMString t = pos.node()->nodeValue();
+            if (t.isEmpty())
+                return *this;
             QChar *chars = t.unicode();
             uint len = t.length();
             int start, end;
@@ -610,7 +614,7 @@ Position Position::nextLinePosition(int x) const
     return Position(rootElement, rootElement->childNodeCount());
 }
 
-Position Position::equivalentUpstreamPosition() const
+Position Position::upstream(bool stayInBlock) const
 {
     if (!node())
         return Position();
@@ -618,10 +622,12 @@ Position Position::equivalentUpstreamPosition() const
     NodeImpl *block = node()->enclosingBlockFlowElement();
     
     PositionIterator it(*this);            
-    for (; !it.atStart(); it.previous()) {   
-        NodeImpl *currentBlock = it.current().node()->enclosingBlockFlowElement();
-        if (block != currentBlock)
-            return it.next();
+    for (; !it.atStart(); it.previous()) {
+        if (stayInBlock) {
+            NodeImpl *currentBlock = it.current().node()->enclosingBlockFlowElement();
+            if (block != currentBlock)
+                return it.next();
+        }
 
         RenderObject *renderer = it.current().node()->renderer();
         if (!renderer)
@@ -660,7 +666,7 @@ Position Position::equivalentUpstreamPosition() const
     return it.current();
 }
 
-Position Position::equivalentDownstreamPosition() const
+Position Position::downstream(bool stayInBlock) const
 {
     if (!node())
         return Position();
@@ -669,9 +675,11 @@ Position Position::equivalentDownstreamPosition() const
     
     PositionIterator it(*this);            
     for (; !it.atEnd(); it.next()) {   
-        NodeImpl *currentBlock = it.current().node()->enclosingBlockFlowElement();
-        if (block != currentBlock)
-            return it.previous();
+        if (stayInBlock) {
+            NodeImpl *currentBlock = it.current().node()->enclosingBlockFlowElement();
+            if (block != currentBlock)
+                return it.previous();
+        }
 
         RenderObject *renderer = it.current().node()->renderer();
         if (!renderer)
@@ -871,7 +879,7 @@ bool Position::inRenderedContent() const
 
 bool Position::inRenderedText() const
 {
-    if (!node()->isTextNode())
+    if (isEmpty() || !node()->isTextNode())
         return false;
         
     RenderObject *renderer = node()->renderer();
@@ -893,6 +901,30 @@ bool Position::inRenderedText() const
     return false;
 }
 
+bool Position::isRenderedCharacter() const
+{
+    if (isEmpty() || !node()->isTextNode())
+        return false;
+        
+    RenderObject *renderer = node()->renderer();
+    if (!renderer)
+        return false;
+    
+    RenderText *textRenderer = static_cast<RenderText *>(renderer);
+    for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+        if (offset() < box->m_start) {
+            // The offset we're looking for is before this node
+            // this means the offset must be in content that is
+            // not rendered. Return false.
+            return false;
+        }
+        if (offset() >= box->m_start && offset() < box->m_start + box->m_len)
+            return true;
+    }
+    
+    return false;
+}
+
 bool Position::rendersOnSameLine(const Position &pos) const
 {
     if (isEmpty() || pos.isEmpty())
@@ -1149,10 +1181,61 @@ bool Position::inLastEditableInContainingEditableBlock() const
     return true;
 }
 
+static inline bool isWS(const QChar &c)
+{
+    return c.isSpace() && c != QChar(0xa0);
+}
+
+Position Position::leadingWhitespacePosition() const
+{
+    if (isEmpty())
+        return Position();
+    
+    if (upstream(StayInBlock).node()->id() == ID_BR)
+        return Position();
+    
+    Position prev = previousCharacterPosition();
+    if (prev != *this && prev.node()->inSameContainingBlockFlowElement(node()) && prev.node()->isTextNode()) {
+        DOMString string = static_cast<TextImpl *>(prev.node())->data();
+        if (isWS(string[prev.offset()]))
+            return prev;
+    }
+
+    return Position();
+}
+
+Position Position::trailingWhitespacePosition() const
+{
+    if (isEmpty())
+        return Position();
+
+    if (node()->isTextNode()) {
+        TextImpl *textNode = static_cast<TextImpl *>(node());
+        if (offset() < (long)textNode->length()) {
+            DOMString string = static_cast<TextImpl *>(node())->data();
+            if (isWS(string[offset()]))
+                return *this;
+            return Position();
+        }
+    }
+
+    if (downstream(StayInBlock).node()->id() == ID_BR)
+        return Position();
+
+    Position next = nextCharacterPosition();
+    if (next != *this && next.node()->inSameContainingBlockFlowElement(node()) && next.node()->isTextNode()) {
+        DOMString string = static_cast<TextImpl *>(next.node())->data();
+        if (isWS(string[0]))
+            return next;
+    }
+
+    return Position();
+}
+
 void Position::debugPosition(const char *msg) const
 {
     if (isEmpty())
-        fprintf(stderr, "Position [%s]: empty\n");
+        fprintf(stderr, "Position [%s]: empty\n", msg);
     else
         fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, getTagName(node()->id()).string().latin1(), node(), offset());
 }
index 32673fdd771e6b78e0190067be1f30acc9590477..92c5e49803ec0353631606d92608833ce2b7870d 100644 (file)
@@ -38,6 +38,8 @@ class NodeImpl;
 // NSSelectionAffinityDownstream = 1
 enum EAffinity { UPSTREAM = 0, DOWNSTREAM = 1 };
 
+static const bool StayInBlock = true;
+
 class Position
 {
 public:
@@ -73,8 +75,11 @@ public:
     Position previousWordBoundary() const;
     Position nextWordBoundary() const;
 
-    Position equivalentUpstreamPosition() const;
-    Position equivalentDownstreamPosition() const;
+    Position leadingWhitespacePosition() const;
+    Position trailingWhitespacePosition() const;
+
+    Position upstream(bool stayInBlock=false) const;
+    Position downstream(bool stayInBlock=false) const;
     Position equivalentRangeCompliantPosition() const;
     Position equivalentShallowPosition() const;
     Position equivalentDeepPosition() const;
@@ -83,6 +88,7 @@ public:
     bool atStartOfRootEditableElement() const;
     bool inRenderedContent() const;
     bool inRenderedText() const;
+    bool isRenderedCharacter() const;
     bool rendersOnSameLine(const Position &pos) const;
     bool rendersInDifferentPosition(const Position &pos) const;
     bool isFirstRenderedPositionOnLine() const;
index 47d135a495f498c6c2cc5c3032295eb2a3d1a6f3..c2f144f7ff55408256c2e262175da30aaf4cf83b 100644 (file)
@@ -474,7 +474,7 @@ Range Selection::toRange() const
         // If the selection is a caret, move the range start upstream. This helps us match
         // the conventions of text editors tested, which make style determinations based
         // on the character before the caret, if any. 
-        s = start().equivalentUpstreamPosition().equivalentRangeCompliantPosition();
+        s = start().upstream().equivalentRangeCompliantPosition();
         e = s;
     }
     else {
@@ -490,8 +490,8 @@ Range Selection::toRange() const
         //                       ^ selected
         //
         ASSERT(state() == RANGE);
-        s = start().equivalentDownstreamPosition();
-        e = end().equivalentUpstreamPosition();
+        s = start().downstream();
+        e = end().upstream();
         if ((s.node() == e.node() && s.offset() > e.offset()) || !nodeIsBeforeNode(s.node(), e.node())) {
             // Make sure the start is before the end.
             // The end can wind up before the start if collapsed whitespace is the only thing selected.
@@ -719,7 +719,7 @@ void Selection::validate(ETextGranularity granularity)
     if (start().isEmpty() && end().isEmpty()) {
         m_state = NONE;
     }
-    else if (start() == end() || start().equivalentUpstreamPosition() == end().equivalentUpstreamPosition()) {
+    else if (start() == end() || start().upstream() == end().upstream()) {
         m_state = CARET;
     }
     else {
@@ -730,8 +730,8 @@ void Selection::validate(ETextGranularity granularity)
         // purposes of comparing selections). This is an ideal point of the code
         // to do this operation, since all selection changes that result in a RANGE 
         // come through here before anyone uses it.
-        assignStart(start().equivalentDownstreamPosition());
-        assignEnd(end().equivalentUpstreamPosition());
+        assignStart(start().downstream());
+        assignEnd(end().upstream());
     }
 
     m_needsCaretLayout = true;
@@ -1009,23 +1009,23 @@ void Selection::debugPosition() const
 
     if (start() == end()) {
         Position pos = start();
-        Position upstream = pos.equivalentUpstreamPosition();
-        Position downstream = pos.equivalentDownstreamPosition();
+        Position upstream = pos.upstream();
+        Position downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
     }
     else {
         Position pos = start();
-        Position upstream = pos.equivalentUpstreamPosition();
-        Position downstream = pos.equivalentDownstreamPosition();
+        Position upstream = pos.upstream();
+        Position downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
         fprintf(stderr, "-----------------------------------\n");
         pos = end();
-        upstream = pos.equivalentUpstreamPosition();
-        downstream = pos.equivalentDownstreamPosition();
+        upstream = pos.upstream();
+        downstream = pos.downstream();
         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
         fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
index 26547debc918b0526688036c4996440aa7eed8ef..5c46640f7c286ebcbd0bc28a62a03eb600fc5774 100644 (file)
@@ -354,7 +354,7 @@ static void writeSelection(QTextStream &ts, const RenderObject *o)
     DocumentImpl *doc = dynamic_cast<DocumentImpl *>(o->element());
     if (!doc || !doc->part())
         return;
-        
+    
     Selection selection = doc->part()->selection();
     if (selection.state() == Selection::NONE)
         return;
@@ -371,8 +371,8 @@ static void writeSelection(QTextStream &ts, const RenderObject *o)
     NodeImpl *rootNode = doc->getElementById("root");
     
     if (selection.state() == Selection::CARET) {
-        Position upstream = startPosition.equivalentUpstreamPosition();
-        Position downstream = startPosition.equivalentDownstreamPosition();
+        Position upstream = startPosition.upstream(DOM::StayInBlock);
+        Position downstream = startPosition.downstream(DOM::StayInBlock);
         QString positionString = nodePositionRelativeToRoot(startPosition.node(), rootNode);
         QString upstreamString = nodePositionRelativeToRoot(upstream.node(), rootNode);
         QString downstreamString = nodePositionRelativeToRoot(downstream.node(), rootNode);
@@ -383,14 +383,14 @@ static void writeSelection(QTextStream &ts, const RenderObject *o)
     }
     else if (selection.state() == Selection::RANGE) {
         QString startString = nodePositionRelativeToRoot(startPosition.node(), rootNode);
-        Position upstreamStart = startPosition.equivalentUpstreamPosition();
+        Position upstreamStart = startPosition.upstream(DOM::StayInBlock);
         QString upstreamStartString = nodePositionRelativeToRoot(upstreamStart.node(), rootNode);
-        Position downstreamStart = startPosition.equivalentDownstreamPosition();
+        Position downstreamStart = startPosition.downstream(DOM::StayInBlock);
         QString downstreamStartString = nodePositionRelativeToRoot(downstreamStart.node(), rootNode);
         QString endString = nodePositionRelativeToRoot(endPosition.node(), rootNode);
-        Position upstreamEnd = endPosition.equivalentUpstreamPosition();
+        Position upstreamEnd = endPosition.upstream(DOM::StayInBlock);
         QString upstreamEndString = nodePositionRelativeToRoot(upstreamEnd.node(), rootNode);
-        Position downstreamEnd = endPosition.equivalentDownstreamPosition();
+        Position downstreamEnd = endPosition.downstream(DOM::StayInBlock);
         QString downstreamEndString = nodePositionRelativeToRoot(downstreamEnd.node(), rootNode);
         ts << "selection is RANGE:\n" <<
             "start:      position " << startPosition.offset() << " of " << startString << "\n" <<