[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:31:42 UTC 2009
The following commit has been merged in the debian/unstable branch:
commit 616320e91ea7c4207e66faf1fe72697d6f44930c
Author: kocienda <kocienda at 268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date: Thu Apr 1 00:34:07 2004 +0000
Reviewed by Dave.
Many, many editing improvements, with a concentration on getting
caret navigation and deleting selections working correctly.
* WebCore.pbproj/project.pbxproj:
* khtml/dom/dom_position.cpp: Removed.
* khtml/dom/dom_position.h: Removed.
* khtml/editing/htmlediting.cpp:
(EditCommand::isNull): Inlined.
(EditCommand::notNull): New function.
(EditCommand::parent): Commands now have parents. Allows for walking the tree of composite commands.
(EditCommand::setParent): Ditto.
(EditCommand::emptyCommand): Returns a static empty command.
(AppendNodeCommand::AppendNodeCommand):
(AppendNodeCommand::parentNode): Member variable name change only. parent -> parentNode.
(DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand): New command.
(InputTextCommand::InputTextCommand):
(InputTextCommand::input):
(InputTextCommand::charactersAdded):
(JoinTextNodesCommand::JoinTextNodesCommand): Now derives directly from EditCommand.
(RemoveNodeAndPruneCommand::RemoveNodeAndPruneCommand): New command.
(SplitTextNodeCommand::SplitTextNodeCommand): Now derives directly from EditCommand.
* khtml/editing/htmlediting.h:
* khtml/editing/htmlediting_impl.cpp:
(isNBSP): New helper.
(isWS): New helper.
(shouldPruneNode): New helper.
(leadingWhitespacePosition): New helper.
(trailingWhitespacePosition): New helper.
(textNodesAreJoinable): New helper.
(nonBreakingSpaceString): Returns a static DOMString containing a non-breaking space.
(EditCommandImpl::EditCommandImpl):
(EditCommandImpl::setStartingSelection): Now recursively sets starting selection on parents.
(EditCommandImpl::setEndingSelection): As above, for ending selection.
(EditCommandImpl::parent): New accessor.
(EditCommandImpl::setParent): New accessor.
(CompositeEditCommandImpl::doUnapply): Removed some logging.
(CompositeEditCommandImpl::doReapply): Removed some logging.
(CompositeEditCommandImpl::applyCommandToComposite): Sets parent.
(CompositeEditCommandImpl::removeNodeAndPrune): New comvenience.
(CompositeEditCommandImpl::replaceText): New comvenience.
(CompositeEditCommandImpl::deleteSelection): New comvenience.
(CompositeEditCommandImpl::deleteCollapsibleWhitespace): New comvenience.
(AppendNodeCommandImpl::AppendNodeCommandImpl): Member variable name change only. parent -> parentNode.
(AppendNodeCommandImpl::~AppendNodeCommandImpl): Ditto.
(AppendNodeCommandImpl::doApply): Ditto.
(AppendNodeCommandImpl::doUnapply): Ditto.
(DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl): New command
(debugPosition): New debugging aid.
(DeleteSelectionCommandImpl::doApply): Major reworking to handle more cases correctly.
(InputNewlineCommandImpl::doApply): Position and selection tweaks.
(InputTextCommandImpl::InputTextCommandImpl): Handles more cases now, like typing after an image.
(JoinTextNodesCommandImpl::JoinTextNodesCommandImpl): Now derives directly from EditCommand.
Implements the guts of the command itself now, rather than replying on its former base class.
(RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl): New command.
(SplitTextNodeCommandImpl::SplitTextNodeCommandImpl): Now derives directly from EditCommand.
Implements the guts of the command itself now, rather than replying on its former base class.
(TypingCommandImpl::TypingCommandImpl): Major rework to handle more cases correctly.
* khtml/editing/htmlediting_impl.h:
* khtml/html/html_elementimpl.cpp:
(HTMLElementImpl::isContentEditable): More efficient use of the style system to answer the question.
* khtml/khtml_part.cpp:
(KHTMLPart::setSelection): Fixes an issue where the caret would not repaint after being moved when undoing.
(KHTMLPart::takeSelectionFrom): Ditto.
(KHTMLPart::clearSelection): Ditto.
(KHTMLPart::invalidateSelection): Ditto.
(KHTMLPart::setSelectionVisible): Ditto.
(KHTMLPart::slotClearSelection): Ditto.
(KHTMLPart::clearCaretRectIfNeeded): Ditto.
(KHTMLPart::notifySelectionChanged): Ditto.
(KHTMLPart::unappliedEditing): Now uses EditCommand::emptyCommand().
(KHTMLPart::reappliedEditing): Ditto.
* khtml/khtml_part.h:
* khtml/khtml_selection.cpp:
(KHTMLSelection::KHTMLSelection):
(KHTMLSelection::modify): Updated to work with new DOMPosition API.
(KHTMLSelection::paintCaret): Ditto.
(KHTMLSelection::moveToRenderedContent): Ditto.
(KHTMLSelection::basePosition): New convenience.
(KHTMLSelection::extentPosition): New convenience.
(KHTMLSelection::startPosition): New convenience.
(KHTMLSelection::endPosition): New convenience.
(KHTMLSelection::debugPosition): Modified debug output.
* khtml/khtml_selection.h:
(KHTMLSelection::isEmpty):
(KHTMLSelection::notEmpty):
* khtml/rendering/bidi.cpp:
(khtml::RenderBlock::layoutInlineChildren): Fixed a crasher that happened when deleting content at the start of a line.
* khtml/rendering/render_br.cpp:
(RenderBR::caretMaxRenderedOffset): New function.
(RenderBR::caretPos): Now draws the caret in the right place when a block is empty.
* khtml/rendering/render_br.h:
* khtml/rendering/render_flow.cpp:
(RenderFlow::caretPos): Now draws the caret in the right place when a flow is empty.
* khtml/rendering/render_line.cpp:
(InlineBox::caretMaxRenderedOffset): New function.
* khtml/rendering/render_line.h:
* khtml/rendering/render_object.cpp:
(RenderObject::isEditable):
(RenderObject::caretMaxRenderedOffset): New function.
* khtml/rendering/render_object.h:
* khtml/rendering/render_replaced.cpp:
(RenderReplaced::caretMaxRenderedOffset): New function.
* khtml/rendering/render_replaced.h:
* khtml/rendering/render_text.cpp:
(InlineTextBox::caretMaxRenderedOffset): New function.
(RenderText::detach):
(RenderText::caretMaxOffset):
(RenderText::caretMaxRenderedOffset): New function.
* khtml/rendering/render_text.h:
(khtml::InlineTextBox::len):
* khtml/xml/dom_edititerator.cpp: Added.
* khtml/xml/dom_edititerator.h: Added.
* khtml/xml/dom_nodeimpl.cpp:
(NodeImpl::previousEditable): Improved the correctness of this function.
(NodeImpl::nextEditable): Ditto.
* khtml/xml/dom_nodeimpl.h:
* khtml/xml/dom_position.cpp: Added.
(DOMPosition::renderedOffset): New function.
(DOMPosition::previousCharacterPosition): New function.
(DOMPosition::nextCharacterPosition): New function.
(DOMPosition::equivalentUpstreamPosition): New function.
(DOMPosition::equivalentDownstreamPosition): New function.
(DOMPosition::validUpstreamDownstreamPosition): New function.
(DOMPosition::inRenderedContent): New function.
(inlineBoxForRenderer): New function.
(renderersOnDifferentLine): New function.
(nextRenderedEditable): New function.
(previousRenderedEditable): New function.
(DOMPosition::inRenderedText): New function.
(DOMPosition::rendersOnSameLine): New function.
(DOMPosition::rendersInDifferentPosition): New function.
(DOMPosition::isFirstRenderedPositionOnLine): New function.
(DOMPosition::isLastRenderedPositionOnLine): New function.
(DOMPosition::isLastRenderedPositionInEditableBlock): New function.
(DOMPosition::inFirstEditableInRootEditableBlock): New function.
(DOMPosition::inLastEditableInRootEditableBlock): New function.
(DOMPosition::inFirstEditableInContainingEditableBlock): New function.
(DOMPosition::inLastEditableInContainingEditableBlock): New function.
* khtml/xml/dom_position.h: Added.
(DOM::DOMPosition::notEmpty): New function.
* khtml/xml/dom_stringimpl.cpp:
(DOM::DOMStringImpl::containsOnlyWhitespace): Added a version which takes an offset and length.
* khtml/xml/dom_stringimpl.h:
* khtml/xml/dom_textimpl.cpp:
(CharacterDataImpl::containsOnlyWhitespace): As above
(CharacterDataImpl::maxOffset): New function.
(CharacterDataImpl::caretMaxRenderedOffset): New function.
(TextImpl::TextImpl):
(TextImpl::rendererIsNeeded): A new bit. When set, makes a text renderer unconditionally.
* khtml/xml/dom_textimpl.h:
(DOM::TextImpl::setRendererIsNeeded): Sets the bit.
* kwq/KWQAssertions.m:
(KWQLog): Change to decrease the amount of output for the Editing log level. (my preference)
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@6289 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/WebCore/ChangeLog-2005-08-23 b/WebCore/ChangeLog-2005-08-23
index 2a663d4..ad2fe87 100644
--- a/WebCore/ChangeLog-2005-08-23
+++ b/WebCore/ChangeLog-2005-08-23
@@ -1,3 +1,161 @@
+2004-03-31 Ken Kocienda <kocienda at apple.com>
+
+ Reviewed by Dave.
+
+ Many, many editing improvements, with a concentration on getting
+ caret navigation and deleting selections working correctly.
+
+ * WebCore.pbproj/project.pbxproj:
+ * khtml/dom/dom_position.cpp: Removed.
+ * khtml/dom/dom_position.h: Removed.
+ * khtml/editing/htmlediting.cpp:
+ (EditCommand::isNull): Inlined.
+ (EditCommand::notNull): New function.
+ (EditCommand::parent): Commands now have parents. Allows for walking the tree of composite commands.
+ (EditCommand::setParent): Ditto.
+ (EditCommand::emptyCommand): Returns a static empty command.
+ (AppendNodeCommand::AppendNodeCommand):
+ (AppendNodeCommand::parentNode): Member variable name change only. parent -> parentNode.
+ (DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand): New command.
+ (InputTextCommand::InputTextCommand):
+ (InputTextCommand::input):
+ (InputTextCommand::charactersAdded):
+ (JoinTextNodesCommand::JoinTextNodesCommand): Now derives directly from EditCommand.
+ (RemoveNodeAndPruneCommand::RemoveNodeAndPruneCommand): New command.
+ (SplitTextNodeCommand::SplitTextNodeCommand): Now derives directly from EditCommand.
+ * khtml/editing/htmlediting.h:
+ * khtml/editing/htmlediting_impl.cpp:
+ (isNBSP): New helper.
+ (isWS): New helper.
+ (shouldPruneNode): New helper.
+ (leadingWhitespacePosition): New helper.
+ (trailingWhitespacePosition): New helper.
+ (textNodesAreJoinable): New helper.
+ (nonBreakingSpaceString): Returns a static DOMString containing a non-breaking space.
+ (EditCommandImpl::EditCommandImpl):
+ (EditCommandImpl::setStartingSelection): Now recursively sets starting selection on parents.
+ (EditCommandImpl::setEndingSelection): As above, for ending selection.
+ (EditCommandImpl::parent): New accessor.
+ (EditCommandImpl::setParent): New accessor.
+ (CompositeEditCommandImpl::doUnapply): Removed some logging.
+ (CompositeEditCommandImpl::doReapply): Removed some logging.
+ (CompositeEditCommandImpl::applyCommandToComposite): Sets parent.
+ (CompositeEditCommandImpl::removeNodeAndPrune): New comvenience.
+ (CompositeEditCommandImpl::replaceText): New comvenience.
+ (CompositeEditCommandImpl::deleteSelection): New comvenience.
+ (CompositeEditCommandImpl::deleteCollapsibleWhitespace): New comvenience.
+ (AppendNodeCommandImpl::AppendNodeCommandImpl): Member variable name change only. parent -> parentNode.
+ (AppendNodeCommandImpl::~AppendNodeCommandImpl): Ditto.
+ (AppendNodeCommandImpl::doApply): Ditto.
+ (AppendNodeCommandImpl::doUnapply): Ditto.
+ (DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl): New command
+ (debugPosition): New debugging aid.
+ (DeleteSelectionCommandImpl::doApply): Major reworking to handle more cases correctly.
+ (InputNewlineCommandImpl::doApply): Position and selection tweaks.
+ (InputTextCommandImpl::InputTextCommandImpl): Handles more cases now, like typing after an image.
+ (JoinTextNodesCommandImpl::JoinTextNodesCommandImpl): Now derives directly from EditCommand.
+ Implements the guts of the command itself now, rather than replying on its former base class.
+ (RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl): New command.
+ (SplitTextNodeCommandImpl::SplitTextNodeCommandImpl): Now derives directly from EditCommand.
+ Implements the guts of the command itself now, rather than replying on its former base class.
+ (TypingCommandImpl::TypingCommandImpl): Major rework to handle more cases correctly.
+ * khtml/editing/htmlediting_impl.h:
+ * khtml/html/html_elementimpl.cpp:
+ (HTMLElementImpl::isContentEditable): More efficient use of the style system to answer the question.
+ * khtml/khtml_part.cpp:
+ (KHTMLPart::setSelection): Fixes an issue where the caret would not repaint after being moved when undoing.
+ (KHTMLPart::takeSelectionFrom): Ditto.
+ (KHTMLPart::clearSelection): Ditto.
+ (KHTMLPart::invalidateSelection): Ditto.
+ (KHTMLPart::setSelectionVisible): Ditto.
+ (KHTMLPart::slotClearSelection): Ditto.
+ (KHTMLPart::clearCaretRectIfNeeded): Ditto.
+ (KHTMLPart::notifySelectionChanged): Ditto.
+ (KHTMLPart::unappliedEditing): Now uses EditCommand::emptyCommand().
+ (KHTMLPart::reappliedEditing): Ditto.
+ * khtml/khtml_part.h:
+ * khtml/khtml_selection.cpp:
+ (KHTMLSelection::KHTMLSelection):
+ (KHTMLSelection::modify): Updated to work with new DOMPosition API.
+ (KHTMLSelection::paintCaret): Ditto.
+ (KHTMLSelection::moveToRenderedContent): Ditto.
+ (KHTMLSelection::basePosition): New convenience.
+ (KHTMLSelection::extentPosition): New convenience.
+ (KHTMLSelection::startPosition): New convenience.
+ (KHTMLSelection::endPosition): New convenience.
+ (KHTMLSelection::debugPosition): Modified debug output.
+ * khtml/khtml_selection.h:
+ (KHTMLSelection::isEmpty):
+ (KHTMLSelection::notEmpty):
+ * khtml/rendering/bidi.cpp:
+ (khtml::RenderBlock::layoutInlineChildren): Fixed a crasher that happened when deleting content at the start of a line.
+ * khtml/rendering/render_br.cpp:
+ (RenderBR::caretMaxRenderedOffset): New function.
+ (RenderBR::caretPos): Now draws the caret in the right place when a block is empty.
+ * khtml/rendering/render_br.h:
+ * khtml/rendering/render_flow.cpp:
+ (RenderFlow::caretPos): Now draws the caret in the right place when a flow is empty.
+ * khtml/rendering/render_line.cpp:
+ (InlineBox::caretMaxRenderedOffset): New function.
+ * khtml/rendering/render_line.h:
+ * khtml/rendering/render_object.cpp:
+ (RenderObject::isEditable):
+ (RenderObject::caretMaxRenderedOffset): New function.
+ * khtml/rendering/render_object.h:
+ * khtml/rendering/render_replaced.cpp:
+ (RenderReplaced::caretMaxRenderedOffset): New function.
+ * khtml/rendering/render_replaced.h:
+ * khtml/rendering/render_text.cpp:
+ (InlineTextBox::caretMaxRenderedOffset): New function.
+ (RenderText::detach):
+ (RenderText::caretMaxOffset):
+ (RenderText::caretMaxRenderedOffset): New function.
+ * khtml/rendering/render_text.h:
+ (khtml::InlineTextBox::len):
+ * khtml/xml/dom_edititerator.cpp: Added.
+ * khtml/xml/dom_edititerator.h: Added.
+ * khtml/xml/dom_nodeimpl.cpp:
+ (NodeImpl::previousEditable): Improved the correctness of this function.
+ (NodeImpl::nextEditable): Ditto.
+ * khtml/xml/dom_nodeimpl.h:
+ * khtml/xml/dom_position.cpp: Added.
+ (DOMPosition::renderedOffset): New function.
+ (DOMPosition::previousCharacterPosition): New function.
+ (DOMPosition::nextCharacterPosition): New function.
+ (DOMPosition::equivalentUpstreamPosition): New function.
+ (DOMPosition::equivalentDownstreamPosition): New function.
+ (DOMPosition::validUpstreamDownstreamPosition): New function.
+ (DOMPosition::inRenderedContent): New function.
+ (inlineBoxForRenderer): New function.
+ (renderersOnDifferentLine): New function.
+ (nextRenderedEditable): New function.
+ (previousRenderedEditable): New function.
+ (DOMPosition::inRenderedText): New function.
+ (DOMPosition::rendersOnSameLine): New function.
+ (DOMPosition::rendersInDifferentPosition): New function.
+ (DOMPosition::isFirstRenderedPositionOnLine): New function.
+ (DOMPosition::isLastRenderedPositionOnLine): New function.
+ (DOMPosition::isLastRenderedPositionInEditableBlock): New function.
+ (DOMPosition::inFirstEditableInRootEditableBlock): New function.
+ (DOMPosition::inLastEditableInRootEditableBlock): New function.
+ (DOMPosition::inFirstEditableInContainingEditableBlock): New function.
+ (DOMPosition::inLastEditableInContainingEditableBlock): New function.
+ * khtml/xml/dom_position.h: Added.
+ (DOM::DOMPosition::notEmpty): New function.
+ * khtml/xml/dom_stringimpl.cpp:
+ (DOM::DOMStringImpl::containsOnlyWhitespace): Added a version which takes an offset and length.
+ * khtml/xml/dom_stringimpl.h:
+ * khtml/xml/dom_textimpl.cpp:
+ (CharacterDataImpl::containsOnlyWhitespace): As above
+ (CharacterDataImpl::maxOffset): New function.
+ (CharacterDataImpl::caretMaxRenderedOffset): New function.
+ (TextImpl::TextImpl):
+ (TextImpl::rendererIsNeeded): A new bit. When set, makes a text renderer unconditionally.
+ * khtml/xml/dom_textimpl.h:
+ (DOM::TextImpl::setRendererIsNeeded): Sets the bit.
+ * kwq/KWQAssertions.m:
+ (KWQLog): Change to decrease the amount of output for the Editing log level. (my preference)
+
2004-03-31 David Hyatt <hyatt at apple.com>
Fix for 3601834, make sure that textareas do a layout when their rows/cols/wrap attributes are dynamically
diff --git a/WebCore/ForwardingHeaders/xml/dom_edititerator.h b/WebCore/ForwardingHeaders/xml/dom_edititerator.h
new file mode 100644
index 0000000..3acedfc
--- /dev/null
+++ b/WebCore/ForwardingHeaders/xml/dom_edititerator.h
@@ -0,0 +1 @@
+#include <dom_edititerator.h>
diff --git a/WebCore/WebCore.pbproj/project.pbxproj b/WebCore/WebCore.pbproj/project.pbxproj
index 82e80bb..b94a0d5 100644
--- a/WebCore/WebCore.pbproj/project.pbxproj
+++ b/WebCore/WebCore.pbproj/project.pbxproj
@@ -510,7 +510,6 @@
BCBDB03A0597B36E00B83B92,
BCBDB096059A28B100B83B92,
BEB1DD3205C1980700DD1F43,
- BEB1DD3E05C1982000DD1F43,
BC7FDE3405C1D9AB0070A902,
BC7E782205C5EB700088A50F,
BC3B364905C9D5E200E42902,
@@ -531,6 +530,8 @@
9321275B0606724900B62302,
9321275C0606724900B62302,
9321275D0606724900B62302,
+ BE91FC8D06133666005E3790,
+ BE91FC9206133697005E3790,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@@ -798,7 +799,6 @@
BCBDB0390597B36E00B83B92,
BCBDB095059A28B100B83B92,
BEB1DD3105C1980700DD1F43,
- BEB1DD3D05C1982000DD1F43,
BC7FDE3305C1D9AB0070A902,
BC3B364805C9D5E200E42902,
BC433AD005D3046F003A5A14,
@@ -810,6 +810,8 @@
BE9CB65A05F9546800514D9C,
BEF7EEA305FF8F0D009717EE,
845563E606014A9800609194,
+ BE91FC8E06133666005E3790,
+ BE91FC9106133697005E3790,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@@ -2741,6 +2743,62 @@
settings = {
};
};
+ BE91FC8B06133666005E3790 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = dom_position.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ BE91FC8C06133666005E3790 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.cpp.cpp;
+ path = dom_position.cpp;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ BE91FC8D06133666005E3790 = {
+ fileRef = BE91FC8B06133666005E3790;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ BE91FC8E06133666005E3790 = {
+ fileRef = BE91FC8C06133666005E3790;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ BE91FC8F06133697005E3790 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.cpp.cpp;
+ path = dom_edititerator.cpp;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ BE91FC9006133697005E3790 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = dom_edititerator.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ BE91FC9106133697005E3790 = {
+ fileRef = BE91FC8F06133697005E3790;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ BE91FC9206133697005E3790 = {
+ fileRef = BE91FC9006133697005E3790;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
BE94EB6305EFFE6B0032DCB5 = {
fileEncoding = 30;
isa = PBXFileReference;
@@ -2896,34 +2954,6 @@
settings = {
};
};
- BEB1DD3B05C1982000DD1F43 = {
- fileEncoding = 30;
- isa = PBXFileReference;
- lastKnownFileType = sourcecode.cpp.cpp;
- path = dom_position.cpp;
- refType = 4;
- sourceTree = "<group>";
- };
- BEB1DD3C05C1982000DD1F43 = {
- fileEncoding = 30;
- isa = PBXFileReference;
- lastKnownFileType = sourcecode.c.h;
- path = dom_position.h;
- refType = 4;
- sourceTree = "<group>";
- };
- BEB1DD3D05C1982000DD1F43 = {
- fileRef = BEB1DD3B05C1982000DD1F43;
- isa = PBXBuildFile;
- settings = {
- };
- };
- BEB1DD3E05C1982000DD1F43 = {
- fileRef = BEB1DD3C05C1982000DD1F43;
- isa = PBXBuildFile;
- settings = {
- };
- };
BEF7EEA005FF8F0D009717EE = {
fileEncoding = 30;
isa = PBXFileReference;
@@ -4222,8 +4252,6 @@
F523D19302DE4322018635CA,
F523D19402DE4322018635CA,
F523D19502DE4322018635CA,
- BEB1DD3B05C1982000DD1F43,
- BEB1DD3C05C1982000DD1F43,
F523D19602DE4322018635CA,
F523D19702DE4322018635CA,
F523D19802DE4322018635CA,
@@ -6293,11 +6321,15 @@
BC3B364705C9D5E200E42902,
F523D2F402DE4476018635CA,
F523D2F502DE4476018635CA,
+ BE91FC8F06133697005E3790,
+ BE91FC9006133697005E3790,
F523D2F702DE4476018635CA,
F523D2F802DE4476018635CA,
BC7E782005C5EB700088A50F,
F523D2F902DE4476018635CA,
F523D2FA02DE4476018635CA,
+ BE91FC8B06133666005E3790,
+ BE91FC8C06133666005E3790,
F523D2FB02DE4476018635CA,
F523D2FC02DE4476018635CA,
F523D2FD02DE4476018635CA,
diff --git a/WebCore/khtml/dom/dom_position.cpp b/WebCore/khtml/dom/dom_position.cpp
deleted file mode 100644
index b6b5e32..0000000
--- a/WebCore/khtml/dom/dom_position.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2003 Apple Computer, Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "dom_position.h"
-#include "xml/dom_nodeimpl.h"
-
-using DOM::DOMPosition;
-
-DOMPosition::DOMPosition(NodeImpl *node, long offset)
- : m_node(0), m_offset(offset)
-{
- if (node) {
- m_node = node;
- m_node->ref();
- }
-};
-
-DOMPosition::DOMPosition(const DOMPosition &o)
- : m_node(0), m_offset(o.offset())
-{
- if (o.node()) {
- m_node = o.node();
- m_node->ref();
- }
-}
-
-DOMPosition::~DOMPosition() {
- if (m_node) {
- m_node->deref();
- }
-}
-
-DOMPosition &DOMPosition::operator=(const DOMPosition &o)
-{
- if (m_node) {
- m_node->deref();
- }
- m_node = o.node();
- if (m_node) {
- m_node->ref();
- }
-
- m_offset = o.offset();
-
- return *this;
-}
diff --git a/WebCore/khtml/editing/SelectionController.cpp b/WebCore/khtml/editing/SelectionController.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/editing/SelectionController.cpp
+++ b/WebCore/khtml/editing/SelectionController.cpp
@@ -25,6 +25,7 @@
#include "khtml_selection.h"
+#include "htmltags.h"
#include "khtml_part.h"
#include "khtmlview.h"
#include "qevent.h"
@@ -38,6 +39,7 @@
#include "rendering/render_style.h"
#include "rendering/render_text.h"
#include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
#include "xml/dom_elementimpl.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
using DOM::DocumentImpl;
using DOM::DOMPosition;
using DOM::DOMString;
+using DOM::EditIterator;
using DOM::ElementImpl;
using DOM::Node;
using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
validate();
}
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
{
init();
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
validate();
}
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+ init();
+
+ setBaseNode(base.node());
+ setExtentNode(extent.node());
+ setBaseOffset(base.offset());
+ setExtentOffset(extent.offset());
+
+ validate();
+}
+
KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
{
init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(endNode());
setExtentOffset(endOffset());
}
- pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().nextCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(endNode(), endOffset());
+ pos = endPosition();
else
- pos = nextCharacterPosition();
+ pos = endPosition().nextCharacterPosition();
}
break;
case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(startNode());
setExtentOffset(startOffset());
}
- pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().previousCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(startNode(), startOffset());
+ pos = startPosition();
else
- pos = previousCharacterPosition();
+ pos = startPosition().previousCharacterPosition();
}
break;
case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
m_needsCaretLayout = flag;
}
-bool KHTMLSelection::isEmpty() const
-{
- return m_baseNode == 0 && m_extentNode == 0;
-}
-
Range KHTMLSelection::toRange() const
{
if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
if (m_needsCaretLayout) {
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (!inRenderedContent(pos)) {
+ if (!pos.inRenderedContent()) {
moveToRenderedContent();
}
layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
#endif
}
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
- return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return from;
-
- NodeImpl *node = from.node();
- long offset = from.offset() - 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->nextEditable() && !renderer->precedesLineBreak())
- offset--;
- assert(offset >= 0);
- return DOMPosition(renderer->element(), offset);
- }
- renderer = renderer->previousEditable();
- }
-
- // can't move the position
- return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
- return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return DOMPosition();
-
- NodeImpl *node = from.node();
- long offset = from.offset() + 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, 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 {
- // 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);
- }
-
- //
- // Look in next renderer(s)
- //
- renderer = renderer->nextEditable();
- while (renderer) {
- // 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);
- }
- else {
- return DOMPosition(renderer->element(), renderer->caretMinOffset());
- }
- renderer = renderer->nextEditable();
- }
-
- // can't move the position
- return from;
-}
-
bool KHTMLSelection::moveToRenderedContent()
{
if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
return false;
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (inRenderedContent(pos))
+ if (pos.inRenderedContent())
return true;
- // not currently rendered, try moving to next
- DOMPosition next = nextCharacterPosition(pos);
- if (next != pos) {
- moveTo(next);
+ // not currently rendered, try moving to prev
+ DOMPosition prev = pos.previousCharacterPosition();
+ if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(prev);
return true;
}
- // could not be moved to next, try prev
- DOMPosition prev = previousCharacterPosition(pos);
- if (prev != pos) {
- moveTo(prev);
+ // could not be moved to prev, try next
+ DOMPosition next = pos.nextCharacterPosition();
+ if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(next);
return true;
}
return false;
}
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
{
- if (pos.isEmpty())
- return false;
-
- long offset = pos.offset();
+ return DOMPosition(baseNode(), baseOffset());
+}
- RenderObject *renderer = pos.node()->renderer();
- if (!renderer)
- return false;
-
- if (renderer->isText() && !renderer->isBR()) {
- RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
- for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
- if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
- return true;
- }
- else 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;
- }
- }
- }
- else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
- return true;
- }
-
- return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+ return DOMPosition(extentNode(), extentOffset());
}
-
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+ return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+ return DOMPosition(endNode(), endOffset());
+}
+
bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2)
{
if (!n1 || !n2)
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
if (!startNode())
return;
- static int context = 5;
+ //static int context = 5;
- RenderObject *r = 0;
+ //RenderObject *r = 0;
fprintf(stderr, "KHTMLSelection =================\n");
-
+
+ if (startPosition() == endPosition()) {
+ DOMPosition pos = startPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 {
+ DOMPosition pos = endPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 = startPosition();
+ upstream = pos.equivalentUpstreamPosition();
+ downstream = pos.equivalentDownstreamPosition();
+ 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());
+ fprintf(stderr, "-----------------------------------\n");
+ }
+
+#if 0
int back = 0;
r = startNode()->renderer();
for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
else
break;
}
+#endif
fprintf(stderr, "================================\n");
}
diff --git a/WebCore/khtml/editing/SelectionController.h b/WebCore/khtml/editing/SelectionController.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/editing/SelectionController.h
+++ b/WebCore/khtml/editing/SelectionController.h
@@ -46,6 +46,7 @@ public:
KHTMLSelection();
KHTMLSelection(DOM::NodeImpl *node, long offset);
KHTMLSelection(const DOM::DOMPosition &);
+ KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
KHTMLSelection(const KHTMLSelection &);
~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
DOM::NodeImpl *endNode() const { return m_endNode; }
long endOffset() const { return m_endOffset; }
- DOM::DOMPosition previousCharacterPosition() const;
- static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
- DOM::DOMPosition nextCharacterPosition() const;
- static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-
+ DOM::DOMPosition basePosition() const;
+ DOM::DOMPosition extentPosition() const;
+ DOM::DOMPosition startPosition() const;
+ DOM::DOMPosition endPosition() const;
+
void setNeedsLayout(bool flag=true);
void clearModifyBias() { m_modifyBiasSet = false; }
- bool isEmpty() const;
+ bool isEmpty() const { return state() == NONE; }
+ bool notEmpty() const { return !isEmpty(); }
DOM::Range toRange() const;
@@ -124,7 +126,6 @@ private:
void setEndNode(DOM::NodeImpl *);
void setEndOffset(long);
- bool inRenderedContent(const DOM::DOMPosition &);
bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/editing/htmlediting.cpp b/WebCore/khtml/editing/htmlediting.cpp
index 6497337..5a6c182 100644
--- a/WebCore/khtml/editing/htmlediting.cpp
+++ b/WebCore/khtml/editing/htmlediting.cpp
@@ -48,8 +48,8 @@ using khtml::AppendNodeCommand;
using khtml::AppendNodeCommandImpl;
using khtml::CompositeEditCommand;
using khtml::CompositeEditCommandImpl;
-using khtml::DeleteKeyCommand;
-using khtml::DeleteKeyCommandImpl;
+using khtml::DeleteCollapsibleWhitespaceCommand;
+using khtml::DeleteCollapsibleWhitespaceCommandImpl;
using khtml::DeleteSelectionCommand;
using khtml::DeleteSelectionCommandImpl;
using khtml::DeleteTextCommand;
@@ -66,10 +66,10 @@ using khtml::InsertTextCommand;
using khtml::InsertTextCommandImpl;
using khtml::JoinTextNodesCommand;
using khtml::JoinTextNodesCommandImpl;
-using khtml::ModifyTextNodeCommand;
-using khtml::ModifyTextNodeCommandImpl;
using khtml::RemoveNodeCommand;
using khtml::RemoveNodeCommandImpl;
+using khtml::RemoveNodeAndPruneCommand;
+using khtml::RemoveNodeAndPruneCommandImpl;
using khtml::PasteHTMLCommand;
using khtml::PasteHTMLCommandImpl;
using khtml::PasteImageCommand;
@@ -88,11 +88,11 @@ using khtml::TypingCommandImpl;
#endif
#define IF_IMPL_NULL_RETURN_ARG(arg) do { \
- if (isNull()) { LOG(Editing, "impl is null"); return arg; } \
+ if (isNull()) { return arg; } \
} while (0)
#define IF_IMPL_NULL_RETURN do { \
- if (isNull()) { LOG(Editing, "impl is null"); return; } \
+ if (isNull()) { return; } \
} while (0)
//------------------------------------------------------------------------------------------
@@ -126,10 +126,14 @@ bool EditCommand::isCompositeStep() const
return get()->isCompositeStep();
}
-void EditCommand::setIsCompositeStep(bool flag)
+bool EditCommand::isNull() const
{
- IF_IMPL_NULL_RETURN;
- get()->setIsCompositeStep(flag);
+ return get() == 0;
+}
+
+bool EditCommand::notNull() const
+{
+ return !isNull();
}
void EditCommand::apply()
@@ -198,11 +202,29 @@ void EditCommand::moveToEndingSelection()
get()->moveToEndingSelection();
}
+EditCommand EditCommand::parent() const
+{
+ IF_IMPL_NULL_RETURN_ARG(0);
+ return get()->parent();
+}
+
+void EditCommand::setParent(const EditCommand &cmd)
+{
+ IF_IMPL_NULL_RETURN;
+ get()->setParent(cmd);
+}
+
EditCommandImpl *EditCommand::handle() const
{
return static_cast<EditCommandImpl *>(get());
}
+EditCommand &EditCommand::emptyCommand()
+{
+ static EditCommand m_emptyCommand;
+ return m_emptyCommand;
+}
+
//------------------------------------------------------------------------------------------
// CompositeEditCommand
@@ -228,24 +250,13 @@ CompositeEditCommandImpl *CompositeEditCommand::impl() const
return static_cast<CompositeEditCommandImpl *>(get());
}
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommand
-
-ModifyTextNodeCommand::ModifyTextNodeCommand(ModifyTextNodeCommandImpl *impl) : EditCommand(impl)
-{
-}
-
-ModifyTextNodeCommand::~ModifyTextNodeCommand()
-{
-}
-
//==========================================================================================
// Concrete commands
//------------------------------------------------------------------------------------------
// AppendNodeCommand
-AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *parent, NodeImpl *appendChild)
- : EditCommand(new AppendNodeCommandImpl(document, parent, appendChild))
+AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *parentNode, NodeImpl *appendChild)
+ : EditCommand(new AppendNodeCommandImpl(document, parentNode, appendChild))
{
}
@@ -258,10 +269,10 @@ AppendNodeCommandImpl *AppendNodeCommand::impl() const
return static_cast<AppendNodeCommandImpl *>(get());
}
-NodeImpl *AppendNodeCommand::parent() const
+NodeImpl *AppendNodeCommand::parentNode() const
{
IF_IMPL_NULL_RETURN_ARG(0);
- return impl()->parent();
+ return impl()->parentNode();
}
NodeImpl *AppendNodeCommand::appendChild() const
@@ -271,30 +282,40 @@ NodeImpl *AppendNodeCommand::appendChild() const
}
//------------------------------------------------------------------------------------------
-// DeleteKeyCommand
+// DeleteCollapsibleWhitespaceCommand
-DeleteKeyCommand::DeleteKeyCommand(DocumentImpl *document)
- : CompositeEditCommand(new DeleteKeyCommandImpl(document))
+DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand(DocumentImpl *document)
+ : CompositeEditCommand(new DeleteCollapsibleWhitespaceCommandImpl(document))
{
}
-DeleteKeyCommand::~DeleteKeyCommand()
+DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand(DocumentImpl *document, const KHTMLSelection &selection)
+ : CompositeEditCommand(new DeleteCollapsibleWhitespaceCommandImpl(document, selection))
{
}
-DeleteKeyCommandImpl *DeleteKeyCommand::impl() const
+DeleteCollapsibleWhitespaceCommand::~DeleteCollapsibleWhitespaceCommand()
+{
+}
+
+DeleteCollapsibleWhitespaceCommandImpl *DeleteCollapsibleWhitespaceCommand::impl() const
{
- return static_cast<DeleteKeyCommandImpl *>(get());
+ return static_cast<DeleteCollapsibleWhitespaceCommandImpl *>(get());
}
//------------------------------------------------------------------------------------------
// DeleteSelectionCommand
-DeleteSelectionCommand::DeleteSelectionCommand(DOM::DocumentImpl *document)
+DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document)
: CompositeEditCommand(new DeleteSelectionCommandImpl(document))
{
}
+DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const KHTMLSelection &selection)
+ : CompositeEditCommand(new DeleteSelectionCommandImpl(document, selection))
+{
+}
+
DeleteSelectionCommand::~DeleteSelectionCommand()
{
}
@@ -312,6 +333,11 @@ DeleteTextCommand::DeleteTextCommand(DocumentImpl *document, TextImpl *node, lon
{
}
+DeleteTextCommand::DeleteTextCommand(const DeleteTextCommand &o)
+ : EditCommand(o.impl())
+{
+}
+
DeleteTextCommand::~DeleteTextCommand()
{
}
@@ -359,8 +385,8 @@ InputNewlineCommandImpl *InputNewlineCommand::impl() const
//------------------------------------------------------------------------------------------
// InputTextCommand
-InputTextCommand::InputTextCommand(DocumentImpl *document, const DOMString &text)
- : CompositeEditCommand(new InputTextCommandImpl(document, text))
+InputTextCommand::InputTextCommand(DocumentImpl *document)
+ : CompositeEditCommand(new InputTextCommandImpl(document))
{
}
@@ -373,22 +399,22 @@ InputTextCommandImpl *InputTextCommand::impl() const
return static_cast<InputTextCommandImpl *>(get());
}
-DOMString InputTextCommand::text() const
-{
- IF_IMPL_NULL_RETURN_ARG(DOMString());
- return impl()->text();
-}
-
void InputTextCommand::deleteCharacter()
{
IF_IMPL_NULL_RETURN;
impl()->deleteCharacter();
}
-void InputTextCommand::coalesce(const DOM::DOMString &text)
+void InputTextCommand::input(const DOM::DOMString &text)
{
IF_IMPL_NULL_RETURN;
- impl()->coalesce(text);
+ impl()->input(text);
+}
+
+unsigned long InputTextCommand::charactersAdded() const
+{
+ IF_IMPL_NULL_RETURN_ARG(0);
+ return impl()->charactersAdded();
}
//------------------------------------------------------------------------------------------
@@ -468,7 +494,7 @@ DOMString InsertTextCommand::text() const
// JoinTextNodesCommand
JoinTextNodesCommand::JoinTextNodesCommand(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
- : ModifyTextNodeCommand(new JoinTextNodesCommandImpl(document, text1, text2))
+ : EditCommand(new JoinTextNodesCommandImpl(document, text1, text2))
{
}
@@ -557,10 +583,33 @@ NodeImpl *RemoveNodeCommand::node() const
}
//------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommand
+
+RemoveNodeAndPruneCommand::RemoveNodeAndPruneCommand(DocumentImpl *document, NodeImpl *node)
+ : CompositeEditCommand(new RemoveNodeAndPruneCommandImpl(document, node))
+{
+}
+
+RemoveNodeAndPruneCommand::~RemoveNodeAndPruneCommand()
+{
+}
+
+RemoveNodeAndPruneCommandImpl *RemoveNodeAndPruneCommand::impl() const
+{
+ return static_cast<RemoveNodeAndPruneCommandImpl *>(get());
+}
+
+NodeImpl *RemoveNodeAndPruneCommand::node() const
+{
+ IF_IMPL_NULL_RETURN_ARG(0);
+ return impl()->node();
+}
+
+//------------------------------------------------------------------------------------------
// SplitTextNodeCommand
SplitTextNodeCommand::SplitTextNodeCommand(DocumentImpl *document, TextImpl *text, long offset)
- : ModifyTextNodeCommand(new SplitTextNodeCommandImpl(document, text, offset))
+ : EditCommand(new SplitTextNodeCommandImpl(document, text, offset))
{
}
diff --git a/WebCore/khtml/editing/htmlediting.h b/WebCore/khtml/editing/htmlediting.h
index c39a07f..19cfb30 100644
--- a/WebCore/khtml/editing/htmlediting.h
+++ b/WebCore/khtml/editing/htmlediting.h
@@ -45,20 +45,20 @@ namespace khtml {
class AppendNodeCommandImpl;
class CompositeEditCommandImpl;
-class DeleteKeyCommandImpl;
+class DeleteCollapsibleWhitespaceCommandImpl;
class DeleteSelectionCommandImpl;
class DeleteTextCommandImpl;
+class EditCommand;
class EditCommandImpl;
class InputNewlineCommandImpl;
class InputTextCommandImpl;
class InsertNodeBeforeCommandImpl;
class InsertTextCommandImpl;
class JoinTextNodesCommandImpl;
-class ModifyTextNodeCommand;
-class ModifyTextNodeCommandImpl;
class PasteHTMLCommandImpl;
class PasteImageCommandImpl;
class RemoveNodeCommandImpl;
+class RemoveNodeAndPruneCommandImpl;
class SplitTextNodeCommandImpl;
class TypingCommandImpl;
@@ -69,7 +69,7 @@ enum ECommandID {
EditCommandID, // leave the base class first, others in alpha order
AppendNodeCommandID,
CompositeEditCommandID,
- DeleteKeyCommandID,
+ DeleteCollapsibleWhitespaceCommandID,
DeleteSelectionCommandID,
DeleteTextCommandID,
InputNewlineCommandID,
@@ -77,10 +77,10 @@ enum ECommandID {
InsertNodeBeforeCommandID,
InsertTextCommandID,
JoinTextNodesCommandID,
- ModifyTextNodeCommandID,
PasteHTMLCommandID,
PasteImageCommandID,
RemoveNodeCommandID,
+ RemoveNodeAndPruneCommandID,
SplitTextNodeCommandID,
TypingCommandID,
};
@@ -96,7 +96,6 @@ public:
virtual int commandID() const = 0;
virtual bool isCompositeStep() const = 0;
- virtual void setIsCompositeStep(bool flag=true) = 0;
virtual void apply() = 0;
virtual void unapply() = 0;
@@ -113,6 +112,9 @@ public:
virtual void moveToStartingSelection() = 0;
virtual void moveToEndingSelection() = 0;
+
+ virtual EditCommand parent() const = 0;
+ virtual void setParent(const EditCommand &) = 0;
};
//------------------------------------------------------------------------------------------
@@ -128,7 +130,8 @@ public:
int commandID() const;
bool isCompositeStep() const;
- void setIsCompositeStep(bool flag=true);
+ bool isNull() const;
+ bool notNull() const;
void apply();
void unapply();
@@ -146,7 +149,12 @@ public:
void moveToStartingSelection();
void moveToEndingSelection();
+ EditCommand parent() const;
+ void setParent(const EditCommand &);
+
EditCommandImpl *handle() const;
+
+ static EditCommand &emptyCommand();
};
//------------------------------------------------------------------------------------------
@@ -164,16 +172,6 @@ private:
inline CompositeEditCommandImpl *impl() const;
};
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommand
-
-class ModifyTextNodeCommand : public EditCommand
-{
-public:
- ModifyTextNodeCommand(ModifyTextNodeCommandImpl *);
- virtual ~ModifyTextNodeCommand();
-};
-
//==========================================================================================
// Concrete commands
//------------------------------------------------------------------------------------------
@@ -182,10 +180,10 @@ public:
class AppendNodeCommand : public EditCommand
{
public:
- AppendNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *parent, DOM::NodeImpl *appendChild);
+ AppendNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *parentNode, DOM::NodeImpl *appendChild);
virtual ~AppendNodeCommand();
- DOM::NodeImpl *parent() const;
+ DOM::NodeImpl *parentNode() const;
DOM::NodeImpl *appendChild() const;
private:
@@ -193,16 +191,18 @@ private:
};
//------------------------------------------------------------------------------------------
-// DeleteKeyCommand
+// DeleteCollapsibleWhitespaceCommand
-class DeleteKeyCommand : public CompositeEditCommand
+class DeleteCollapsibleWhitespaceCommand : public CompositeEditCommand
{
public:
- DeleteKeyCommand(DOM::DocumentImpl *document);
- virtual ~DeleteKeyCommand();
+ DeleteCollapsibleWhitespaceCommand(DOM::DocumentImpl *document);
+ DeleteCollapsibleWhitespaceCommand(DOM::DocumentImpl *document, const KHTMLSelection &selection);
+
+ virtual ~DeleteCollapsibleWhitespaceCommand();
private:
- inline DeleteKeyCommandImpl *impl() const;
+ inline DeleteCollapsibleWhitespaceCommandImpl *impl() const;
};
//------------------------------------------------------------------------------------------
@@ -212,6 +212,7 @@ class DeleteSelectionCommand : public CompositeEditCommand
{
public:
DeleteSelectionCommand(DOM::DocumentImpl *document);
+ DeleteSelectionCommand(DOM::DocumentImpl *document, const KHTMLSelection &selection);
virtual ~DeleteSelectionCommand();
private:
@@ -225,6 +226,7 @@ class DeleteTextCommand : public EditCommand
{
public:
DeleteTextCommand(DOM::DocumentImpl *document, DOM::TextImpl *, long offset, long count);
+ DeleteTextCommand(const DeleteTextCommand &);
virtual ~DeleteTextCommand();
DOM::TextImpl *node() const;
@@ -254,13 +256,13 @@ private:
class InputTextCommand : public CompositeEditCommand
{
public:
- InputTextCommand(DOM::DocumentImpl *document, const DOM::DOMString &text);
+ InputTextCommand(DOM::DocumentImpl *document);
virtual ~InputTextCommand();
- DOM::DOMString text() const;
-
void deleteCharacter();
- void coalesce(const DOM::DOMString &text);
+ void input(const DOM::DOMString &text);
+
+ unsigned long charactersAdded() const;
private:
inline InputTextCommandImpl *impl() const;
@@ -304,7 +306,7 @@ private:
//------------------------------------------------------------------------------------------
// JoinTextNodesCommand
-class JoinTextNodesCommand : public ModifyTextNodeCommand
+class JoinTextNodesCommand : public EditCommand
{
public:
JoinTextNodesCommand(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *);
@@ -361,9 +363,24 @@ private:
};
//------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommand
+
+class RemoveNodeAndPruneCommand : public CompositeEditCommand
+{
+public:
+ RemoveNodeAndPruneCommand(DOM::DocumentImpl *, DOM::NodeImpl *);
+ virtual ~RemoveNodeAndPruneCommand();
+
+ DOM::NodeImpl *node() const;
+
+private:
+ inline RemoveNodeAndPruneCommandImpl *impl() const;
+};
+
+//------------------------------------------------------------------------------------------
// SplitTextNodeCommand
-class SplitTextNodeCommand : public ModifyTextNodeCommand
+class SplitTextNodeCommand : public EditCommand
{
public:
SplitTextNodeCommand(DOM::DocumentImpl *, DOM::TextImpl *, long);
diff --git a/WebCore/khtml/editing/htmlediting_impl.cpp b/WebCore/khtml/editing/htmlediting_impl.cpp
index 10c1e77..8883984 100644
--- a/WebCore/khtml/editing/htmlediting_impl.cpp
+++ b/WebCore/khtml/editing/htmlediting_impl.cpp
@@ -29,12 +29,16 @@
#include "html/html_elementimpl.h"
#include "html/html_imageimpl.h"
#include "htmlattrs.h"
+#include "htmltags.h"
#include "khtml_part.h"
#include "khtml_selection.h"
#include "khtmlview.h"
#include "rendering/render_object.h"
+#include "rendering/render_style.h"
+#include "rendering/render_text.h"
#include "xml/dom_docimpl.h"
#include "xml/dom_elementimpl.h"
+#include "xml/dom_edititerator.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_stringimpl.h"
#include "xml/dom_textimpl.h"
@@ -50,6 +54,7 @@ using DOM::DocumentImpl;
using DOM::DOMPosition;
using DOM::DOMString;
using DOM::DOMStringImpl;
+using DOM::EditIterator;
using DOM::ElementImpl;
using DOM::HTMLElementImpl;
using DOM::HTMLImageElementImpl;
@@ -64,14 +69,15 @@ using khtml::AppendNodeCommand;
using khtml::AppendNodeCommandImpl;
using khtml::CompositeEditCommand;
using khtml::CompositeEditCommandImpl;
-using khtml::DeleteKeyCommand;
-using khtml::DeleteKeyCommandImpl;
+using khtml::DeleteCollapsibleWhitespaceCommand;
+using khtml::DeleteCollapsibleWhitespaceCommandImpl;
using khtml::DeleteSelectionCommand;
using khtml::DeleteSelectionCommandImpl;
using khtml::DeleteTextCommand;
using khtml::DeleteTextCommandImpl;
using khtml::EditCommand;
using khtml::EditCommandImpl;
+using khtml::InlineTextBox;
using khtml::InputNewlineCommand;
using khtml::InputNewlineCommandImpl;
using khtml::InputTextCommand;
@@ -82,10 +88,13 @@ using khtml::InsertTextCommand;
using khtml::InsertTextCommandImpl;
using khtml::JoinTextNodesCommand;
using khtml::JoinTextNodesCommandImpl;
-using khtml::ModifyTextNodeCommand;
-using khtml::ModifyTextNodeCommandImpl;
using khtml::RemoveNodeCommand;
using khtml::RemoveNodeCommandImpl;
+using khtml::RemoveNodeAndPruneCommand;
+using khtml::RemoveNodeAndPruneCommandImpl;
+using khtml::RenderObject;
+using khtml::RenderStyle;
+using khtml::RenderText;
using khtml::PasteHTMLCommand;
using khtml::PasteHTMLCommandImpl;
using khtml::PasteImageCommand;
@@ -101,13 +110,133 @@ using khtml::TypingCommandImpl;
#define ASSERT_NOT_REACHED() ((void)0)
#define LOG(channel, formatAndArgs...) ((void)0)
#define ERROR(formatAndArgs...) ((void)0)
+#if LOG_DISABLED
+#define debugPosition(a,b) ((void)0)
#endif
+#endif
+
+static inline bool isNBSP(const QChar &c)
+{
+ return c == QChar(0xa0);
+}
+
+static inline bool isWS(const QChar &c)
+{
+ return c.isSpace() && c != QChar(0xa0);
+}
+
+static inline bool isWS(const DOMString &text)
+{
+ if (text.length() != 1)
+ return false;
+
+ return isWS(text[0]);
+}
+
+static inline bool isWS(const DOMPosition &pos)
+{
+ if (!pos.node())
+ return false;
+
+ if (!pos.node()->isTextNode())
+ return false;
+
+ const DOMString &string = static_cast<TextImpl *>(pos.node())->data();
+ 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 (renderer->isBR() || renderer->isBlockFlow() || 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 DOMPosition leadingWhitespacePosition(const DOMPosition &pos)
+{
+ ASSERT(pos.notEmpty());
+
+ KHTMLSelection selection(pos);
+ DOMPosition prev = pos.previousCharacterPosition();
+ if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node()) && prev.node()->isTextNode()) {
+ DOMString string = static_cast<TextImpl *>(prev.node())->data();
+ if (isWS(string[prev.offset()]))
+ return prev;
+ }
+
+ return DOMPosition();
+}
+
+static DOMPosition trailingWhitespacePosition(const DOMPosition &pos)
+{
+ ASSERT(pos.notEmpty());
+
+ if (pos.node()->isTextNode()) {
+ TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+ if (pos.offset() >= (long)textNode->length()) {
+ DOMPosition next = pos.nextCharacterPosition();
+ if (next != pos && next.node()->inSameContainingEditableBlock(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 DOMPosition();
+}
+
+static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
+{
+ ASSERT(text1);
+ ASSERT(text2);
+
+ return (text1->nextSibling() == text2);
+}
+
+static DOMString &nonBreakingSpaceString()
+{
+ static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
+ return nonBreakingSpaceString;
+}
//------------------------------------------------------------------------------------------
// EditCommandImpl
EditCommandImpl::EditCommandImpl(DocumentImpl *document)
- : SharedCommandImpl(), m_document(document), m_state(NotApplied), m_isCompositeStep(false)
+ : SharedCommandImpl(), m_document(document), m_state(NotApplied), m_parent(0)
{
ASSERT(m_document);
ASSERT(m_document->part());
@@ -190,12 +319,22 @@ KHTMLSelection EditCommandImpl::currentSelection() const
void EditCommandImpl::setStartingSelection(const KHTMLSelection &s)
{
m_startingSelection = s;
+ EditCommand cmd = parent();
+ while (cmd.notNull()) {
+ cmd.setStartingSelection(s);
+ cmd = cmd.parent();
+ }
moveToStartingSelection();
}
void EditCommandImpl::setEndingSelection(const KHTMLSelection &s)
{
m_endingSelection = s;
+ EditCommand cmd = parent();
+ while (cmd.notNull()) {
+ cmd.setEndingSelection(s);
+ cmd = cmd.parent();
+ }
moveToEndingSelection();
}
@@ -213,6 +352,16 @@ void EditCommandImpl::moveToEndingSelection()
m_document->part()->takeSelectionFrom(this, true);
}
+EditCommand EditCommandImpl::parent() const
+{
+ return m_parent;
+}
+
+void EditCommandImpl::setParent(const EditCommand &cmd)
+{
+ m_parent = cmd;
+}
+
//------------------------------------------------------------------------------------------
// CompositeEditCommandImpl
@@ -233,7 +382,6 @@ int CompositeEditCommandImpl::commandID() const
void CompositeEditCommandImpl::doUnapply()
{
if (m_cmds.count() == 0) {
- ERROR("Unapplying composite command containing zero steps");
return;
}
@@ -247,7 +395,6 @@ void CompositeEditCommandImpl::doUnapply()
void CompositeEditCommandImpl::doReapply()
{
if (m_cmds.count() == 0) {
- ERROR("Reapplying composite command containing zero steps");
return;
}
@@ -263,9 +410,8 @@ void CompositeEditCommandImpl::doReapply()
//
void CompositeEditCommandImpl::applyCommandToComposite(EditCommand &cmd)
{
- cmd.setIsCompositeStep();
+ cmd.setParent(this);
cmd.apply();
- setEndingSelection(cmd.endingSelection());
m_cmds.append(cmd);
}
@@ -312,6 +458,12 @@ void CompositeEditCommandImpl::removeNode(DOM::NodeImpl *removeChild)
applyCommandToComposite(cmd);
}
+void CompositeEditCommandImpl::removeNodeAndPrune(DOM::NodeImpl *removeChild)
+{
+ RemoveNodeAndPruneCommand cmd(document(), removeChild);
+ applyCommandToComposite(cmd);
+}
+
void CompositeEditCommandImpl::splitTextNode(DOM::TextImpl *text, long offset)
{
SplitTextNodeCommand cmd(document(), text, offset);
@@ -336,6 +488,14 @@ void CompositeEditCommandImpl::deleteText(DOM::TextImpl *node, long offset, long
applyCommandToComposite(cmd);
}
+void CompositeEditCommandImpl::replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText)
+{
+ DeleteTextCommand deleteCommand(document(), node, offset, count);
+ applyCommandToComposite(deleteCommand);
+ InsertTextCommand insertCommand(document(), node, offset, replacementText);
+ applyCommandToComposite(insertCommand);
+}
+
void CompositeEditCommandImpl::deleteSelection()
{
if (currentSelection().state() == KHTMLSelection::RANGE) {
@@ -344,88 +504,24 @@ void CompositeEditCommandImpl::deleteSelection()
}
}
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommandImpl
-
-ModifyTextNodeCommandImpl::ModifyTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
- : EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
+void CompositeEditCommandImpl::deleteSelection(const KHTMLSelection &selection)
{
- ASSERT(m_text2);
- ASSERT(m_text2->length() > 0);
- ASSERT(m_offset >= 0);
-
- m_text2->ref();
-}
-
-ModifyTextNodeCommandImpl::ModifyTextNodeCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
- : EditCommandImpl(document), m_text1(text1), m_text2(text2), m_offset(0)
-{
- ASSERT(m_text1);
- ASSERT(m_text2);
- ASSERT(m_text1->nextSibling() == m_text2);
- ASSERT(m_text1->length() > 0);
- ASSERT(m_text2->length() > 0);
-
- m_text1->ref();
- m_text2->ref();
-}
-
-ModifyTextNodeCommandImpl::~ModifyTextNodeCommandImpl()
-{
- if (m_text2)
- m_text2->deref();
- if (m_text1)
- m_text1->deref();
-}
-
-int ModifyTextNodeCommandImpl::commandID() const
-{
- return ModifyTextNodeCommandID;
+ if (selection.state() == KHTMLSelection::RANGE) {
+ DeleteSelectionCommand cmd(document(), selection);
+ applyCommandToComposite(cmd);
+ }
}
-void ModifyTextNodeCommandImpl::splitTextNode()
+void CompositeEditCommandImpl::deleteCollapsibleWhitespace()
{
- ASSERT(m_text2);
- ASSERT(m_text1 == 0);
- ASSERT(m_offset > 0);
- ASSERT(state() == splitState());
-
- ASSERT(m_offset >= m_text2->caretMinOffset() && m_offset <= m_text2->caretMaxOffset());
-
- int exceptionCode = 0;
- m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
- ASSERT(exceptionCode == 0);
- ASSERT(m_text1);
- m_text1->ref();
-
- m_text2->deleteData(0, m_offset, exceptionCode);
- ASSERT(exceptionCode == 0);
-
- m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
- ASSERT(exceptionCode == 0);
-
- ASSERT(m_text2->previousSibling()->isTextNode());
- m_text1 = static_cast<TextImpl *>(m_text2->previousSibling());
+ DeleteCollapsibleWhitespaceCommand cmd(document());
+ applyCommandToComposite(cmd);
}
-void ModifyTextNodeCommandImpl::joinTextNodes()
+void CompositeEditCommandImpl::deleteCollapsibleWhitespace(const KHTMLSelection &selection)
{
- ASSERT(m_text1);
- ASSERT(m_text2);
- ASSERT(state() == joinState());
-
- ASSERT(m_text1->nextSibling() == m_text2);
-
- int exceptionCode = 0;
- m_text2->insertData(0, m_text1->data(), exceptionCode);
- ASSERT(exceptionCode == 0);
-
- m_text2->parent()->removeChild(m_text1, exceptionCode);
- ASSERT(exceptionCode == 0);
-
- m_offset = m_text1->length();
- m_text1->deref();
- m_text1 = 0;
+ DeleteCollapsibleWhitespaceCommand cmd(document(), selection);
+ applyCommandToComposite(cmd);
}
//==========================================================================================
@@ -433,11 +529,11 @@ void ModifyTextNodeCommandImpl::joinTextNodes()
//------------------------------------------------------------------------------------------
// AppendNodeCommandImpl
-AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *parent, NodeImpl *appendChild)
- : EditCommandImpl(document), m_parent(parent), m_appendChild(appendChild)
+AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *parentNode, NodeImpl *appendChild)
+ : EditCommandImpl(document), m_parentNode(parentNode), m_appendChild(appendChild)
{
- ASSERT(m_parent);
- m_parent->ref();
+ ASSERT(m_parentNode);
+ m_parentNode->ref();
ASSERT(m_appendChild);
m_appendChild->ref();
@@ -445,8 +541,8 @@ AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *p
AppendNodeCommandImpl::~AppendNodeCommandImpl()
{
- if (m_parent)
- m_parent->deref();
+ if (m_parentNode)
+ m_parentNode->deref();
if (m_appendChild)
m_appendChild->deref();
}
@@ -458,87 +554,175 @@ int AppendNodeCommandImpl::commandID() const
void AppendNodeCommandImpl::doApply()
{
- ASSERT(m_parent);
+ ASSERT(m_parentNode);
ASSERT(m_appendChild);
int exceptionCode = 0;
- m_parent->appendChild(m_appendChild, exceptionCode);
+ m_parentNode->appendChild(m_appendChild, exceptionCode);
ASSERT(exceptionCode == 0);
}
void AppendNodeCommandImpl::doUnapply()
{
- ASSERT(m_parent);
+ ASSERT(m_parentNode);
ASSERT(m_appendChild);
ASSERT(state() == Applied);
int exceptionCode = 0;
- m_parent->removeChild(m_appendChild, exceptionCode);
+ m_parentNode->removeChild(m_appendChild, exceptionCode);
ASSERT(exceptionCode == 0);
}
//------------------------------------------------------------------------------------------
-// DeleteKeyCommandImpl
+// DeleteCollapsibleWhitespaceCommandImpl
-DeleteKeyCommandImpl::DeleteKeyCommandImpl(DocumentImpl *document)
- : CompositeEditCommandImpl(document)
+DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
+ : CompositeEditCommandImpl(document), m_selectionToCollapse(currentSelection()), m_charactersDeleted(0)
{
}
-DeleteKeyCommandImpl::~DeleteKeyCommandImpl()
+DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const KHTMLSelection &selection)
+ : CompositeEditCommandImpl(document), m_selectionToCollapse(selection), m_charactersDeleted(0)
{
}
-int DeleteKeyCommandImpl::commandID() const
+DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
{
- return DeleteKeyCommandID;
}
-void DeleteKeyCommandImpl::doApply()
+int DeleteCollapsibleWhitespaceCommandImpl::commandID() const
{
- KHTMLPart *part = document()->part();
- ASSERT(part);
+ return DeleteCollapsibleWhitespaceCommandID;
+}
- KHTMLSelection selection = part->selection();
- ASSERT(!selection.isEmpty());
+static bool shouldDeleteUpstreamPosition(const DOMPosition &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;
- // Delete the current selection
- if (selection.state() == KHTMLSelection::RANGE) {
- deleteSelection();
- setEndingSelection(currentSelection());
- return;
+ if (pos.isLastRenderedPositionInEditableBlock())
+ return false;
+
+ if (pos.isFirstRenderedPositionOnLine())
+ 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;
+}
- NodeImpl *caretNode = selection.startNode();
+DOMPosition DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const DOMPosition &pos)
+{
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+
+ bool del = shouldDeleteUpstreamPosition(upstream);
- if (caretNode->isTextNode()) {
- // Check if we can delete character at cursor
- int offset = selection.startOffset() - 1;
- if (offset >= caretNode->caretMinOffset()) {
- TextImpl *textNode = static_cast<TextImpl *>(caretNode);
- deleteText(textNode, offset, 1);
- selection = KHTMLSelection(textNode, offset);
- setEndingSelection(selection);
- return;
+ 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());
+ EditIterator 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;
- // Check if previous sibling is a BR element
- NodeImpl *previousSibling = caretNode->previousSibling();
- if (previousSibling && previousSibling->renderer() && previousSibling->renderer()->isBR()) {
- removeNode(previousSibling);
- return;
+ EditIterator it(upstream);
+ DOMPosition deleteStart = upstream;
+ if (!del) {
+ deleteStart = it.peekNext();
+ if (deleteStart == downstream)
+ return upstream;
+ }
+
+ DOMPosition endingPosition = upstream;
+
+ while (it.current() != downstream) {
+
+ DOMPosition next = it.peekNext();
+ if (next.node() != deleteStart.node()) {
+ ASSERT(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);
+ 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) {
+ ASSERT(deleteStart.node() == downstream.node());
+ ASSERT(downstream.node()->isTextNode());
+ 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 = DOMPosition(downstream.node(), downstream.offset() - m_charactersDeleted);
+ }
}
- // Check if previous leaf node is a text node
- NodeImpl *previousLeafNode = caretNode->previousLeafNode();
- if (previousLeafNode && previousLeafNode->isTextNode()) {
- TextImpl *textNode = static_cast<TextImpl *>(previousLeafNode);
- offset = previousLeafNode->caretMaxOffset() - 1;
- deleteText(textNode, offset, 1);
- selection = KHTMLSelection(textNode, offset);
- setEndingSelection(selection);
- return;
+ it.setPosition(next);
+ }
+
+ return endingPosition;
+}
+
+void DeleteCollapsibleWhitespaceCommandImpl::doApply()
+{
+ int state = m_selectionToCollapse.state();
+ if (state == KHTMLSelection::CARET) {
+ DOMPosition endPosition = deleteWhitespace(m_selectionToCollapse.startPosition());
+ setEndingSelection(endPosition);
+ LOG(Editing, "-----------------------------------------------------\n");
+ }
+ else if (state == KHTMLSelection::RANGE) {
+ DOMPosition startPosition = deleteWhitespace(m_selectionToCollapse.startPosition());
+ LOG(Editing, "-----------------------------------------------------\n");
+ DOMPosition endPosition = m_selectionToCollapse.endPosition();
+ if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
+ LOG(Editing, "adjust end position by %d\n", m_charactersDeleted);
+ endPosition = DOMPosition(endPosition.node(), endPosition.offset() - m_charactersDeleted);
}
+ endPosition = deleteWhitespace(endPosition);
+ setEndingSelection(KHTMLSelection(startPosition, endPosition));
+ LOG(Editing, "=====================================================\n");
}
}
@@ -548,6 +732,13 @@ void DeleteKeyCommandImpl::doApply()
DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DOM::DocumentImpl *document)
: CompositeEditCommandImpl(document)
{
+ m_selectionToDelete = startingSelection();
+}
+
+DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DOM::DocumentImpl *document, const KHTMLSelection &selection)
+ : CompositeEditCommandImpl(document)
+{
+ m_selectionToDelete = selection;
}
DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl()
@@ -559,124 +750,211 @@ int DeleteSelectionCommandImpl::commandID() const
return DeleteSelectionCommandID;
}
-void DeleteSelectionCommandImpl::doApply()
+void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
{
- if (startingSelection().isEmpty()) {
+ KHTMLSelection selection = currentSelection();
+
+ if (selection.state() != KHTMLSelection::CARET)
return;
- }
- KHTMLSelection selection = startingSelection();
+ DOMPosition pos = selection.startPosition();
+
+ if (!pos.node()->isTextNode())
+ return;
- //
- // Figure out where to place the caret after doing the delete:
- //
- // 1. If the start node is not completely selected, use the start
- // node and start offset for the new position; else
- // 2. If the start and end nodes are completely selected:
- // a. If there is an editable node following the end node,
- // place the caret in the min caret offset of that node; else
- // b. If there is an editable node before the start node,
- // place the caret in the max caret offset of that node; else
- // c. There is no more editable content in the document.
- // EDIT FIXME: We do not handle this case now
- // 3. If the start node is completely selected and the end node is
- // different than the start node and it is not completely selected,
- // use the end node and the end node min caret for the new position; else
- //
+ TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+
+ if (pos.offset() == 0) {
+ EditIterator it(pos);
+ DOMPosition 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(DOMPosition(textNode, prevTextNode->length()));
+ LOG(Editing, "joinTextNodesWithSameStyle [1]\n");
+ }
+ }
+ }
+ else if (pos.offset() == (long)textNode->length()) {
+ EditIterator it(pos);
+ DOMPosition 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(DOMPosition(nextTextNode, pos.offset()));
+ LOG(Editing, "joinTextNodesWithSameStyle [2]\n");
+ }
+ }
+ }
+}
+
+static void debugPosition(const char *prefix, const DOMPosition &pos)
+{
+ LOG(Editing, "%s%s %p : %d", prefix, getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+}
- DOMPosition deleteStart = DOMPosition(selection.startNode(), selection.startOffset());
- DOMPosition deleteEnd = DOMPosition(selection.endNode(), selection.endOffset());
+enum { NoPositionModification, MoveDownstreamPositionModification, MoveToNextCharacterModification };
- bool startIsCompletelySelected =
- deleteStart.offset() == deleteStart.node()->caretMinOffset() &&
- ((deleteStart.node() != deleteEnd.node()) ||
- (deleteEnd.offset() == deleteEnd.node()->caretMaxOffset()));
+void DeleteSelectionCommandImpl::doApply()
+{
+ if (m_selectionToDelete.state() != KHTMLSelection::RANGE)
+ return;
- bool endIsCompletelySelected =
- deleteEnd.offset() == deleteEnd.node()->caretMaxOffset() &&
- ((deleteStart.node() != deleteEnd.node()) ||
- (deleteStart.offset() == deleteStart.node()->caretMinOffset()));
+ KHTMLSelection selection = m_selectionToDelete;
DOMPosition endingPosition;
+ bool adjustEndingPositionDownstream = false;
+
+ DOMPosition upstreamStart = selection.startPosition().equivalentUpstreamPosition();
+ DOMPosition downstreamStart = selection.startPosition().equivalentDownstreamPosition();
+ DOMPosition upstreamEnd = selection.endPosition().equivalentUpstreamPosition();
+ DOMPosition downstreamEnd = selection.endPosition().equivalentDownstreamPosition();
+
+ bool startCompletelySelected =
+ downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
+ ((downstreamStart.node() != upstreamEnd.node()) ||
+ (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset()));
- if (!startIsCompletelySelected) {
- // Case 1
- endingPosition = DOMPosition(deleteStart.node(), deleteStart.offset());
+ bool endCompletelySelected =
+ upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
+ ((downstreamStart.node() != upstreamEnd.node()) ||
+ (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset()));
+
+ unsigned long startRenderedOffset = downstreamStart.renderedOffset();
+ //unsigned long endRenderedOffset = upstreamEnd.renderedOffset();
+
+ bool startAtStartOfRootEditableBlock = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableBlock();
+ bool startAtStartOfBlock = startAtStartOfRootEditableBlock ||
+ (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", startAtStartOfRootEditableBlock ? "YES" : "NO");
+ LOG(Editing, "at end block: %s", endAtEndOfBlock ? "YES" : "NO");
+
+ // Start is not completely selected
+ if (startAtStartOfBlock) {
+ LOG(Editing, "ending position case 1");
+ endingPosition = DOMPosition(downstreamStart.node()->containingEditableBlock(), 1);
+ 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 (endIsCompletelySelected) {
- DOMPosition pos = selection.nextCharacterPosition(deleteEnd);
- if (pos != deleteEnd) {
- // Case 2a
- endingPosition = DOMPosition(pos.node(), pos.node()->caretMinOffset());
+ else if (upstreamStart != downstreamStart) {
+ LOG(Editing, "ending position case 3");
+ endingPosition = upstreamStart;
+ if (downstreamStart.node()->id() == ID_BR && downstreamStart.offset() == 0)
+ adjustEndingPositionDownstream = true;
+ }
+
+ //
+ // Figure out the whitespace conversions to do
+ //
+ if (startAtStartOfBlock && !endAtEndOfBlock) {
+ // convert trailing whitespace
+ DOMPosition trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
+ if (trailing.notEmpty()) {
+ debugPosition("convertTrailingWhitespace: ", trailing);
+ DOMPosition collapse = trailing.nextCharacterPosition();
+ if (collapse != trailing)
+ deleteCollapsibleWhitespace(collapse);
+ TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
+ replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
}
- else {
- pos = selection.previousCharacterPosition(deleteStart);
- if (pos != deleteStart) {
- // Case 2b
- endingPosition = DOMPosition(pos.node(), pos.node()->caretMaxOffset());
- }
- else {
- // Case 2c
- // EDIT FIXME
- endingPosition = DOMPosition();
- }
+ }
+ else if (!startAtStartOfBlock && endAtEndOfBlock) {
+ // convert leading whitespace
+ DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
+ if (leading.notEmpty()) {
+ debugPosition("convertLeadingWhitespace: ", leading);
+ TextImpl *textNode = static_cast<TextImpl *>(leading.node());
+ replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
}
}
- else {
- // Case 3
- endingPosition = DOMPosition(deleteEnd.node(), deleteEnd.node()->caretMinOffset());
+ else if (!startAtStartOfBlock && !endAtEndOfBlock) {
+ // convert contiguous whitespace
+ DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
+ DOMPosition 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());
+ }
}
-
+
//
// Do the delete
//
- NodeImpl *n = deleteStart.node()->nextLeafNode();
+ NodeImpl *n = downstreamStart.node()->traverseNextNode();
// work on start node
- if (startIsCompletelySelected) {
- removeNode(deleteStart.node());
+ if (startCompletelySelected) {
+ removeNodeAndPrune(downstreamStart.node());
}
- else if (deleteStart.node()->isTextNode()) {
- TextImpl *text = static_cast<TextImpl *>(deleteStart.node());
- int endOffset = text == deleteEnd.node() ? deleteEnd.offset() : text->length();
- if (endOffset > deleteStart.offset()) {
- deleteText(text, deleteStart.offset(), endOffset - deleteStart.offset());
+ else if (downstreamStart.node()->isTextNode()) {
+ 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());
}
}
else {
- ASSERT_NOT_REACHED();
+ // we have clipped the end of a non-text element
+ // the offset must be 1 here. if it is, do nothing and move on.
+ ASSERT(downstreamStart.offset() == 1);
}
- if (deleteStart.node() != deleteEnd.node()) {
+ if (downstreamStart.node() != upstreamEnd.node()) {
// work on intermediate nodes
- while (n != deleteEnd.node()) {
+ while (n != upstreamEnd.node()) {
NodeImpl *d = n;
- n = n->nextLeafNode();
- removeNode(d);
+ n = n->traverseNextNode();
+ if (d->renderer() && d->renderer()->isEditable())
+ removeNodeAndPrune(d);
}
// work on end node
- ASSERT(n == deleteEnd.node());
- if (endIsCompletelySelected) {
- removeNode(deleteEnd.node());
+ ASSERT(n == upstreamEnd.node());
+ if (endCompletelySelected) {
+ removeNodeAndPrune(upstreamEnd.node());
}
- else if (deleteEnd.node()->isTextNode()) {
- if (deleteEnd.offset() > 0) {
- TextImpl *text = static_cast<TextImpl *>(deleteEnd.node());
- deleteText(text, 0, deleteEnd.offset());
+ else if (upstreamEnd.node()->isTextNode()) {
+ if (upstreamEnd.offset() > 0) {
+ TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
+ deleteText(text, 0, upstreamEnd.offset());
}
}
else {
- ASSERT_NOT_REACHED();
+ // 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);
}
}
- //
- // set the ending selection
- //
- selection.moveTo(endingPosition);
- selection.moveToRenderedContent();
- setEndingSelection(selection);
+ if (adjustEndingPositionDownstream) {
+ LOG(Editing, "adjust ending position downstream");
+ endingPosition = endingPosition.equivalentDownstreamPosition();
+ }
+
+ debugPosition("ending position: ", endingPosition);
+ setEndingSelection(endingPosition);
+
+ LOG(Editing, "-----------------------------------------------------\n");
}
//------------------------------------------------------------------------------------------
@@ -752,8 +1030,12 @@ void InputNewlineCommandImpl::doApply()
// Delete the current selection
deleteSelection();
+ // reset the current selection since it may have changed due to the delete
+ selection = currentSelection();
+
int exceptionCode = 0;
ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
+ ASSERT(exceptionCode == 0);
TextImpl *textNode = static_cast<TextImpl *>(selection.startNode());
bool atStart = selection.startOffset() == textNode->renderer()->caretMinOffset();
@@ -767,8 +1049,8 @@ void InputNewlineCommandImpl::doApply()
else if (atEnd) {
insertNodeAfter(breakNode, textNode);
// Set the cursor at the beginning of the the BR.
- selection = selection.nextCharacterPosition();
- setEndingSelection(selection);
+ DOMPosition pos(breakNode, 0);
+ setEndingSelection(pos);
}
else {
TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.startOffset(), exceptionCode));
@@ -787,16 +1069,15 @@ void InputNewlineCommandImpl::doApply()
//------------------------------------------------------------------------------------------
// InputTextCommandImpl
-InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document, const DOMString &text)
- : CompositeEditCommandImpl(document)
+InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document)
+ : CompositeEditCommandImpl(document), m_insertedTextNode(0), m_charactersAdded(0)
{
- ASSERT(!text.isEmpty());
- m_text = text;
- nbsp = DOMString(QChar(0xa0));
}
InputTextCommandImpl::~InputTextCommandImpl()
{
+ if (m_insertedTextNode)
+ m_insertedTextNode->deref();
}
int InputTextCommandImpl::commandID() const
@@ -806,20 +1087,16 @@ int InputTextCommandImpl::commandID() const
void InputTextCommandImpl::doApply()
{
- execute(m_text);
}
-void InputTextCommandImpl::coalesce(const DOMString &text)
+void InputTextCommandImpl::input(const DOMString &text)
{
- ASSERT(state() == Applied);
execute(text);
- m_text += text;
}
void InputTextCommandImpl::deleteCharacter()
{
ASSERT(state() == Applied);
- ASSERT(m_text.length() > 0);
KHTMLSelection selection = currentSelection();
@@ -834,24 +1111,111 @@ void InputTextCommandImpl::deleteCharacter()
ASSERT(exceptionCode == 0);
selection = KHTMLSelection(textNode, offset);
setEndingSelection(selection);
- m_text = m_text.string().left(m_text.length() - 1);
+ m_charactersAdded--;
}
}
-void InputTextCommandImpl::execute(const DOMString &text)
+DOMPosition InputTextCommandImpl::prepareForTextInsertion()
{
+ // Prepare for text input by looking at the current position.
+ // It may be necessary to insert a text node to receive characters.
KHTMLSelection selection = currentSelection();
+ ASSERT(selection.state() == KHTMLSelection::CARET);
+
+ DOMPosition pos = selection.startPosition().equivalentUpstreamPosition();
+ if (!pos.node()->inSameContainingEditableBlock(selection.startNode()))
+ pos = selection.startPosition();
+
+ if (!pos.node()->isTextNode()) {
+ if (!m_insertedTextNode) {
+ m_insertedTextNode = document()->createTextNode("");
+ m_insertedTextNode->setRendererIsNeeded();
+ m_insertedTextNode->ref();
+ }
+
+ if (pos.node()->isEditableBlock())
+ appendNode(pos.node(), m_insertedTextNode);
+ else if (pos.node()->id() == ID_BR || pos.offset() == 1)
+ insertNodeAfter(m_insertedTextNode, pos.node());
+ else {
+ ASSERT(pos.offset() == 0);
+ insertNodeBefore(m_insertedTextNode, pos.node());
+ }
+
+ pos = DOMPosition(m_insertedTextNode, 0);
+ }
+
+ return pos;
+}
- if (!selection.startNode()->isTextNode())
- return;
+void InputTextCommandImpl::execute(const DOMString &text)
+{
+ KHTMLSelection selection = currentSelection();
// Delete the current selection
deleteSelection();
- TextImpl *textNode = static_cast<TextImpl *>(selection.startNode());
- insertText(textNode, selection.startOffset(), text);
- selection = KHTMLSelection(selection.startNode(), selection.startOffset() + text.length());
+ // Make sure the document is set up to receive text
+ DOMPosition pos = prepareForTextInsertion();
+
+ TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+ long offset = pos.offset();
+
+ // This is a temporary implementation for inserting adjoining spaces
+ // into a document. We are working on a CSS-related whitespace solution
+ // that will replace this some day.
+ if (isWS(text))
+ insertSpace(textNode, offset);
+ else
+ insertText(textNode, offset, text);
+ selection = KHTMLSelection(textNode, offset + text.length());
setEndingSelection(selection);
+ m_charactersAdded += text.length();
+}
+
+void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
+{
+ ASSERT(textNode);
+
+ DOMString text(textNode->data());
+
+ // count up all spaces and newlines in front of the caret
+ // delete all collapsed ones
+ // this will work out OK since the offset we have been passed has been upstream-ized
+ int count = 0;
+ for (unsigned int i = offset; i < text.length(); i++) {
+ if (isWS(text[i]))
+ count++;
+ else
+ break;
+ }
+ if (count > 0) {
+ // By checking the character at the downstream position, we can
+ // check if there is a rendered WS at the caret
+ DOMPosition pos(textNode, offset);
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
+ count--; // leave this WS in
+ if (count > 0)
+ deleteText(textNode, offset, count);
+ }
+
+ if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
+ // insert a "regular" space
+ insertText(textNode, offset, " ");
+ return;
+ }
+
+ if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
+ // DOM looks like this:
+ // nbsp nbsp caret
+ // insert a space between the two nbsps
+ insertText(textNode, offset - 1, " ");
+ return;
+ }
+
+ // insert an nbsp
+ insertText(textNode, offset, nonBreakingSpaceString());
}
//------------------------------------------------------------------------------------------
@@ -951,12 +1315,24 @@ void InsertTextCommandImpl::doUnapply()
// JoinTextNodesCommandImpl
JoinTextNodesCommandImpl::JoinTextNodesCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
- : ModifyTextNodeCommandImpl(document, text1, text2)
+ : EditCommandImpl(document), m_text1(text1), m_text2(text2)
{
+ ASSERT(m_text1);
+ ASSERT(m_text2);
+ ASSERT(m_text1->nextSibling() == m_text2);
+ ASSERT(m_text1->length() > 0);
+ ASSERT(m_text2->length() > 0);
+
+ m_text1->ref();
+ m_text2->ref();
}
JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl()
{
+ if (m_text1)
+ m_text1->deref();
+ if (m_text2)
+ m_text2->deref();
}
int JoinTextNodesCommandImpl::commandID() const
@@ -966,12 +1342,35 @@ int JoinTextNodesCommandImpl::commandID() const
void JoinTextNodesCommandImpl::doApply()
{
- joinTextNodes();
+ ASSERT(m_text1);
+ ASSERT(m_text2);
+ ASSERT(m_text1->nextSibling() == m_text2);
+
+ int exceptionCode = 0;
+ m_text2->insertData(0, m_text1->data(), exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ m_text2->parent()->removeChild(m_text1, exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ m_offset = m_text1->length();
}
void JoinTextNodesCommandImpl::doUnapply()
{
- splitTextNode();
+ ASSERT(m_text2);
+ ASSERT(m_offset > 0);
+
+ int exceptionCode = 0;
+
+ m_text2->deleteData(0, m_offset, exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ ASSERT(m_text2->previousSibling()->isTextNode());
+ ASSERT(m_text2->previousSibling() == m_text1);
}
//------------------------------------------------------------------------------------------
@@ -1152,15 +1551,55 @@ void RemoveNodeCommandImpl::doUnapply()
}
//------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommandImpl
+
+RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
+ : CompositeEditCommandImpl(document), m_removeChild(removeChild)
+{
+ ASSERT(m_removeChild);
+ m_removeChild->ref();
+}
+
+RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
+{
+ if (m_removeChild)
+ m_removeChild->deref();
+}
+
+int RemoveNodeAndPruneCommandImpl::commandID() const
+{
+ return RemoveNodeAndPruneCommandID;
+}
+
+void RemoveNodeAndPruneCommandImpl::doApply()
+{
+ NodeImpl *editableBlock = m_removeChild->containingEditableBlock();
+ NodeImpl *pruneNode = m_removeChild;
+ NodeImpl *node = pruneNode->traversePreviousNode();
+ removeNode(pruneNode);
+ while (1) {
+ if (editableBlock != node->containingEditableBlock() || !shouldPruneNode(node))
+ break;
+ pruneNode = node;
+ node = node->traversePreviousNode();
+ removeNode(pruneNode);
+ }
+}
+
+//------------------------------------------------------------------------------------------
// SplitTextNodeCommandImpl
SplitTextNodeCommandImpl::SplitTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
- : ModifyTextNodeCommandImpl(document, text, offset)
+ : EditCommandImpl(document), m_text2(text), m_offset(offset)
{
}
SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl()
{
+ if (m_text1)
+ m_text1->deref();
+ if (m_text2)
+ m_text2->deref();
}
int SplitTextNodeCommandImpl::commandID() const
@@ -1170,19 +1609,56 @@ int SplitTextNodeCommandImpl::commandID() const
void SplitTextNodeCommandImpl::doApply()
{
- splitTextNode();
+ ASSERT(m_text2);
+ ASSERT(m_offset > 0);
+
+ int exceptionCode = 0;
+
+ // EDIT FIXME: This should use better smarts for figuring out which portion
+ // of the split to copy (based on their comparitive sizes). We should also
+ // just use the DOM's splitText function.
+
+ if (!m_text1) {
+ // create only if needed.
+ // if reapplying, this object will already exist.
+ m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
+ ASSERT(exceptionCode == 0);
+ ASSERT(m_text1);
+ m_text1->ref();
+ }
+
+ m_text2->deleteData(0, m_offset, exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ ASSERT(m_text2->previousSibling()->isTextNode());
+ ASSERT(m_text2->previousSibling() == m_text1);
}
void SplitTextNodeCommandImpl::doUnapply()
{
- joinTextNodes();
+ ASSERT(m_text1);
+ ASSERT(m_text2);
+
+ ASSERT(m_text1->nextSibling() == m_text2);
+
+ int exceptionCode = 0;
+ m_text2->insertData(0, m_text1->data(), exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ m_text2->parent()->removeChild(m_text1, exceptionCode);
+ ASSERT(exceptionCode == 0);
+
+ m_offset = m_text1->length();
}
//------------------------------------------------------------------------------------------
// TypingCommandImpl
TypingCommandImpl::TypingCommandImpl(DocumentImpl *document)
- : CompositeEditCommandImpl(document)
+ : CompositeEditCommandImpl(document), m_openForMoreTyping(true)
{
}
@@ -1202,18 +1678,22 @@ void TypingCommandImpl::doApply()
void TypingCommandImpl::insertText(const DOM::DOMString &text)
{
if (m_cmds.count() == 0) {
- InputTextCommand cmd(document(), text);
+ InputTextCommand cmd(document());
applyCommandToComposite(cmd);
+ cmd.input(text);
+ setEndingSelection(cmd.endingSelection());
}
else {
EditCommand lastCommand = m_cmds.last();
if (lastCommand.commandID() == InputTextCommandID) {
- static_cast<InputTextCommand &>(lastCommand).coalesce(text);
+ static_cast<InputTextCommand &>(lastCommand).input(text);
setEndingSelection(lastCommand.endingSelection());
}
else {
- InputTextCommand cmd(document(), text);
+ InputTextCommand cmd(document());
applyCommandToComposite(cmd);
+ cmd.input(text);
+ setEndingSelection(cmd.endingSelection());
}
}
}
@@ -1224,29 +1704,47 @@ void TypingCommandImpl::insertNewline()
applyCommandToComposite(cmd);
}
+void TypingCommandImpl::issueCommandForDeleteKey()
+{
+ KHTMLSelection selection = currentSelection();
+ ASSERT(selection.state() != KHTMLSelection::NONE);
+
+ if (selection.state() == KHTMLSelection::CARET) {
+ KHTMLSelection selectionToDelete(selection.startPosition().previousCharacterPosition(), selection.startPosition());
+ setEndingSelection(selectionToDelete);
+ deleteCollapsibleWhitespace();
+ selection = currentSelection();
+ deleteSelection(selection);
+ }
+ else { // selection.state() == KHTMLSelection::RANGE
+ deleteCollapsibleWhitespace();
+ deleteSelection();
+ }
+}
+
void TypingCommandImpl::deleteKeyPressed()
{
if (m_cmds.count() == 0) {
- DeleteKeyCommand cmd(document());
- applyCommandToComposite(cmd);
+ issueCommandForDeleteKey();
}
else {
EditCommand lastCommand = m_cmds.last();
if (lastCommand.commandID() == InputTextCommandID) {
InputTextCommand cmd = static_cast<InputTextCommand &>(lastCommand);
cmd.deleteCharacter();
- if (cmd.text().length() == 0)
+ if (cmd.charactersAdded() == 0) {
removeCommand(cmd);
- else
+ }
+ else {
setEndingSelection(cmd.endingSelection());
+ }
}
else if (lastCommand.commandID() == InputNewlineCommandID) {
lastCommand.unapply();
removeCommand(lastCommand);
}
else {
- DeleteKeyCommand cmd(document());
- applyCommandToComposite(cmd);
+ issueCommandForDeleteKey();
}
}
}
diff --git a/WebCore/khtml/editing/htmlediting_impl.h b/WebCore/khtml/editing/htmlediting_impl.h
index 5517fd7..39ef1c1 100644
--- a/WebCore/khtml/editing/htmlediting_impl.h
+++ b/WebCore/khtml/editing/htmlediting_impl.h
@@ -40,6 +40,7 @@ namespace DOM {
class DocumentImpl;
class DOMPosition;
class DOMString;
+ class ElementImpl;
class NodeImpl;
class TextImpl;
};
@@ -56,8 +57,9 @@ public:
virtual ~EditCommandImpl();
virtual int commandID() const;
- bool isCompositeStep() const { return m_isCompositeStep; }
- void setIsCompositeStep(bool flag=true) { m_isCompositeStep = flag; }
+ bool isCompositeStep() const { return parent().notNull(); }
+ EditCommand parent() const;
+ void setParent(const EditCommand &);
enum ECommandState { NotApplied, Applied };
@@ -89,7 +91,7 @@ private:
ECommandState m_state;
KHTMLSelection m_startingSelection;
KHTMLSelection m_endingSelection;
- bool m_isCompositeStep;
+ EditCommand m_parent;
};
//------------------------------------------------------------------------------------------
@@ -117,42 +119,21 @@ protected:
void insertNodeAt(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset);
void appendNode(DOM::NodeImpl *parent, DOM::NodeImpl *appendChild);
void removeNode(DOM::NodeImpl *removeChild);
+ void removeNodeAndPrune(DOM::NodeImpl *removeChild);
void splitTextNode(DOM::TextImpl *text, long offset);
void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2);
void insertText(DOM::TextImpl *node, long offset, const DOM::DOMString &text);
void deleteText(DOM::TextImpl *node, long offset, long count);
+ void replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText);
void deleteSelection();
+ void deleteSelection(const KHTMLSelection &selection);
void deleteKeyPressed();
+ void deleteCollapsibleWhitespace();
+ void deleteCollapsibleWhitespace(const KHTMLSelection &selection);
QValueList<EditCommand> m_cmds;
};
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommandImpl
-
-class ModifyTextNodeCommandImpl : public EditCommandImpl
-{
-public:
- // used by SplitTextNodeCommandImpl derived class
- ModifyTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, long);
- // used by JoinTextNodesCommandImpl derived class
- ModifyTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *);
- virtual ~ModifyTextNodeCommandImpl();
-
- virtual int commandID() const;
-
-protected:
- void splitTextNode();
- void joinTextNodes();
-
- virtual ECommandState joinState() = 0;
- virtual ECommandState splitState() = 0;
-
- DOM::TextImpl *m_text1;
- DOM::TextImpl *m_text2;
- long m_offset;
-};
-
//==========================================================================================
// Concrete commands
//------------------------------------------------------------------------------------------
@@ -161,7 +142,7 @@ protected:
class AppendNodeCommandImpl : public EditCommandImpl
{
public:
- AppendNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *parent, DOM::NodeImpl *appendChild);
+ AppendNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *parentNode, DOM::NodeImpl *appendChild);
virtual ~AppendNodeCommandImpl();
virtual int commandID() const;
@@ -169,40 +150,56 @@ public:
virtual void doApply();
virtual void doUnapply();
- DOM::NodeImpl *parent() const { return m_parent; }
+ DOM::NodeImpl *parentNode() const { return m_parentNode; }
DOM::NodeImpl *appendChild() const { return m_appendChild; }
private:
- DOM::NodeImpl *m_parent;
+ DOM::NodeImpl *m_parentNode;
DOM::NodeImpl *m_appendChild;
};
//------------------------------------------------------------------------------------------
-// DeleteKeyCommandImpl
+// DeleteCollapsibleWhitespaceCommandImpl
-class DeleteKeyCommandImpl : public CompositeEditCommandImpl
-{
+class DeleteCollapsibleWhitespaceCommandImpl : public CompositeEditCommandImpl
+{
public:
- DeleteKeyCommandImpl(DOM::DocumentImpl *document);
- virtual ~DeleteKeyCommandImpl();
+ DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document);
+ DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document, const KHTMLSelection &selection);
+ virtual ~DeleteCollapsibleWhitespaceCommandImpl();
+
virtual int commandID() const;
- virtual void doApply();
+ virtual void doApply();
+
+private:
+ DOM::DOMPosition deleteWhitespace(const DOM::DOMPosition &pos);
+
+ KHTMLSelection m_selectionToCollapse;
+ unsigned long m_charactersDeleted;
};
//------------------------------------------------------------------------------------------
// DeleteSelectionCommandImpl
class DeleteSelectionCommandImpl : public CompositeEditCommandImpl
-{
+{
public:
DeleteSelectionCommandImpl(DOM::DocumentImpl *document);
+ DeleteSelectionCommandImpl(DOM::DocumentImpl *document, const KHTMLSelection &selection);
+
virtual ~DeleteSelectionCommandImpl();
virtual int commandID() const;
virtual void doApply();
+
+private:
+ void deleteDownstreamWS(const DOM::DOMPosition &start);
+ void joinTextNodesWithSameStyle();
+
+ KHTMLSelection m_selectionToDelete;
};
//------------------------------------------------------------------------------------------
@@ -250,23 +247,25 @@ public:
class InputTextCommandImpl : public CompositeEditCommandImpl
{
public:
- InputTextCommandImpl(DOM::DocumentImpl *document, const DOM::DOMString &text);
+ InputTextCommandImpl(DOM::DocumentImpl *document);
virtual ~InputTextCommandImpl();
virtual int commandID() const;
virtual void doApply();
- DOM::DOMString text() const { return m_text; }
-
void deleteCharacter();
- void coalesce(const DOM::DOMString &text);
+ void input(const DOM::DOMString &text);
+
+ unsigned long charactersAdded() const { return m_charactersAdded; }
private:
+ DOM::DOMPosition prepareForTextInsertion();
void execute(const DOM::DOMString &text);
+ void insertSpace(DOM::TextImpl *textNode, unsigned long offset);
- DOM::DOMString m_text;
- DOM::DOMString nbsp;
+ DOM::TextImpl *m_insertedTextNode;
+ unsigned long m_charactersAdded;
};
//------------------------------------------------------------------------------------------
@@ -318,7 +317,7 @@ private:
//------------------------------------------------------------------------------------------
// JoinTextNodesCommandImpl
-class JoinTextNodesCommandImpl : public ModifyTextNodeCommandImpl
+class JoinTextNodesCommandImpl : public EditCommandImpl
{
public:
JoinTextNodesCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *);
@@ -329,11 +328,13 @@ public:
virtual void doApply();
virtual void doUnapply();
- virtual ECommandState joinState() { return NotApplied; }
- virtual ECommandState splitState() { return Applied; }
-
DOM::TextImpl *firstNode() const { return m_text1; }
DOM::TextImpl *secondNode() const { return m_text2; }
+
+private:
+ DOM::TextImpl *m_text1;
+ DOM::TextImpl *m_text2;
+ unsigned long m_offset;
};
//------------------------------------------------------------------------------------------
@@ -395,9 +396,28 @@ private:
};
//------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommandImpl
+
+class RemoveNodeAndPruneCommandImpl : public CompositeEditCommandImpl
+{
+public:
+ RemoveNodeAndPruneCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *);
+ virtual ~RemoveNodeAndPruneCommandImpl();
+
+ virtual int commandID() const;
+
+ virtual void doApply();
+
+ DOM::NodeImpl *node() const { return m_removeChild; }
+
+private:
+ DOM::NodeImpl *m_removeChild;
+};
+
+//------------------------------------------------------------------------------------------
// SplitTextNodeCommandImpl
-class SplitTextNodeCommandImpl : public ModifyTextNodeCommandImpl
+class SplitTextNodeCommandImpl : public EditCommandImpl
{
public:
SplitTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, long);
@@ -408,11 +428,13 @@ public:
virtual void doApply();
virtual void doUnapply();
- virtual ECommandState joinState() { return Applied; }
- virtual ECommandState splitState() { return NotApplied; }
-
DOM::TextImpl *node() const { return m_text2; }
long offset() const { return m_offset; }
+
+private:
+ DOM::TextImpl *m_text1;
+ DOM::TextImpl *m_text2;
+ unsigned long m_offset;
};
//------------------------------------------------------------------------------------------
@@ -436,6 +458,7 @@ public:
void deleteKeyPressed();
private:
+ void issueCommandForDeleteKey();
void removeCommand(const EditCommand &);
bool m_openForMoreTyping;
diff --git a/WebCore/khtml/editing/selection.cpp b/WebCore/khtml/editing/selection.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/editing/selection.cpp
+++ b/WebCore/khtml/editing/selection.cpp
@@ -25,6 +25,7 @@
#include "khtml_selection.h"
+#include "htmltags.h"
#include "khtml_part.h"
#include "khtmlview.h"
#include "qevent.h"
@@ -38,6 +39,7 @@
#include "rendering/render_style.h"
#include "rendering/render_text.h"
#include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
#include "xml/dom_elementimpl.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
using DOM::DocumentImpl;
using DOM::DOMPosition;
using DOM::DOMString;
+using DOM::EditIterator;
using DOM::ElementImpl;
using DOM::Node;
using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
validate();
}
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
{
init();
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
validate();
}
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+ init();
+
+ setBaseNode(base.node());
+ setExtentNode(extent.node());
+ setBaseOffset(base.offset());
+ setExtentOffset(extent.offset());
+
+ validate();
+}
+
KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
{
init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(endNode());
setExtentOffset(endOffset());
}
- pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().nextCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(endNode(), endOffset());
+ pos = endPosition();
else
- pos = nextCharacterPosition();
+ pos = endPosition().nextCharacterPosition();
}
break;
case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(startNode());
setExtentOffset(startOffset());
}
- pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().previousCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(startNode(), startOffset());
+ pos = startPosition();
else
- pos = previousCharacterPosition();
+ pos = startPosition().previousCharacterPosition();
}
break;
case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
m_needsCaretLayout = flag;
}
-bool KHTMLSelection::isEmpty() const
-{
- return m_baseNode == 0 && m_extentNode == 0;
-}
-
Range KHTMLSelection::toRange() const
{
if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
if (m_needsCaretLayout) {
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (!inRenderedContent(pos)) {
+ if (!pos.inRenderedContent()) {
moveToRenderedContent();
}
layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
#endif
}
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
- return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return from;
-
- NodeImpl *node = from.node();
- long offset = from.offset() - 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->nextEditable() && !renderer->precedesLineBreak())
- offset--;
- assert(offset >= 0);
- return DOMPosition(renderer->element(), offset);
- }
- renderer = renderer->previousEditable();
- }
-
- // can't move the position
- return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
- return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return DOMPosition();
-
- NodeImpl *node = from.node();
- long offset = from.offset() + 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, 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 {
- // 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);
- }
-
- //
- // Look in next renderer(s)
- //
- renderer = renderer->nextEditable();
- while (renderer) {
- // 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);
- }
- else {
- return DOMPosition(renderer->element(), renderer->caretMinOffset());
- }
- renderer = renderer->nextEditable();
- }
-
- // can't move the position
- return from;
-}
-
bool KHTMLSelection::moveToRenderedContent()
{
if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
return false;
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (inRenderedContent(pos))
+ if (pos.inRenderedContent())
return true;
- // not currently rendered, try moving to next
- DOMPosition next = nextCharacterPosition(pos);
- if (next != pos) {
- moveTo(next);
+ // not currently rendered, try moving to prev
+ DOMPosition prev = pos.previousCharacterPosition();
+ if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(prev);
return true;
}
- // could not be moved to next, try prev
- DOMPosition prev = previousCharacterPosition(pos);
- if (prev != pos) {
- moveTo(prev);
+ // could not be moved to prev, try next
+ DOMPosition next = pos.nextCharacterPosition();
+ if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(next);
return true;
}
return false;
}
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
{
- if (pos.isEmpty())
- return false;
-
- long offset = pos.offset();
+ return DOMPosition(baseNode(), baseOffset());
+}
- RenderObject *renderer = pos.node()->renderer();
- if (!renderer)
- return false;
-
- if (renderer->isText() && !renderer->isBR()) {
- RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
- for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
- if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
- return true;
- }
- else 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;
- }
- }
- }
- else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
- return true;
- }
-
- return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+ return DOMPosition(extentNode(), extentOffset());
}
-
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+ return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+ return DOMPosition(endNode(), endOffset());
+}
+
bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2)
{
if (!n1 || !n2)
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
if (!startNode())
return;
- static int context = 5;
+ //static int context = 5;
- RenderObject *r = 0;
+ //RenderObject *r = 0;
fprintf(stderr, "KHTMLSelection =================\n");
-
+
+ if (startPosition() == endPosition()) {
+ DOMPosition pos = startPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 {
+ DOMPosition pos = endPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 = startPosition();
+ upstream = pos.equivalentUpstreamPosition();
+ downstream = pos.equivalentDownstreamPosition();
+ 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());
+ fprintf(stderr, "-----------------------------------\n");
+ }
+
+#if 0
int back = 0;
r = startNode()->renderer();
for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
else
break;
}
+#endif
fprintf(stderr, "================================\n");
}
diff --git a/WebCore/khtml/editing/selection.h b/WebCore/khtml/editing/selection.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/editing/selection.h
+++ b/WebCore/khtml/editing/selection.h
@@ -46,6 +46,7 @@ public:
KHTMLSelection();
KHTMLSelection(DOM::NodeImpl *node, long offset);
KHTMLSelection(const DOM::DOMPosition &);
+ KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
KHTMLSelection(const KHTMLSelection &);
~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
DOM::NodeImpl *endNode() const { return m_endNode; }
long endOffset() const { return m_endOffset; }
- DOM::DOMPosition previousCharacterPosition() const;
- static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
- DOM::DOMPosition nextCharacterPosition() const;
- static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-
+ DOM::DOMPosition basePosition() const;
+ DOM::DOMPosition extentPosition() const;
+ DOM::DOMPosition startPosition() const;
+ DOM::DOMPosition endPosition() const;
+
void setNeedsLayout(bool flag=true);
void clearModifyBias() { m_modifyBiasSet = false; }
- bool isEmpty() const;
+ bool isEmpty() const { return state() == NONE; }
+ bool notEmpty() const { return !isEmpty(); }
DOM::Range toRange() const;
@@ -124,7 +126,6 @@ private:
void setEndNode(DOM::NodeImpl *);
void setEndOffset(long);
- bool inRenderedContent(const DOM::DOMPosition &);
bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/html/html_elementimpl.cpp b/WebCore/khtml/html/html_elementimpl.cpp
index aa756a6..a0476ea 100644
--- a/WebCore/khtml/html/html_elementimpl.cpp
+++ b/WebCore/khtml/html/html_elementimpl.cpp
@@ -819,8 +819,16 @@ bool HTMLElementImpl::isFocusable() const
return isContentEditable() && !parent()->isContentEditable();
}
-bool HTMLElementImpl::isContentEditable() const {
- return contentEditable() == "true";
+bool HTMLElementImpl::isContentEditable() const
+{
+ if (!renderer()) {
+ if (parentNode())
+ return parentNode()->isContentEditable();
+ else
+ return false;
+ }
+
+ return renderer()->style()->userModify() == READ_WRITE;
}
DOMString HTMLElementImpl::contentEditable() const {
diff --git a/WebCore/khtml/khtml_part.cpp b/WebCore/khtml/khtml_part.cpp
index d3708e8..695c9e0 100644
--- a/WebCore/khtml/khtml_part.cpp
+++ b/WebCore/khtml/khtml_part.cpp
@@ -99,7 +99,6 @@ using namespace DOM;
#endif
using khtml::Decoder;
-using khtml::DeleteKeyCommand;
using khtml::DeleteSelectionCommand;
using khtml::EditCommand;
using khtml::InlineTextBox;
@@ -2472,6 +2471,7 @@ const KHTMLSelection &KHTMLPart::selection() const
void KHTMLPart::setSelection(const KHTMLSelection &s)
{
if (d->m_selection != s) {
+ clearCaretRectIfNeeded();
d->m_selection = s;
notifySelectionChanged();
}
@@ -2486,6 +2486,7 @@ void KHTMLPart::takeSelectionFrom(const EditCommand &cmd, bool useEndingSelectio
s = cmd.startingSelection();
if (d->m_selection != s) {
+ clearCaretRectIfNeeded();
d->m_selection = s;
notifySelectionChanged(false);
}
@@ -2493,6 +2494,7 @@ void KHTMLPart::takeSelectionFrom(const EditCommand &cmd, bool useEndingSelectio
void KHTMLPart::clearSelection()
{
+ clearCaretRectIfNeeded();
d->m_selection = KHTMLSelection();
notifySelectionChanged();
}
@@ -2505,6 +2507,7 @@ void KHTMLPart::deleteSelection()
void KHTMLPart::invalidateSelection()
{
+ clearCaretRectIfNeeded();
d->m_selection.setNeedsLayout();
notifySelectionChanged(false);
}
@@ -2513,19 +2516,29 @@ void KHTMLPart::setSelectionVisible(bool flag)
{
if (d->m_caretVisible == flag)
return;
-
+
+ clearCaretRectIfNeeded();
d->m_caretVisible = flag;
notifySelectionChanged();
}
void KHTMLPart::slotClearSelection()
{
+ clearCaretRectIfNeeded();
bool hadSelection = hasSelection();
d->m_selection.clear();
if (hadSelection)
notifySelectionChanged();
}
+void KHTMLPart::clearCaretRectIfNeeded()
+{
+ if (d->m_caretPaint) {
+ d->m_caretPaint = false;
+ d->m_selection.needsCaretRepaint();
+ }
+}
+
void KHTMLPart::notifySelectionChanged(bool endTyping)
{
// kill any caret blink timer now running
@@ -2541,10 +2554,6 @@ void KHTMLPart::notifySelectionChanged(bool endTyping)
d->m_caretPaint = true;
d->m_selection.needsCaretRepaint();
}
- else if (d->m_caretPaint) {
- d->m_caretPaint = false;
- d->m_selection.needsCaretRepaint();
- }
if (d->m_doc)
d->m_doc->updateSelection();
@@ -5101,10 +5110,12 @@ void KHTMLPart::appliedEditing(EditCommand &cmd)
void KHTMLPart::unappliedEditing(EditCommand &cmd)
{
+ TypingCommand::closeTyping(lastEditCommand());
+
#if APPLE_CHANGES
KWQ(this)->registerCommandForRedo(cmd);
#endif
- d->m_lastEditCommand = EditCommand();
+ d->m_lastEditCommand = EditCommand::emptyCommand();
}
void KHTMLPart::reappliedEditing(EditCommand &cmd)
@@ -5112,7 +5123,7 @@ void KHTMLPart::reappliedEditing(EditCommand &cmd)
#if APPLE_CHANGES
KWQ(this)->registerCommandForUndo(cmd);
#endif
- d->m_lastEditCommand = EditCommand();
+ d->m_lastEditCommand = EditCommand::emptyCommand();
}
void KHTMLPart::pasteHTMLString(const QString &HTMLString)
diff --git a/WebCore/khtml/khtml_part.h b/WebCore/khtml/khtml_part.h
index 4d55874..22678e5 100644
--- a/WebCore/khtml/khtml_part.h
+++ b/WebCore/khtml/khtml_part.h
@@ -1096,6 +1096,11 @@ private:
/**
* @internal
*/
+ void clearCaretRectIfNeeded();
+
+ /**
+ * @internal
+ */
void notifySelectionChanged(bool endTyping=true);
/**
diff --git a/WebCore/khtml/khtml_selection.cpp b/WebCore/khtml/khtml_selection.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/khtml_selection.cpp
+++ b/WebCore/khtml/khtml_selection.cpp
@@ -25,6 +25,7 @@
#include "khtml_selection.h"
+#include "htmltags.h"
#include "khtml_part.h"
#include "khtmlview.h"
#include "qevent.h"
@@ -38,6 +39,7 @@
#include "rendering/render_style.h"
#include "rendering/render_text.h"
#include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
#include "xml/dom_elementimpl.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
using DOM::DocumentImpl;
using DOM::DOMPosition;
using DOM::DOMString;
+using DOM::EditIterator;
using DOM::ElementImpl;
using DOM::Node;
using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
validate();
}
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
{
init();
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
validate();
}
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+ init();
+
+ setBaseNode(base.node());
+ setExtentNode(extent.node());
+ setBaseOffset(base.offset());
+ setExtentOffset(extent.offset());
+
+ validate();
+}
+
KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
{
init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(endNode());
setExtentOffset(endOffset());
}
- pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().nextCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(endNode(), endOffset());
+ pos = endPosition();
else
- pos = nextCharacterPosition();
+ pos = endPosition().nextCharacterPosition();
}
break;
case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(startNode());
setExtentOffset(startOffset());
}
- pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().previousCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(startNode(), startOffset());
+ pos = startPosition();
else
- pos = previousCharacterPosition();
+ pos = startPosition().previousCharacterPosition();
}
break;
case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
m_needsCaretLayout = flag;
}
-bool KHTMLSelection::isEmpty() const
-{
- return m_baseNode == 0 && m_extentNode == 0;
-}
-
Range KHTMLSelection::toRange() const
{
if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
if (m_needsCaretLayout) {
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (!inRenderedContent(pos)) {
+ if (!pos.inRenderedContent()) {
moveToRenderedContent();
}
layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
#endif
}
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
- return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return from;
-
- NodeImpl *node = from.node();
- long offset = from.offset() - 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->nextEditable() && !renderer->precedesLineBreak())
- offset--;
- assert(offset >= 0);
- return DOMPosition(renderer->element(), offset);
- }
- renderer = renderer->previousEditable();
- }
-
- // can't move the position
- return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
- return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return DOMPosition();
-
- NodeImpl *node = from.node();
- long offset = from.offset() + 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, 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 {
- // 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);
- }
-
- //
- // Look in next renderer(s)
- //
- renderer = renderer->nextEditable();
- while (renderer) {
- // 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);
- }
- else {
- return DOMPosition(renderer->element(), renderer->caretMinOffset());
- }
- renderer = renderer->nextEditable();
- }
-
- // can't move the position
- return from;
-}
-
bool KHTMLSelection::moveToRenderedContent()
{
if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
return false;
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (inRenderedContent(pos))
+ if (pos.inRenderedContent())
return true;
- // not currently rendered, try moving to next
- DOMPosition next = nextCharacterPosition(pos);
- if (next != pos) {
- moveTo(next);
+ // not currently rendered, try moving to prev
+ DOMPosition prev = pos.previousCharacterPosition();
+ if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(prev);
return true;
}
- // could not be moved to next, try prev
- DOMPosition prev = previousCharacterPosition(pos);
- if (prev != pos) {
- moveTo(prev);
+ // could not be moved to prev, try next
+ DOMPosition next = pos.nextCharacterPosition();
+ if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(next);
return true;
}
return false;
}
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
{
- if (pos.isEmpty())
- return false;
-
- long offset = pos.offset();
+ return DOMPosition(baseNode(), baseOffset());
+}
- RenderObject *renderer = pos.node()->renderer();
- if (!renderer)
- return false;
-
- if (renderer->isText() && !renderer->isBR()) {
- RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
- for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
- if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
- return true;
- }
- else 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;
- }
- }
- }
- else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
- return true;
- }
-
- return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+ return DOMPosition(extentNode(), extentOffset());
}
-
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+ return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+ return DOMPosition(endNode(), endOffset());
+}
+
bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2)
{
if (!n1 || !n2)
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
if (!startNode())
return;
- static int context = 5;
+ //static int context = 5;
- RenderObject *r = 0;
+ //RenderObject *r = 0;
fprintf(stderr, "KHTMLSelection =================\n");
-
+
+ if (startPosition() == endPosition()) {
+ DOMPosition pos = startPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 {
+ DOMPosition pos = endPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 = startPosition();
+ upstream = pos.equivalentUpstreamPosition();
+ downstream = pos.equivalentDownstreamPosition();
+ 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());
+ fprintf(stderr, "-----------------------------------\n");
+ }
+
+#if 0
int back = 0;
r = startNode()->renderer();
for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
else
break;
}
+#endif
fprintf(stderr, "================================\n");
}
diff --git a/WebCore/khtml/khtml_selection.h b/WebCore/khtml/khtml_selection.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/khtml_selection.h
+++ b/WebCore/khtml/khtml_selection.h
@@ -46,6 +46,7 @@ public:
KHTMLSelection();
KHTMLSelection(DOM::NodeImpl *node, long offset);
KHTMLSelection(const DOM::DOMPosition &);
+ KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
KHTMLSelection(const KHTMLSelection &);
~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
DOM::NodeImpl *endNode() const { return m_endNode; }
long endOffset() const { return m_endOffset; }
- DOM::DOMPosition previousCharacterPosition() const;
- static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
- DOM::DOMPosition nextCharacterPosition() const;
- static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-
+ DOM::DOMPosition basePosition() const;
+ DOM::DOMPosition extentPosition() const;
+ DOM::DOMPosition startPosition() const;
+ DOM::DOMPosition endPosition() const;
+
void setNeedsLayout(bool flag=true);
void clearModifyBias() { m_modifyBiasSet = false; }
- bool isEmpty() const;
+ bool isEmpty() const { return state() == NONE; }
+ bool notEmpty() const { return !isEmpty(); }
DOM::Range toRange() const;
@@ -124,7 +126,6 @@ private:
void setEndNode(DOM::NodeImpl *);
void setEndOffset(long);
- bool inRenderedContent(const DOM::DOMPosition &);
bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/rendering/bidi.cpp b/WebCore/khtml/rendering/bidi.cpp
index c4ad942..2235232 100644
--- a/WebCore/khtml/rendering/bidi.cpp
+++ b/WebCore/khtml/rendering/bidi.cpp
@@ -1542,6 +1542,10 @@ QRect RenderBlock::layoutInlineChildren(bool relayoutChildren)
setLinesAppended(false);
+ if (!firstLineBox() && element() && element()->containingEditableBlock() == element()) {
+ m_height += lineHeight(true);
+ }
+
return repaintRect;
#if BIDI_DEBUG > 1
diff --git a/WebCore/khtml/rendering/render_br.cpp b/WebCore/khtml/rendering/render_br.cpp
index e6fb2a0..aa0d9a6 100644
--- a/WebCore/khtml/rendering/render_br.cpp
+++ b/WebCore/khtml/rendering/render_br.cpp
@@ -107,11 +107,16 @@ long RenderBR::caretMaxOffset() const
return 1;
}
+unsigned long RenderBR::caretMaxRenderedOffset() const
+{
+ 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();
+ _h = lineHeight(false);
_x = xPos();
_y = yPos();
diff --git a/WebCore/khtml/rendering/render_br.h b/WebCore/khtml/rendering/render_br.h
index 2426821..998451f 100644
--- a/WebCore/khtml/rendering/render_br.h
+++ b/WebCore/khtml/rendering/render_br.h
@@ -61,6 +61,7 @@ public:
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
+ virtual unsigned long caretMaxRenderedOffset() const;
virtual void caretPos(int offset, bool override, int &_x, int &_y, int &_w, int &_h);
diff --git a/WebCore/khtml/rendering/render_flow.cpp b/WebCore/khtml/rendering/render_flow.cpp
index 4a7713c..e6fa455 100644
--- a/WebCore/khtml/rendering/render_flow.cpp
+++ b/WebCore/khtml/rendering/render_flow.cpp
@@ -476,7 +476,7 @@ int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf)
void RenderFlow::caretPos(int offset, bool override, int &_x, int &_y, int &width, int &height)
{
- if (firstChild() || style()->display() == INLINE) {
+ if (firstChild() || style()->display() == INLINE) {
// Do the normal calculation
RenderBox::caretPos(offset, override, _x, _y, width, height);
return;
@@ -492,7 +492,8 @@ void RenderFlow::caretPos(int offset, bool override, int &_x, int &_y, int &widt
// the caret size of an empty :first-line'd block is wrong, but I think we
// can live with that.
RenderStyle *currentStyle = style(true);
- height = currentStyle->fontMetrics().height();
+ //height = currentStyle->fontMetrics().height();
+ height = lineHeight(true);
width = 1;
// EDIT FIXME: This needs to account for text direction
@@ -518,6 +519,6 @@ void RenderFlow::caretPos(int offset, bool override, int &_x, int &_y, int &widt
int absx, absy;
absolutePosition(absx, absy, false);
- _x += absx;
- _y += absy;
+ _x += absx + paddingLeft() + borderLeft();
+ _y += absy + paddingTop() + borderTop();
}
diff --git a/WebCore/khtml/rendering/render_line.cpp b/WebCore/khtml/rendering/render_line.cpp
index 6e9c6dc..63fc074 100644
--- a/WebCore/khtml/rendering/render_line.cpp
+++ b/WebCore/khtml/rendering/render_line.cpp
@@ -90,6 +90,11 @@ long InlineBox::caretMaxOffset() const
return 1;
}
+unsigned long InlineBox::caretMaxRenderedOffset() const
+{
+ return 1;
+}
+
void InlineBox::dirtyLineBoxes()
{
markDirty();
diff --git a/WebCore/khtml/rendering/render_line.h b/WebCore/khtml/rendering/render_line.h
index b85c4a0..3b89d9d 100644
--- a/WebCore/khtml/rendering/render_line.h
+++ b/WebCore/khtml/rendering/render_line.h
@@ -119,6 +119,7 @@ public:
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
+ virtual unsigned long caretMaxRenderedOffset() const;
bool isDirty() const { return m_dirty; }
void markDirty(bool dirty=true) { m_dirty = dirty; }
diff --git a/WebCore/khtml/rendering/render_object.cpp b/WebCore/khtml/rendering/render_object.cpp
index 7afaa14..8a2dff6 100644
--- a/WebCore/khtml/rendering/render_object.cpp
+++ b/WebCore/khtml/rendering/render_object.cpp
@@ -217,34 +217,6 @@ 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())
@@ -286,7 +258,10 @@ bool RenderObject::isEditable() const
return style()->visibility() == VISIBLE &&
element() && element()->isContentEditable() &&
- (isReplaced() || isBR() || (textRenderer && textRenderer->firstTextBox()));
+ ((isBlockFlow() && !firstChild()) ||
+ isReplaced() ||
+ isBR() ||
+ (textRenderer && textRenderer->firstTextBox()));
}
RenderObject *RenderObject::nextEditable() const
@@ -2180,3 +2155,18 @@ int RenderObject::maximalOutlineSize(PaintAction p) const
return 0;
return static_cast<RenderCanvas*>(document()->renderer())->maximalOutlineSize();
}
+
+long RenderObject::caretMinOffset() const
+{
+ return 0;
+}
+
+long RenderObject::caretMaxOffset() const
+{
+ return 0;
+}
+
+unsigned long RenderObject::caretMaxRenderedOffset() const
+{
+ return 0;
+}
diff --git a/WebCore/khtml/rendering/render_object.h b/WebCore/khtml/rendering/render_object.h
index 5ac2664..3bdc19e 100644
--- a/WebCore/khtml/rendering/render_object.h
+++ b/WebCore/khtml/rendering/render_object.h
@@ -717,12 +717,10 @@ public:
// Convenience, to avoid repeating the code to dig down to get this.
QChar backslashAsCurrencySymbol() const;
- virtual long caretMinOffset() const { return 0; }
- virtual long caretMaxOffset() const { return 0; }
+ virtual long caretMinOffset() const;
+ virtual long caretMaxOffset() const;
+ virtual unsigned long caretMaxRenderedOffset() const;
- bool precedesLineBreak() const;
- bool followsLineBreak() const;
-
virtual void setPixmap(const QPixmap&, const QRect&, CachedImage *);
protected:
diff --git a/WebCore/khtml/rendering/render_replaced.cpp b/WebCore/khtml/rendering/render_replaced.cpp
index 9546550..bdb7385 100644
--- a/WebCore/khtml/rendering/render_replaced.cpp
+++ b/WebCore/khtml/rendering/render_replaced.cpp
@@ -121,6 +121,11 @@ long RenderReplaced::caretMaxOffset() const
return 1;
}
+unsigned long RenderReplaced::caretMaxRenderedOffset() const
+{
+ return 1;
+}
+
// -----------------------------------------------------------------------------
RenderWidget::RenderWidget(DOM::NodeImpl* node)
diff --git a/WebCore/khtml/rendering/render_replaced.h b/WebCore/khtml/rendering/render_replaced.h
index 0d44853..b01bbd3 100644
--- a/WebCore/khtml/rendering/render_replaced.h
+++ b/WebCore/khtml/rendering/render_replaced.h
@@ -54,7 +54,8 @@ public:
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
-
+ virtual unsigned long caretMaxRenderedOffset() const;
+
private:
int m_intrinsicWidth;
int m_intrinsicHeight;
diff --git a/WebCore/khtml/rendering/render_text.cpp b/WebCore/khtml/rendering/render_text.cpp
index 8e647b0..3cae19a 100644
--- a/WebCore/khtml/rendering/render_text.cpp
+++ b/WebCore/khtml/rendering/render_text.cpp
@@ -195,6 +195,11 @@ long InlineTextBox::caretMaxOffset() const
return m_start + m_len;
}
+unsigned long InlineTextBox::caretMaxRenderedOffset() const
+{
+ return m_start + m_len;
+}
+
#define LOCAL_WIDTH_BUF_SIZE 1024
FindSelectionResult InlineTextBox::checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineHeight)
@@ -319,11 +324,11 @@ RenderText::~RenderText()
void RenderText::detach()
{
if (!documentBeingDestroyed()) {
+ if (parent() && isBR())
+ parent()->dirtyLinesFromChangedChild(this);
if (firstTextBox())
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
box->remove();
- else if (parent() && isBR())
- parent()->dirtyLinesFromChangedChild(this);
}
deleteTextBoxes();
RenderObject::detach();
@@ -1453,6 +1458,14 @@ long RenderText::caretMaxOffset() const
return lastTextBox()->m_start + lastTextBox()->m_len;
}
+unsigned long RenderText::caretMaxRenderedOffset() const
+{
+ int l = 0;
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
+ l += box->m_len;
+ return l;
+}
+
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 9b62137..440003a 100644
--- a/WebCore/khtml/rendering/render_text.h
+++ b/WebCore/khtml/rendering/render_text.h
@@ -61,6 +61,7 @@ public:
uint start() const { return m_start; }
uint end() const { return m_len ? m_start+m_len-1 : m_start; }
+ uint len() const { return m_len; }
void offsetRun(int d) { m_start += d; }
@@ -92,6 +93,7 @@ public:
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
+ virtual unsigned long caretMaxRenderedOffset() const;
// Return before, after (offset set to max), or inside the text, at @p offset
FindSelectionResult checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineheight);
@@ -224,6 +226,7 @@ public:
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
+ virtual unsigned long caretMaxRenderedOffset() const;
#if APPLE_CHANGES
public:
diff --git a/WebCore/kwq/KWQComboBox.h b/WebCore/khtml/xml/dom_edititerator.cpp
similarity index 52%
copy from WebCore/kwq/KWQComboBox.h
copy to WebCore/khtml/xml/dom_edititerator.cpp
index b3712b4..22f7668 100644
--- a/WebCore/kwq/KWQComboBox.h
+++ b/WebCore/khtml/xml/dom_edititerator.cpp
@@ -23,62 +23,66 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef QCOMBOBOX_H_
-#define QCOMBOBOX_H_
+#include "dom_edititerator.h"
-#include "KWQWidget.h"
-#include "KWQStringList.h"
+#include "dom_nodeimpl.h"
-class QListBox;
+namespace DOM {
-#ifdef __OBJC__
- at class KWQComboBoxAdapter;
-#else
-class KWQComboBoxAdapter;
-#endif
-
-class QComboBox : public QWidget {
-public:
- QComboBox();
- ~QComboBox();
+DOMPosition EditIterator::peekPrevious() const
+{
+ DOMPosition pos = m_current;
- void clear();
- void appendItem(const QString &text);
-
- int currentItem() const { return _currentItem; }
- void setCurrentItem(int);
-
- QListBox *listBox() const { return 0; }
- void popup() { }
+ if (pos.isEmpty())
+ return pos;
- QSize sizeHint() const;
- QRect frameGeometry() const;
- void setFrameGeometry(const QRect &);
- int baselinePosition(int height) const;
- void setFont(const QFont &);
-
- void itemSelected();
+ if (pos.offset() <= 0) {
+ NodeImpl *prevNode = pos.node()->previousEditable();
+ if (prevNode)
+ pos = DOMPosition(prevNode, prevNode->maxOffset());
+ }
+ else {
+ pos = DOMPosition(pos.node(), pos.offset() - 1);
+ }
- virtual FocusPolicy focusPolicy() const;
+ return pos;
+}
- void setWritingDirection(QPainter::TextDirection);
-
- virtual void populate();
- void populateMenu();
+DOMPosition EditIterator::peekNext() const
+{
+ DOMPosition pos = m_current;
+
+ if (pos.isEmpty())
+ return pos;
-private:
- const int *dimensions() const;
+ if (pos.offset() >= pos.node()->maxOffset()) {
+ NodeImpl *nextNode = pos.node()->nextEditable();
+ if (nextNode)
+ pos = DOMPosition(nextNode, 0);
+ }
+ else {
+ pos = DOMPosition(pos.node(), pos.offset() + 1);
+ }
- mutable int _width;
- mutable bool _widthGood;
+ return pos;
+}
+
+bool EditIterator::atStart() const
+{
+ if (m_current.isEmpty())
+ return true;
- mutable int _currentItem;
+ return m_current.offset() == 0 &&
+ m_current.node()->previousEditable() == 0;
+}
- // A vector<QString> or QValueVector<QString> may be more efficient for large menus.
- QStringList _items;
- mutable bool _menuPopulated;
+bool EditIterator::atEnd() const
+{
+ if (m_current.isEmpty())
+ return true;
- KWQSignal _activated;
-};
+ return m_current.offset() == m_current.node()->maxOffset() &&
+ m_current.node()->nextEditable() == 0;
+}
-#endif
+} // namespace DOM
diff --git a/WebCore/kwq/KWQEditCommand.mm b/WebCore/khtml/xml/dom_edititerator.h
similarity index 62%
copy from WebCore/kwq/KWQEditCommand.mm
copy to WebCore/khtml/xml/dom_edititerator.h
index fffbe24..60a3928 100644
--- a/WebCore/kwq/KWQEditCommand.mm
+++ b/WebCore/khtml/xml/dom_edititerator.h
@@ -23,39 +23,39 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#import "KWQEditCommand.h"
+#ifndef _EditIterator_h_
+#define _EditIterator_h_
-#import "KWQAssertions.h"
-#import "htmlediting_impl.h"
+#include "dom_position.h"
-using khtml::EditCommandImpl;
+namespace DOM {
- at implementation KWQEditCommand
+class DOMPosition;
+class NodeImpl;
-- (id)initWithEditCommandImpl:(EditCommandImpl *)impl
+class EditIterator
{
- ASSERT(impl);
- [super init];
- m_impl = impl;
- impl->ref();
- return self;
-}
-
-- (void)dealloc
-{
- if (m_impl)
- m_impl->deref();
- [super dealloc];
-}
+public:
+ EditIterator() : m_current() {}
+ EditIterator(NodeImpl *node, long offset) : m_current(node, offset) {}
+ EditIterator(const DOMPosition &o) : m_current(o) {}
-+ (KWQEditCommand *)commandWithEditCommandImpl:(EditCommandImpl *)impl
-{
- return [[[KWQEditCommand alloc] initWithEditCommandImpl:impl] autorelease];
-}
+ DOMPosition current() const { return m_current; }
+ DOMPosition previous() { return m_current = peekPrevious(); }
+ DOMPosition next() { return m_current = peekNext(); }
+ DOMPosition peekPrevious() const;
+ DOMPosition peekNext() const;
-- (EditCommandImpl *)impl
-{
- return m_impl;
-}
+ void setPosition(const DOMPosition &pos) { m_current = pos; }
+
+ bool atStart() const;
+ bool atEnd() const;
+ bool isEmpty() const { return m_current.isEmpty(); }
+
+private:
+ DOMPosition m_current;
+};
+
+} // namespace DOM
- at end
\ No newline at end of file
+#endif // _EditIterator_h_
diff --git a/WebCore/khtml/xml/dom_nodeimpl.cpp b/WebCore/khtml/xml/dom_nodeimpl.cpp
index 710b851..6493a1e 100644
--- a/WebCore/khtml/xml/dom_nodeimpl.cpp
+++ b/WebCore/khtml/xml/dom_nodeimpl.cpp
@@ -37,12 +37,14 @@
#include <kglobal.h>
#include <kdebug.h>
+#include "rendering/render_object.h"
#include "rendering/render_text.h"
#include "ecma/kjs_binding.h"
#include "ecma/kjs_proxy.h"
#include "khtmlview.h"
#include "khtml_part.h"
+#include "khtml_selection.h"
#include "html/dtd.h"
@@ -1148,6 +1150,32 @@ bool NodeImpl::isReadOnly()
return false;
}
+NodeImpl *NodeImpl::previousEditable() const
+{
+ NodeImpl *node = traversePreviousNode();
+ while (node) {
+ if (!node->isContentEditable())
+ return 0;
+ if (node->hasChildNodes() == 0)
+ return node;
+ node = node->traversePreviousNode();
+ }
+ return 0;
+}
+
+NodeImpl *NodeImpl::nextEditable() const
+{
+ NodeImpl *node = traverseNextNode();
+ while (node) {
+ if (!node->isContentEditable())
+ return 0;
+ if (node->hasChildNodes() == 0)
+ return node;
+ node = node->traverseNextNode();
+ }
+ return 0;
+}
+
RenderObject * NodeImpl::previousRenderer()
{
for (NodeImpl *n = previousSibling(); n; n = n->previousSibling()) {
@@ -1294,6 +1322,11 @@ RenderObject *NodeImpl::createRenderer(RenderArena *arena, RenderStyle *style)
return 0;
}
+long NodeImpl::maxOffset() const
+{
+ return 1;
+}
+
long NodeImpl::caretMinOffset() const
{
return renderer() ? renderer()->caretMinOffset() : 0;
@@ -1304,6 +1337,71 @@ long NodeImpl::caretMaxOffset() const
return renderer() ? renderer()->caretMaxOffset() : 1;
}
+unsigned long NodeImpl::caretMaxRenderedOffset() const
+{
+ return renderer() ? renderer()->caretMaxRenderedOffset() : 1;
+}
+
+bool NodeImpl::isBlockFlow() const
+{
+ return renderer() && renderer()->isBlockFlow();
+}
+
+bool NodeImpl::isEditableBlock() const
+{
+ return isContentEditable() && isBlockFlow();
+}
+
+NodeImpl *NodeImpl::containingEditableBlock() const
+{
+ if (!isContentEditable())
+ return 0;
+
+ NodeImpl *n = const_cast<NodeImpl *>(this);
+ if (isEditableBlock())
+ return n;
+
+ while (1) {
+ n = n->parentNode();
+ if (!n || !n->isContentEditable())
+ break;
+ if (n->isBlockFlow() || n->id() == ID_BODY)
+ return n;
+ }
+ return 0;
+}
+
+NodeImpl *NodeImpl::rootEditableBlock() const
+{
+ if (!isContentEditable())
+ return 0;
+
+ NodeImpl *n = const_cast<NodeImpl *>(this);
+ NodeImpl *result = n->isEditableBlock() ? n : 0;
+ while (1) {
+ n = n->parentNode();
+ if (!n || !n->isContentEditable())
+ break;
+ if (n->id() == ID_BODY) {
+ result = n;
+ break;
+ }
+ if (n->isBlockFlow())
+ result = n;
+ }
+ return result;
+}
+
+bool NodeImpl::inSameRootEditableBlock(NodeImpl *n)
+{
+ return n ? rootEditableBlock() == n->rootEditableBlock() : false;
+}
+
+bool NodeImpl::inSameContainingEditableBlock(NodeImpl *n)
+{
+ return n ? containingEditableBlock() == n->containingEditableBlock() : false;
+}
+
//-------------------------------------------------------------------------
NodeBaseImpl::NodeBaseImpl(DocumentPtr *doc)
@@ -1938,6 +2036,12 @@ void NodeBaseImpl::setFocus(bool received)
NodeImpl::setFocus(received);
+ if (received && isEditableBlock() && !hasChildNodes()) {
+ KHTMLPart *part = getDocument()->part();
+ part->setSelection(KHTMLSelection(this, 0));
+ fprintf(stderr, "place caret in me\n");
+ }
+
// note that we need to recalc the style
setChanged();
}
diff --git a/WebCore/khtml/xml/dom_nodeimpl.h b/WebCore/khtml/xml/dom_nodeimpl.h
index ed8b8f7..b4692fc 100644
--- a/WebCore/khtml/xml/dom_nodeimpl.h
+++ b/WebCore/khtml/xml/dom_nodeimpl.h
@@ -125,6 +125,7 @@ public:
virtual bool isTextNode() const { return false; }
virtual bool isDocumentNode() const { return false; }
virtual bool isXMLElementNode() const { return false; }
+ bool isBlockFlow() const;
// Used by <form> elements to indicate a malformed state of some kind, typically
// used to keep from applying the bottom margin of the form.
@@ -159,6 +160,13 @@ public:
*/
NodeImpl *previousLeafNode() const;
+ bool isEditableBlock() const;
+ NodeImpl *containingEditableBlock() const;
+ NodeImpl *rootEditableBlock() const;
+
+ bool inSameRootEditableBlock(NodeImpl *);
+ bool inSameContainingEditableBlock(NodeImpl *);
+
// used by the parser. Doesn't do as many error checkings as
// appendChild(), and returns the node into which will be parsed next.
virtual NodeImpl *addChild(NodeImpl *newChild);
@@ -314,6 +322,10 @@ public:
DocumentPtr *docPtr() const { return document; }
+ NodeImpl *previousEditable() const;
+ NodeImpl *nextEditable() const;
+ //bool isEditable() const;
+
khtml::RenderObject *renderer() const { return m_render; }
khtml::RenderObject *nextRenderer();
khtml::RenderObject *previousRenderer();
@@ -324,8 +336,10 @@ public:
bool isAncestor( NodeImpl *other );
virtual bool childAllowed( NodeImpl *newChild );
+ virtual long maxOffset() const;
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
+ virtual unsigned long caretMaxRenderedOffset() const;
#ifndef NDEBUG
virtual void dump(QTextStream *stream, QString ind = "") const;
diff --git a/WebCore/khtml/xml/dom_position.cpp b/WebCore/khtml/xml/dom_position.cpp
new file mode 100644
index 0000000..cee8535
--- /dev/null
+++ b/WebCore/khtml/xml/dom_position.cpp
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dom_position.h"
+
+#include "htmltags.h"
+#include "rendering/render_line.h"
+#include "rendering/render_object.h"
+#include "rendering/render_style.h"
+#include "rendering/render_text.h"
+#include "xml/dom_edititerator.h"
+#include "xml/dom_nodeimpl.h"
+
+#if APPLE_CHANGES
+#include "KWQAssertions.h"
+#include "KWQLogging.h"
+#endif
+
+using DOM::DOMPosition;
+using DOM::EditIterator;
+using DOM::NodeImpl;
+using khtml::InlineBox;
+using khtml::InlineFlowBox;
+using khtml::InlineTextBox;
+using khtml::RenderObject;
+using khtml::RenderText;
+
+#if !APPLE_CHANGES
+#define ASSERT(assertion) ((void)0)
+#define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
+#define ASSERT_NOT_REACHED() ((void)0)
+#define LOG(channel, formatAndArgs...) ((void)0)
+#define ERROR(formatAndArgs...) ((void)0)
+#endif
+
+DOMPosition::DOMPosition(NodeImpl *node, long offset)
+ : m_node(0), m_offset(offset)
+{
+ if (node) {
+ m_node = node;
+ m_node->ref();
+ }
+};
+
+DOMPosition::DOMPosition(const DOMPosition &o)
+ : m_node(0), m_offset(o.offset())
+{
+ if (o.node()) {
+ m_node = o.node();
+ m_node->ref();
+ }
+}
+
+DOMPosition::~DOMPosition() {
+ if (m_node) {
+ m_node->deref();
+ }
+}
+
+DOMPosition &DOMPosition::operator=(const DOMPosition &o)
+{
+ if (m_node) {
+ m_node->deref();
+ }
+ m_node = o.node();
+ if (m_node) {
+ m_node->ref();
+ }
+
+ m_offset = o.offset();
+
+ return *this;
+}
+
+long DOMPosition::renderedOffset() const
+{
+ if (!node()->isTextNode())
+ return offset();
+
+ if (!node()->renderer())
+ return offset();
+
+ long result = 0;
+ RenderText *textRenderer = static_cast<RenderText *>(node()->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() < start)
+ return result;
+ if (offset() <= end) {
+ result += offset() - start;
+ return result;
+ }
+ result += box->m_len;
+ }
+ return result;
+}
+
+DOMPosition DOMPosition::previousCharacterPosition() const
+{
+ if (isEmpty())
+ return DOMPosition();
+
+ NodeImpl *fromRootEditableBlock = node()->rootEditableBlock();
+ EditIterator it(*this);
+
+ bool atStartOfLine = isFirstRenderedPositionOnLine();
+
+ while (!it.atStart()) {
+ DOMPosition pos = it.previous();
+
+ if (pos.node()->rootEditableBlock() != fromRootEditableBlock)
+ return *this;
+
+ if (atStartOfLine) {
+ if (pos.inRenderedContent())
+ return pos;
+ }
+ else if (rendersInDifferentPosition(pos))
+ return pos;
+ }
+
+ return *this;
+}
+
+DOMPosition DOMPosition::nextCharacterPosition() const
+{
+ if (isEmpty())
+ return DOMPosition();
+
+ NodeImpl *fromRootEditableBlock = node()->rootEditableBlock();
+ EditIterator it(*this);
+
+ bool atEndOfLine = isLastRenderedPositionOnLine();
+
+ while (!it.atEnd()) {
+ DOMPosition pos = it.next();
+
+ if (pos.node()->rootEditableBlock() != fromRootEditableBlock)
+ return *this;
+
+ if (atEndOfLine) {
+ if (pos.inRenderedContent())
+ return pos;
+ }
+ else if (rendersInDifferentPosition(pos))
+ return pos;
+ }
+
+ return *this;
+}
+
+DOMPosition DOMPosition::equivalentUpstreamPosition() const
+{
+ if (!node())
+ return DOMPosition();
+
+ if (!node()->isTextNode() && offset() > node()->caretMinOffset())
+ return *this;
+
+ NodeImpl *block = node()->containingEditableBlock();
+
+ EditIterator it(*this);
+ DOMPosition prev = it.peekPrevious();
+ if (validUpstreamDownstreamPosition() && prev.validUpstreamDownstreamPosition()) {
+ if (node() == prev.node())
+ return *this;
+ else
+ return prev;
+ }
+ while (!it.atStart()) {
+ it.previous();
+ if (it.current().validUpstreamDownstreamPosition())
+ return it.current();
+ if (block != it.current().node()->containingEditableBlock())
+ return it.next();
+ }
+ return *this;
+}
+
+DOMPosition DOMPosition::equivalentDownstreamPosition() const
+{
+ if (!node())
+ return DOMPosition();
+
+ if (!node()->isTextNode() && offset() < node()->caretMaxOffset())
+ return *this;
+
+ NodeImpl *block = node()->containingEditableBlock();
+
+ EditIterator it(*this);
+ DOMPosition next = it.peekNext();
+ if (validUpstreamDownstreamPosition() && next.validUpstreamDownstreamPosition()) {
+ if (node() == next.node())
+ return *this;
+ else
+ return next;
+ }
+ while (!it.atEnd()) {
+ if (it.next().validUpstreamDownstreamPosition())
+ return it.current();
+ if (block != it.current().node()->containingEditableBlock())
+ return it.previous();
+ }
+ return *this;
+}
+
+bool DOMPosition::validUpstreamDownstreamPosition() const
+{
+ if (isEmpty())
+ return false;
+
+ RenderObject *renderer = node()->renderer();
+ if (!renderer || !renderer->isEditable())
+ return false;
+
+ if (renderer->style()->visibility() != khtml::VISIBLE)
+ return false;
+
+ if (renderer->isBR() || renderer->isBlockFlow())
+ return true;
+
+ if (renderer->isText()) {
+ RenderText *textRenderer = static_cast<RenderText *>(renderer);
+ InlineTextBox *lastTextBox = textRenderer->lastTextBox();
+ for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+ if (offset() >= box->m_start) {
+ if (box == lastTextBox) {
+ if (offset() <= box->m_start + box->m_len)
+ return true;
+ }
+ else if (offset() < box->m_start + box->m_len)
+ return true;
+ }
+ else 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;
+ }
+ }
+ return false;
+ }
+
+ if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset())
+ return true;
+
+ return false;
+}
+
+bool DOMPosition::inRenderedContent() const
+{
+ if (isEmpty())
+ return false;
+
+ RenderObject *renderer = node()->renderer();
+ if (!renderer || !renderer->isEditable())
+ return false;
+
+ if (renderer->style()->visibility() != khtml::VISIBLE)
+ return false;
+
+ if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) {
+ return offset() == 0;
+ }
+ else if (renderer->isText()) {
+ RenderText *textRenderer = static_cast<RenderText *>(renderer);
+ for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+ if (offset() >= box->m_start && offset() <= box->m_start + box->m_len) {
+ return true;
+ }
+ else 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;
+ }
+ }
+ }
+ else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) {
+ // don't return containing editable blocks unless they are empty
+ if (node()->containingEditableBlock() == node() && node()->firstChild())
+ return false;
+ return true;
+ }
+
+ return false;
+}
+
+
+static InlineBox *inlineBoxForRenderer(RenderObject *renderer, long offset)
+{
+ if (!renderer)
+ return 0;
+
+ if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox())
+ return static_cast<RenderText *>(renderer)->firstTextBox();
+
+ if (renderer->isText()) {
+ RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
+ if (textRenderer->isBR() && textRenderer->firstTextBox())
+ return textRenderer->firstTextBox();
+
+ for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+ if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
+ return box;
+ }
+ else 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 box->prevTextBox() ? box->prevTextBox() : textRenderer->firstTextBox();
+ }
+ }
+ }
+ else {
+ return renderer->inlineBoxWrapper();
+ }
+
+ return 0;
+}
+
+static bool renderersOnDifferentLine(RenderObject *r1, long o1, RenderObject *r2, long o2)
+{
+ InlineBox *b1 = inlineBoxForRenderer(r1, o1);
+ InlineBox *b2 = inlineBoxForRenderer(r2, o2);
+
+ if (b1 && b2 && b1->root() != b2->root())
+ return true;
+
+ return false;
+}
+
+static NodeImpl *nextRenderedEditable(NodeImpl *node)
+{
+ while (1) {
+ node = node->nextEditable();
+ if (!node)
+ return 0;
+ if (inlineBoxForRenderer(node->renderer(), 0))
+ return node;
+ }
+ return 0;
+}
+
+static NodeImpl *previousRenderedEditable(NodeImpl *node)
+{
+ while (1) {
+ node = node->previousEditable();
+ if (!node)
+ return 0;
+ if (inlineBoxForRenderer(node->renderer(), 0))
+ return node;
+ }
+ return 0;
+}
+
+bool DOMPosition::inRenderedText() const
+{
+ if (!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 DOMPosition::rendersOnSameLine(const DOMPosition &pos) const
+{
+ if (isEmpty() || pos.isEmpty())
+ return false;
+
+ if (node() == pos.node() && offset() == pos.offset())
+ return true;
+
+ if (node()->containingEditableBlock() != pos.node()->containingEditableBlock())
+ return false;
+
+ RenderObject *renderer = node()->renderer();
+ if (!renderer)
+ return false;
+
+ RenderObject *posRenderer = pos.node()->renderer();
+ if (!posRenderer)
+ return false;
+
+ if (renderer->style()->visibility() != khtml::VISIBLE ||
+ posRenderer->style()->visibility() != khtml::VISIBLE)
+ return false;
+
+ return renderersOnDifferentLine(renderer, offset(), posRenderer, pos.offset());
+}
+
+bool DOMPosition::rendersInDifferentPosition(const DOMPosition &pos) const
+{
+ if (isEmpty() || pos.isEmpty())
+ return false;
+
+ RenderObject *renderer = node()->renderer();
+ if (!renderer)
+ return false;
+
+ RenderObject *posRenderer = pos.node()->renderer();
+ if (!posRenderer)
+ return false;
+
+ if (renderer->style()->visibility() != khtml::VISIBLE ||
+ posRenderer->style()->visibility() != khtml::VISIBLE)
+ return false;
+
+ if (node() == pos.node()) {
+ if (node()->id() == ID_BR)
+ return false;
+
+ if (offset() == pos.offset())
+ return false;
+
+ if (!node()->isTextNode() && !pos.node()->isTextNode()) {
+ if (offset() != pos.offset())
+ return true;
+ }
+ }
+
+ if (node()->id() == ID_BR && pos.inRenderedContent())
+ return true;
+
+ if (pos.node()->id() == ID_BR && inRenderedContent())
+ return true;
+
+ if (node()->containingEditableBlock() != pos.node()->containingEditableBlock())
+ return true;
+
+ if (node()->isTextNode() && !inRenderedText())
+ return false;
+
+ if (pos.node()->isTextNode() && !pos.inRenderedText())
+ return false;
+
+ long thisRenderedOffset = renderedOffset();
+ long posRenderedOffset = pos.renderedOffset();
+
+ if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset)
+ return false;
+
+ LOG(Editing, "onDifferentLine: %s\n", renderersOnDifferentLine(renderer, offset(), posRenderer, pos.offset()) ? "YES" : "NO");
+ LOG(Editing, "renderer: %p [%p]\n", renderer, inlineBoxForRenderer(renderer, offset()));
+ LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset);
+ LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, inlineBoxForRenderer(posRenderer, pos.offset()));
+ LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset);
+ LOG(Editing, "node min/max: %d:%d\n", node()->caretMinOffset(), node()->caretMaxRenderedOffset());
+ LOG(Editing, "pos node min/max: %d:%d\n", pos.node()->caretMinOffset(), pos.node()->caretMaxRenderedOffset());
+ LOG(Editing, "----------------------------------------------------------------------\n");
+
+ InlineBox *b1 = inlineBoxForRenderer(renderer, offset());
+ InlineBox *b2 = inlineBoxForRenderer(posRenderer, pos.offset());
+
+ if (!b1 || !b2) {
+ return false;
+ }
+
+ if (b1->root() != b2->root()) {
+ return true;
+ }
+
+ if (nextRenderedEditable(node()) == pos.node() &&
+ thisRenderedOffset == (long)node()->caretMaxRenderedOffset() && posRenderedOffset == 0) {
+ return false;
+ }
+
+ if (previousRenderedEditable(node()) == pos.node() &&
+ thisRenderedOffset == 0 && posRenderedOffset == (long)pos.node()->caretMaxRenderedOffset()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool DOMPosition::isFirstRenderedPositionOnLine() const
+{
+ if (isEmpty())
+ return false;
+
+ RenderObject *renderer = node()->renderer();
+ if (!renderer)
+ return false;
+
+ if (renderer->style()->visibility() != khtml::VISIBLE)
+ return false;
+
+ DOMPosition pos(node(), offset());
+ EditIterator it(pos);
+ while (!it.atStart()) {
+ it.previous();
+ if (it.current().inRenderedContent())
+ return renderersOnDifferentLine(renderer, offset(), it.current().node()->renderer(), it.current().offset());
+ }
+
+ return true;
+}
+
+bool DOMPosition::isLastRenderedPositionOnLine() const
+{
+ if (isEmpty())
+ return false;
+
+ RenderObject *renderer = node()->renderer();
+ if (!renderer)
+ return false;
+
+ if (renderer->style()->visibility() != khtml::VISIBLE)
+ return false;
+
+ if (node()->id() == ID_BR)
+ return true;
+
+ DOMPosition pos(node(), offset());
+ EditIterator it(pos);
+ while (!it.atEnd()) {
+ it.next();
+ if (it.current().inRenderedContent())
+ return renderersOnDifferentLine(renderer, offset(), it.current().node()->renderer(), it.current().offset());
+ }
+
+ return true;
+}
+
+bool DOMPosition::isLastRenderedPositionInEditableBlock() const
+{
+ if (isEmpty())
+ return false;
+
+ RenderObject *renderer = node()->renderer();
+ if (!renderer)
+ return false;
+
+ if (renderer->style()->visibility() != khtml::VISIBLE)
+ return false;
+
+ if (renderedOffset() != (long)node()->caretMaxRenderedOffset())
+ return false;
+
+ NodeImpl *next = node()->nextEditable();
+ return !next || !node()->inSameContainingEditableBlock(next);
+}
+
+bool DOMPosition::inFirstEditableInRootEditableBlock() const
+{
+ if (isEmpty() || !inRenderedContent())
+ return false;
+
+ EditIterator it(node(), offset());
+ while (!it.atStart()) {
+ if (it.previous().inRenderedContent())
+ return false;
+ }
+
+ return true;
+}
+
+bool DOMPosition::inLastEditableInRootEditableBlock() const
+{
+ if (isEmpty() || !inRenderedContent())
+ return false;
+
+ EditIterator it(node(), offset());
+ while (!it.atEnd()) {
+ if (it.next().inRenderedContent())
+ return false;
+ }
+
+ return true;
+}
+
+bool DOMPosition::inFirstEditableInContainingEditableBlock() const
+{
+ if (isEmpty() || !inRenderedContent())
+ return false;
+
+ NodeImpl *block = node()->containingEditableBlock();
+
+ EditIterator it(node(), offset());
+ while (!it.atStart()) {
+ it.previous();
+ if (!it.current().inRenderedContent())
+ continue;
+ return block != it.current().node()->containingEditableBlock();
+ }
+
+ return true;
+}
+
+bool DOMPosition::inLastEditableInContainingEditableBlock() const
+{
+ if (isEmpty() || !inRenderedContent())
+ return false;
+
+ NodeImpl *block = node()->containingEditableBlock();
+
+ EditIterator it(node(), offset());
+ while (!it.atEnd()) {
+ it.next();
+ if (!it.current().inRenderedContent())
+ continue;
+ return block != it.current().node()->containingEditableBlock();
+ }
+
+ return true;
+}
+
+
diff --git a/WebCore/khtml/dom/dom_position.h b/WebCore/khtml/xml/dom_position.h
similarity index 68%
rename from WebCore/khtml/dom/dom_position.h
rename to WebCore/khtml/xml/dom_position.h
index b38ac9c..8e700ee 100644
--- a/WebCore/khtml/dom/dom_position.h
+++ b/WebCore/khtml/xml/dom_position.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -41,8 +41,28 @@ public:
NodeImpl *node() const { return m_node; }
long offset() const { return m_offset; }
- bool isEmpty() const { return m_node == 0; }
+ long renderedOffset() const;
+ bool isEmpty() const { return m_node == 0; }
+ bool notEmpty() const { return m_node != 0; }
+
+ DOMPosition previousCharacterPosition() const;
+ DOMPosition nextCharacterPosition() const;
+ DOMPosition equivalentUpstreamPosition() const;
+ DOMPosition equivalentDownstreamPosition() const;
+ bool validUpstreamDownstreamPosition() const;
+ bool inRenderedContent() const;
+ bool inRenderedText() const;
+ bool rendersOnSameLine(const DOMPosition &pos) const;
+ bool rendersInDifferentPosition(const DOMPosition &pos) const;
+ bool isFirstRenderedPositionOnLine() const;
+ bool isLastRenderedPositionOnLine() const;
+ bool isLastRenderedPositionInEditableBlock() const;
+ bool inFirstEditableInRootEditableBlock() const;
+ bool inLastEditableInRootEditableBlock() const;
+ bool inFirstEditableInContainingEditableBlock() const;
+ bool inLastEditableInContainingEditableBlock() const;
+
DOMPosition &operator=(const DOMPosition &o);
friend bool operator==(const DOMPosition &a, const DOMPosition &b);
diff --git a/WebCore/kwq/KWQComboBox.h b/WebCore/khtml/xml/dom_positioniterator.cpp
similarity index 52%
copy from WebCore/kwq/KWQComboBox.h
copy to WebCore/khtml/xml/dom_positioniterator.cpp
index b3712b4..22f7668 100644
--- a/WebCore/kwq/KWQComboBox.h
+++ b/WebCore/khtml/xml/dom_positioniterator.cpp
@@ -23,62 +23,66 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef QCOMBOBOX_H_
-#define QCOMBOBOX_H_
+#include "dom_edititerator.h"
-#include "KWQWidget.h"
-#include "KWQStringList.h"
+#include "dom_nodeimpl.h"
-class QListBox;
+namespace DOM {
-#ifdef __OBJC__
- at class KWQComboBoxAdapter;
-#else
-class KWQComboBoxAdapter;
-#endif
-
-class QComboBox : public QWidget {
-public:
- QComboBox();
- ~QComboBox();
+DOMPosition EditIterator::peekPrevious() const
+{
+ DOMPosition pos = m_current;
- void clear();
- void appendItem(const QString &text);
-
- int currentItem() const { return _currentItem; }
- void setCurrentItem(int);
-
- QListBox *listBox() const { return 0; }
- void popup() { }
+ if (pos.isEmpty())
+ return pos;
- QSize sizeHint() const;
- QRect frameGeometry() const;
- void setFrameGeometry(const QRect &);
- int baselinePosition(int height) const;
- void setFont(const QFont &);
-
- void itemSelected();
+ if (pos.offset() <= 0) {
+ NodeImpl *prevNode = pos.node()->previousEditable();
+ if (prevNode)
+ pos = DOMPosition(prevNode, prevNode->maxOffset());
+ }
+ else {
+ pos = DOMPosition(pos.node(), pos.offset() - 1);
+ }
- virtual FocusPolicy focusPolicy() const;
+ return pos;
+}
- void setWritingDirection(QPainter::TextDirection);
-
- virtual void populate();
- void populateMenu();
+DOMPosition EditIterator::peekNext() const
+{
+ DOMPosition pos = m_current;
+
+ if (pos.isEmpty())
+ return pos;
-private:
- const int *dimensions() const;
+ if (pos.offset() >= pos.node()->maxOffset()) {
+ NodeImpl *nextNode = pos.node()->nextEditable();
+ if (nextNode)
+ pos = DOMPosition(nextNode, 0);
+ }
+ else {
+ pos = DOMPosition(pos.node(), pos.offset() + 1);
+ }
- mutable int _width;
- mutable bool _widthGood;
+ return pos;
+}
+
+bool EditIterator::atStart() const
+{
+ if (m_current.isEmpty())
+ return true;
- mutable int _currentItem;
+ return m_current.offset() == 0 &&
+ m_current.node()->previousEditable() == 0;
+}
- // A vector<QString> or QValueVector<QString> may be more efficient for large menus.
- QStringList _items;
- mutable bool _menuPopulated;
+bool EditIterator::atEnd() const
+{
+ if (m_current.isEmpty())
+ return true;
- KWQSignal _activated;
-};
+ return m_current.offset() == m_current.node()->maxOffset() &&
+ m_current.node()->nextEditable() == 0;
+}
-#endif
+} // namespace DOM
diff --git a/WebCore/kwq/KWQEditCommand.mm b/WebCore/khtml/xml/dom_positioniterator.h
similarity index 62%
copy from WebCore/kwq/KWQEditCommand.mm
copy to WebCore/khtml/xml/dom_positioniterator.h
index fffbe24..60a3928 100644
--- a/WebCore/kwq/KWQEditCommand.mm
+++ b/WebCore/khtml/xml/dom_positioniterator.h
@@ -23,39 +23,39 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#import "KWQEditCommand.h"
+#ifndef _EditIterator_h_
+#define _EditIterator_h_
-#import "KWQAssertions.h"
-#import "htmlediting_impl.h"
+#include "dom_position.h"
-using khtml::EditCommandImpl;
+namespace DOM {
- at implementation KWQEditCommand
+class DOMPosition;
+class NodeImpl;
-- (id)initWithEditCommandImpl:(EditCommandImpl *)impl
+class EditIterator
{
- ASSERT(impl);
- [super init];
- m_impl = impl;
- impl->ref();
- return self;
-}
-
-- (void)dealloc
-{
- if (m_impl)
- m_impl->deref();
- [super dealloc];
-}
+public:
+ EditIterator() : m_current() {}
+ EditIterator(NodeImpl *node, long offset) : m_current(node, offset) {}
+ EditIterator(const DOMPosition &o) : m_current(o) {}
-+ (KWQEditCommand *)commandWithEditCommandImpl:(EditCommandImpl *)impl
-{
- return [[[KWQEditCommand alloc] initWithEditCommandImpl:impl] autorelease];
-}
+ DOMPosition current() const { return m_current; }
+ DOMPosition previous() { return m_current = peekPrevious(); }
+ DOMPosition next() { return m_current = peekNext(); }
+ DOMPosition peekPrevious() const;
+ DOMPosition peekNext() const;
-- (EditCommandImpl *)impl
-{
- return m_impl;
-}
+ void setPosition(const DOMPosition &pos) { m_current = pos; }
+
+ bool atStart() const;
+ bool atEnd() const;
+ bool isEmpty() const { return m_current.isEmpty(); }
+
+private:
+ DOMPosition m_current;
+};
+
+} // namespace DOM
- at end
\ No newline at end of file
+#endif // _EditIterator_h_
diff --git a/WebCore/khtml/xml/dom_selection.cpp b/WebCore/khtml/xml/dom_selection.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/xml/dom_selection.cpp
+++ b/WebCore/khtml/xml/dom_selection.cpp
@@ -25,6 +25,7 @@
#include "khtml_selection.h"
+#include "htmltags.h"
#include "khtml_part.h"
#include "khtmlview.h"
#include "qevent.h"
@@ -38,6 +39,7 @@
#include "rendering/render_style.h"
#include "rendering/render_text.h"
#include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
#include "xml/dom_elementimpl.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
using DOM::DocumentImpl;
using DOM::DOMPosition;
using DOM::DOMString;
+using DOM::EditIterator;
using DOM::ElementImpl;
using DOM::Node;
using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
validate();
}
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
{
init();
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
validate();
}
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+ init();
+
+ setBaseNode(base.node());
+ setExtentNode(extent.node());
+ setBaseOffset(base.offset());
+ setExtentOffset(extent.offset());
+
+ validate();
+}
+
KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
{
init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(endNode());
setExtentOffset(endOffset());
}
- pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().nextCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(endNode(), endOffset());
+ pos = endPosition();
else
- pos = nextCharacterPosition();
+ pos = endPosition().nextCharacterPosition();
}
break;
case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
setExtentNode(startNode());
setExtentOffset(startOffset());
}
- pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+ pos = extentPosition().previousCharacterPosition();
}
else {
m_modifyBiasSet = false;
if (state() == RANGE)
- pos = DOMPosition(startNode(), startOffset());
+ pos = startPosition();
else
- pos = previousCharacterPosition();
+ pos = startPosition().previousCharacterPosition();
}
break;
case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
m_needsCaretLayout = flag;
}
-bool KHTMLSelection::isEmpty() const
-{
- return m_baseNode == 0 && m_extentNode == 0;
-}
-
Range KHTMLSelection::toRange() const
{
if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
if (m_needsCaretLayout) {
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (!inRenderedContent(pos)) {
+ if (!pos.inRenderedContent()) {
moveToRenderedContent();
}
layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
#endif
}
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
- return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return from;
-
- NodeImpl *node = from.node();
- long offset = from.offset() - 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->nextEditable() && !renderer->precedesLineBreak())
- offset--;
- assert(offset >= 0);
- return DOMPosition(renderer->element(), offset);
- }
- renderer = renderer->previousEditable();
- }
-
- // can't move the position
- return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
- return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
- if (!from.node())
- return DOMPosition();
-
- NodeImpl *node = from.node();
- long offset = from.offset() + 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, 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 {
- // 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);
- }
-
- //
- // Look in next renderer(s)
- //
- renderer = renderer->nextEditable();
- while (renderer) {
- // 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);
- }
- else {
- return DOMPosition(renderer->element(), renderer->caretMinOffset());
- }
- renderer = renderer->nextEditable();
- }
-
- // can't move the position
- return from;
-}
-
bool KHTMLSelection::moveToRenderedContent()
{
if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
return false;
DOMPosition pos = DOMPosition(startNode(), startOffset());
- if (inRenderedContent(pos))
+ if (pos.inRenderedContent())
return true;
- // not currently rendered, try moving to next
- DOMPosition next = nextCharacterPosition(pos);
- if (next != pos) {
- moveTo(next);
+ // not currently rendered, try moving to prev
+ DOMPosition prev = pos.previousCharacterPosition();
+ if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(prev);
return true;
}
- // could not be moved to next, try prev
- DOMPosition prev = previousCharacterPosition(pos);
- if (prev != pos) {
- moveTo(prev);
+ // could not be moved to prev, try next
+ DOMPosition next = pos.nextCharacterPosition();
+ if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+ moveTo(next);
return true;
}
return false;
}
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
{
- if (pos.isEmpty())
- return false;
-
- long offset = pos.offset();
+ return DOMPosition(baseNode(), baseOffset());
+}
- RenderObject *renderer = pos.node()->renderer();
- if (!renderer)
- return false;
-
- if (renderer->isText() && !renderer->isBR()) {
- RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
- for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
- if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
- return true;
- }
- else 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;
- }
- }
- }
- else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
- return true;
- }
-
- return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+ return DOMPosition(extentNode(), extentOffset());
}
-
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+ return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+ return DOMPosition(endNode(), endOffset());
+}
+
bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2)
{
if (!n1 || !n2)
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
if (!startNode())
return;
- static int context = 5;
+ //static int context = 5;
- RenderObject *r = 0;
+ //RenderObject *r = 0;
fprintf(stderr, "KHTMLSelection =================\n");
-
+
+ if (startPosition() == endPosition()) {
+ DOMPosition pos = startPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 {
+ DOMPosition pos = endPosition();
+ DOMPosition upstream = pos.equivalentUpstreamPosition();
+ DOMPosition downstream = pos.equivalentDownstreamPosition();
+ 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 = startPosition();
+ upstream = pos.equivalentUpstreamPosition();
+ downstream = pos.equivalentDownstreamPosition();
+ 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());
+ fprintf(stderr, "-----------------------------------\n");
+ }
+
+#if 0
int back = 0;
r = startNode()->renderer();
for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
else
break;
}
+#endif
fprintf(stderr, "================================\n");
}
diff --git a/WebCore/khtml/xml/dom_selection.h b/WebCore/khtml/xml/dom_selection.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/xml/dom_selection.h
+++ b/WebCore/khtml/xml/dom_selection.h
@@ -46,6 +46,7 @@ public:
KHTMLSelection();
KHTMLSelection(DOM::NodeImpl *node, long offset);
KHTMLSelection(const DOM::DOMPosition &);
+ KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
KHTMLSelection(const KHTMLSelection &);
~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
DOM::NodeImpl *endNode() const { return m_endNode; }
long endOffset() const { return m_endOffset; }
- DOM::DOMPosition previousCharacterPosition() const;
- static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
- DOM::DOMPosition nextCharacterPosition() const;
- static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-
+ DOM::DOMPosition basePosition() const;
+ DOM::DOMPosition extentPosition() const;
+ DOM::DOMPosition startPosition() const;
+ DOM::DOMPosition endPosition() const;
+
void setNeedsLayout(bool flag=true);
void clearModifyBias() { m_modifyBiasSet = false; }
- bool isEmpty() const;
+ bool isEmpty() const { return state() == NONE; }
+ bool notEmpty() const { return !isEmpty(); }
DOM::Range toRange() const;
@@ -124,7 +126,6 @@ private:
void setEndNode(DOM::NodeImpl *);
void setEndOffset(long);
- bool inRenderedContent(const DOM::DOMPosition &);
bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/xml/dom_stringimpl.cpp b/WebCore/khtml/xml/dom_stringimpl.cpp
index c8f67a7..a2c860b 100644
--- a/WebCore/khtml/xml/dom_stringimpl.cpp
+++ b/WebCore/khtml/xml/dom_stringimpl.cpp
@@ -191,10 +191,15 @@ DOMStringImpl *DOMStringImpl::split(uint pos)
bool DOMStringImpl::containsOnlyWhitespace() const
{
+ return containsOnlyWhitespace(0, l);
+}
+
+bool DOMStringImpl::containsOnlyWhitespace(unsigned int from, unsigned int len) const
+{
if (!s)
return true;
- for (uint i = 0; i < l; i++) {
+ for (uint i = from; i < len; i++) {
QChar c = s[i];
if (c.unicode() <= 0x7F) {
if (!isspace(c.unicode()))
diff --git a/WebCore/khtml/xml/dom_stringimpl.h b/WebCore/khtml/xml/dom_stringimpl.h
index af4eaab..ed9cfde 100644
--- a/WebCore/khtml/xml/dom_stringimpl.h
+++ b/WebCore/khtml/xml/dom_stringimpl.h
@@ -69,6 +69,7 @@ public:
khtml::Length toLength() const;
bool containsOnlyWhitespace() const;
+ bool containsOnlyWhitespace(unsigned int from, unsigned int len) const;
// ignores trailing garbage, unlike QString
int toInt(bool* ok=0) const;
diff --git a/WebCore/khtml/xml/dom_textimpl.cpp b/WebCore/khtml/xml/dom_textimpl.cpp
index 19bd751..344c6e6 100644
--- a/WebCore/khtml/xml/dom_textimpl.cpp
+++ b/WebCore/khtml/xml/dom_textimpl.cpp
@@ -180,6 +180,13 @@ DOMString CharacterDataImpl::nodeValue() const
return str;
}
+bool CharacterDataImpl::containsOnlyWhitespace(unsigned int from, unsigned int len) const
+{
+ if (str)
+ return str->containsOnlyWhitespace(from, len);
+ return true;
+}
+
bool CharacterDataImpl::containsOnlyWhitespace() const
{
if (str)
@@ -227,6 +234,11 @@ void CharacterDataImpl::checkCharDataOperation( const unsigned long offset, int
}
}
+long CharacterDataImpl::maxOffset() const
+{
+ return (long)length();
+}
+
long CharacterDataImpl::caretMinOffset() const
{
RenderText *r = static_cast<RenderText *>(renderer());
@@ -239,6 +251,12 @@ long CharacterDataImpl::caretMaxOffset() const
return r && r->isText() ? r->caretMaxOffset() : (long)length();
}
+unsigned long CharacterDataImpl::caretMaxRenderedOffset() const
+{
+ RenderText *r = static_cast<RenderText *>(renderer());
+ return r ? r->caretMaxRenderedOffset() : length();
+}
+
#ifndef NDEBUG
void CharacterDataImpl::dump(QTextStream *stream, QString ind) const
{
@@ -301,12 +319,12 @@ DOMString CommentImpl::toString() const
// ### allow having children in text nodes for entities, comments etc.
TextImpl::TextImpl(DocumentPtr *doc, const DOMString &_text)
- : CharacterDataImpl(doc, _text)
+ : CharacterDataImpl(doc, _text), m_rendererIsNeeded(false)
{
}
TextImpl::TextImpl(DocumentPtr *doc)
- : CharacterDataImpl(doc)
+ : CharacterDataImpl(doc), m_rendererIsNeeded(false)
{
}
@@ -371,6 +389,11 @@ NodeImpl *TextImpl::cloneNode(bool /*deep*/)
bool TextImpl::rendererIsNeeded(RenderStyle *style)
{
+ if (m_rendererIsNeeded) {
+ m_rendererIsNeeded = false;
+ return true;
+ }
+
if (!CharacterDataImpl::rendererIsNeeded(style)) {
return false;
}
@@ -378,7 +401,7 @@ bool TextImpl::rendererIsNeeded(RenderStyle *style)
if (!onlyWS) {
return true;
}
-
+
RenderObject *par = parentNode()->renderer();
if (par->isTable() || par->isTableRow() || par->isTableSection()) {
diff --git a/WebCore/khtml/xml/dom_textimpl.h b/WebCore/khtml/xml/dom_textimpl.h
index 6abbe30..e6f2469 100644
--- a/WebCore/khtml/xml/dom_textimpl.h
+++ b/WebCore/khtml/xml/dom_textimpl.h
@@ -54,6 +54,7 @@ public:
virtual void replaceData ( const unsigned long offset, const unsigned long count, const DOMString &arg, int &exceptioncode );
virtual bool containsOnlyWhitespace() const;
+ bool containsOnlyWhitespace(unsigned int from, unsigned int len) const;
// DOM methods overridden from parent classes
@@ -65,9 +66,11 @@ public:
DOMStringImpl *string() { return str; }
virtual void checkCharDataOperation( const unsigned long offset, int &exceptioncode );
+ virtual long maxOffset() const;
virtual long caretMinOffset() const;
virtual long caretMaxOffset() const;
-
+ virtual unsigned long caretMaxRenderedOffset() const;
+
#ifndef NDEBUG
virtual void dump(QTextStream *stream, QString ind = "") const;
#endif
@@ -135,6 +138,8 @@ public:
virtual bool childTypeAllowed( unsigned short type );
virtual DOMString toString() const;
+
+ void setRendererIsNeeded(bool flag=true) { m_rendererIsNeeded = flag; }
#if APPLE_CHANGES
static Text createInstance(TextImpl *impl);
@@ -142,6 +147,9 @@ public:
protected:
virtual TextImpl *createNew(DOMStringImpl *_str);
+
+private:
+ bool m_rendererIsNeeded;
};
// ----------------------------------------------------------------------------
diff --git a/WebCore/kwq/KWQAssertions.m b/WebCore/kwq/KWQAssertions.m
index 4c802b8..d1dbd36 100644
--- a/WebCore/kwq/KWQAssertions.m
+++ b/WebCore/kwq/KWQAssertions.m
@@ -100,7 +100,8 @@ void KWQLog(const char *file, int line, const char *function, KWQLogChannel *cha
return;
}
- fprintf(stderr, "- %s:%d %s - ", file, line, function);
+ if (channel->mask != 0x100) // kocienda does not want this output when logging editing
+ fprintf(stderr, "- %s:%d %s - ", file, line, function);
va_list args;
va_start(args, format);
vprintf_stderr_objc(format, args);
--
WebKit Debian packaging
More information about the Pkg-webkit-commits
mailing list