+2004-12-06 Ken Kocienda <kocienda@apple.com>
+
+ Reviewed by John
+
+ * khtml/dom/dom_string.cpp:
+ (DOM::DOMString::substring): Expose method already on DOMStrimgImpl.
+ * khtml/dom/dom_string.h: Ditto.
+ * khtml/editing/htmlediting.cpp:
+ (khtml::CompositeEditCommand::rebalanceWhitespace): New helper to create and execute a
+ RebalanceWhitespaceCommand instance.
+ (khtml::DeleteSelectionCommand::doApply): Call rebalanceWhitespace() after running command.
+ (khtml::InsertLineBreakCommand::doApply): Ditto.
+ (khtml::InsertParagraphSeparatorCommand::doApply): Ditto.
+ (khtml::InsertParagraphSeparatorInQuotedContentCommand::doApply): Ditto.
+ (khtml::InsertTextCommand::input): Ditto.
+ (khtml::RebalanceWhitespaceCommand::RebalanceWhitespaceCommand): New command.
+ (khtml::RebalanceWhitespaceCommand::~RebalanceWhitespaceCommand): Ditto.
+ (khtml::RebalanceWhitespaceCommand::doApply): Ditto.
+ (khtml::RebalanceWhitespaceCommand::doUnapply): Ditto.
+ (khtml::RebalanceWhitespaceCommand::preservesTypingStyle): Ditto.
+ (khtml::ReplaceSelectionCommand::completeHTMLReplacement): Ditto.
+ * khtml/editing/htmlediting.h: Ditto.
+ (khtml::RebalanceWhitespaceCommand::): Ditto.
+
2004-12-05 Darin Adler <darin@apple.com>
- fixed small problem in my check-in from yesterday
applyCommandToComposite(cmd);
}
+void CompositeEditCommand::rebalanceWhitespace()
+{
+ Selection selection = endingSelection();
+ if (selection.isCaretOrRange()) {
+ EditCommandPtr startCmd(new RebalanceWhitespaceCommand(document(), endingSelection().start()));
+ applyCommandToComposite(startCmd);
+ if (selection.isRange()) {
+ EditCommandPtr endCmd(new RebalanceWhitespaceCommand(document(), endingSelection().end()));
+ applyCommandToComposite(endCmd);
+ }
+ }
+}
+
NodeImpl *CompositeEditCommand::applyTypingStyle(NodeImpl *child) const
{
// FIXME: This function should share code with ApplyStyleCommand::applyStyleIfNeeded
setEndingSelection(m_endingPosition);
debugPosition("endingPosition ", m_endingPosition);
clearTransientState();
+ rebalanceWhitespace();
}
bool DeleteSelectionCommand::preservesTypingStyle() const
setEndingSelection(endingPosition);
}
+ rebalanceWhitespace();
}
//------------------------------------------------------------------------------------------
if (doneAfterDelete) {
document()->updateLayout();
setEndingSelection(endingSelection().start().downstream());
+ rebalanceWhitespace();
return;
}
pos = endingSelection().start().upstream();
// Put the selection right at the start of the added block.
setEndingSelection(Position(addedBlock, 0));
+ rebalanceWhitespace();
}
//------------------------------------------------------------------------------------------
// Put the selection right before the break.
setEndingSelection(Position(m_breakNode, 0));
+ rebalanceWhitespace();
}
//------------------------------------------------------------------------------------------
// a set number of spaces. This also seems to be the HTML editing convention.
for (int i = 0; i < spacesPerTab; i++) {
insertSpace(textNode, offset);
+ rebalanceWhitespace();
document()->updateLayout();
}
if (selectInsertedText)
else
setEndingSelection(Position(textNode, offset + 1));
m_charactersAdded++;
+ rebalanceWhitespace();
}
else {
const DOMString &existingText = textNode->data();
applyCommandToComposite(cmd);
}
+//------------------------------------------------------------------------------------------
+// RebalanceWhitespaceCommand
+
+RebalanceWhitespaceCommand::RebalanceWhitespaceCommand(DocumentImpl *document, const Position &pos)
+ : EditCommand(document), m_position(pos), m_upstreamOffset(InvalidOffset), m_downstreamOffset(InvalidOffset)
+{
+}
+
+RebalanceWhitespaceCommand::~RebalanceWhitespaceCommand()
+{
+}
+
+void RebalanceWhitespaceCommand::doApply()
+{
+ static DOMString space(" ");
+
+ if (m_position.isNull() || !m_position.node()->isTextNode())
+ return;
+
+ TextImpl *textNode = static_cast<TextImpl *>(m_position.node());
+ DOMString text = textNode->data();
+ if (text.length() == 0)
+ return;
+
+ // find upstream offset
+ long upstream = m_position.offset();
+ while (upstream > 0 && isWS(text[upstream - 1]) || isNBSP(text[upstream - 1])) {
+ upstream--;
+ m_upstreamOffset = upstream;
+ }
+
+ // find downstream offset
+ long downstream = m_position.offset();
+ while ((unsigned)downstream < text.length() && isWS(text[downstream]) || isNBSP(text[downstream])) {
+ downstream++;
+ m_downstreamOffset = downstream;
+ }
+
+ if (m_upstreamOffset == InvalidOffset && m_downstreamOffset == InvalidOffset)
+ return;
+
+ m_upstreamOffset = upstream;
+ m_downstreamOffset = downstream;
+ long length = m_downstreamOffset - m_upstreamOffset;
+
+ m_beforeString = text.substring(m_upstreamOffset, length);
+
+ // The following loop figures out a "rebalanced" whitespace string for any length
+ // string, and takes into account the special cases that need to handled for the
+ // start and end of strings (i.e. first and last character must be an nbsp.
+ long i = m_upstreamOffset;
+ while (i < m_downstreamOffset) {
+ long add = (m_downstreamOffset - i) % 3;
+ switch (add) {
+ case 0:
+ m_afterString += nonBreakingSpaceString();
+ m_afterString += space;
+ m_afterString += nonBreakingSpaceString();
+ add = 3;
+ break;
+ case 1:
+ if (i == 0 || (unsigned)i + 1 == text.length()) // at start or end of string
+ m_afterString += nonBreakingSpaceString();
+ else
+ m_afterString += space;
+ break;
+ case 2:
+ if ((unsigned)i + 2 == text.length()) {
+ // at end of string
+ m_afterString += nonBreakingSpaceString();
+ m_afterString += nonBreakingSpaceString();
+ }
+ else {
+ m_afterString += nonBreakingSpaceString();
+ m_afterString += space;
+ }
+ break;
+ }
+ i += add;
+ }
+
+ text.remove(m_upstreamOffset, length);
+ text.insert(m_afterString, m_upstreamOffset);
+}
+
+void RebalanceWhitespaceCommand::doUnapply()
+{
+ if (m_upstreamOffset == InvalidOffset && m_downstreamOffset == InvalidOffset)
+ return;
+
+ ASSERT(m_position.node()->isTextNode());
+ TextImpl *textNode = static_cast<TextImpl *>(m_position.node());
+ DOMString text = textNode->data();
+ text.remove(m_upstreamOffset, m_afterString.length());
+ text.insert(m_beforeString, m_upstreamOffset);
+}
+
+bool RebalanceWhitespaceCommand::preservesTypingStyle() const
+{
+ return true;
+}
+
//------------------------------------------------------------------------------------------
// RemoveCSSPropertyCommand
if (start.isNull() || !start.node()->inDocument() || end.isNull() || !end.node()->inDocument())
return;
m_selectReplacement ? setEndingSelection(Selection(start, end)) : setEndingSelection(end);
+ rebalanceWhitespace();
}
void ReplaceSelectionCommand::completeHTMLReplacement(NodeImpl *firstNodeInserted, NodeImpl *lastNodeInserted)
// Place the cursor after what was inserted, and mark misspellings in the inserted content.
setEndingSelection(end);
}
+ rebalanceWhitespace();
}
//------------------------------------------------------------------------------------------
void insertParagraphSeparator();
void insertTextIntoNode(DOM::TextImpl *node, long offset, const DOM::DOMString &text);
void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2);
+ void rebalanceWhitespace();
void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property);
void removeFullySelectedNode(DOM::NodeImpl *);
void removeNodeAttribute(DOM::ElementImpl *, int attribute);
bool m_smartMove;
};
+//------------------------------------------------------------------------------------------
+// RebalanceWhitespaceCommand
+
+class RebalanceWhitespaceCommand : public EditCommand
+{
+public:
+ RebalanceWhitespaceCommand(DOM::DocumentImpl *, const DOM::Position &);
+ virtual ~RebalanceWhitespaceCommand();
+
+ virtual void doApply();
+ virtual void doUnapply();
+
+private:
+ enum { InvalidOffset = -1 };
+
+ virtual bool preservesTypingStyle() const;
+
+ DOM::DOMString m_beforeString;
+ DOM::DOMString m_afterString;
+ DOM::Position m_position;
+ long m_upstreamOffset;
+ long m_downstreamOffset;
+};
+
//------------------------------------------------------------------------------------------
// RemoveCSSPropertyCommand