diff options
Diffstat (limited to 'WebCore/editing/InsertParagraphSeparatorCommand.cpp')
-rw-r--r-- | WebCore/editing/InsertParagraphSeparatorCommand.cpp | 332 |
1 files changed, 0 insertions, 332 deletions
diff --git a/WebCore/editing/InsertParagraphSeparatorCommand.cpp b/WebCore/editing/InsertParagraphSeparatorCommand.cpp deleted file mode 100644 index bcd2bfa..0000000 --- a/WebCore/editing/InsertParagraphSeparatorCommand.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2005, 2006 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 "config.h" -#include "InsertParagraphSeparatorCommand.h" - -#include "Document.h" -#include "Logging.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSPropertyNames.h" -#include "Text.h" -#include "htmlediting.h" -#include "HTMLElement.h" -#include "HTMLNames.h" -#include "InsertLineBreakCommand.h" -#include "RenderObject.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document *document, bool mustUseDefaultParagraphElement) - : CompositeEditCommand(document) - , m_mustUseDefaultParagraphElement(mustUseDefaultParagraphElement) -{ -} - -bool InsertParagraphSeparatorCommand::preservesTypingStyle() const -{ - return true; -} - -void InsertParagraphSeparatorCommand::calculateStyleBeforeInsertion(const Position &pos) -{ - // It is only important to set a style to apply later if we're at the boundaries of - // a paragraph. Otherwise, content that is moved as part of the work of the command - // will lend their styles to the new paragraph without any extra work needed. - VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY); - if (!isStartOfParagraph(visiblePos) && !isEndOfParagraph(visiblePos)) - return; - - m_style = styleAtPosition(pos); -} - -void InsertParagraphSeparatorCommand::applyStyleAfterInsertion(Node* originalEnclosingBlock) -{ - // Not only do we break out of header tags, but we also do not preserve the typing style, - // in order to match other browsers. - if (originalEnclosingBlock->hasTagName(h1Tag) || - originalEnclosingBlock->hasTagName(h2Tag) || - originalEnclosingBlock->hasTagName(h3Tag) || - originalEnclosingBlock->hasTagName(h4Tag) || - originalEnclosingBlock->hasTagName(h5Tag)) - return; - - if (!m_style) - return; - - computedStyle(endingSelection().start().node())->diff(m_style.get()); - if (m_style->length() > 0) - applyStyle(m_style.get()); -} - -bool InsertParagraphSeparatorCommand::shouldUseDefaultParagraphElement(Node* enclosingBlock) const -{ - if (m_mustUseDefaultParagraphElement) - return true; - - // Assumes that if there was a range selection, it was already deleted. - if (!isEndOfBlock(endingSelection().visibleStart())) - return false; - - return enclosingBlock->hasTagName(h1Tag) || - enclosingBlock->hasTagName(h2Tag) || - enclosingBlock->hasTagName(h3Tag) || - enclosingBlock->hasTagName(h4Tag) || - enclosingBlock->hasTagName(h5Tag); -} - -void InsertParagraphSeparatorCommand::doApply() -{ - bool splitText = false; - if (endingSelection().isNone()) - return; - - Position pos = endingSelection().start(); - - EAffinity affinity = endingSelection().affinity(); - - // Delete the current selection. - if (endingSelection().isRange()) { - calculateStyleBeforeInsertion(pos); - deleteSelection(false, true); - pos = endingSelection().start(); - affinity = endingSelection().affinity(); - } - - // FIXME: The rangeCompliantEquivalent conversion needs to be moved into enclosingBlock. - Node* startBlock = enclosingBlock(rangeCompliantEquivalent(pos).node()); - Position canonicalPos = VisiblePosition(pos).deepEquivalent(); - if (!startBlock || !startBlock->parentNode() || - isTableCell(startBlock) || - startBlock->hasTagName(formTag) || - canonicalPos.node()->renderer() && canonicalPos.node()->renderer()->isTable() || - canonicalPos.node()->hasTagName(hrTag)) { - applyCommandToComposite(InsertLineBreakCommand::create(document())); - return; - } - - // Use the leftmost candidate. - pos = pos.upstream(); - if (!pos.isCandidate()) - pos = pos.downstream(); - - // Adjust the insertion position after the delete - pos = positionAvoidingSpecialElementBoundary(pos); - VisiblePosition visiblePos(pos, affinity); - calculateStyleBeforeInsertion(pos); - - //--------------------------------------------------------------------- - // Handle special case of typing return on an empty list item - if (breakOutOfEmptyListItem()) - return; - - //--------------------------------------------------------------------- - // Prepare for more general cases. - // FIXME: We shouldn't peel off the node here because then we lose track of - // the fact that it's the node that belongs to an editing position and - // not a rangeCompliantEquivalent. - Node *startNode = pos.node(); - - bool isFirstInBlock = isStartOfBlock(visiblePos); - bool isLastInBlock = isEndOfBlock(visiblePos); - bool nestNewBlock = false; - - // Create block to be inserted. - RefPtr<Node> blockToInsert; - if (startBlock == startBlock->rootEditableElement()) { - blockToInsert = static_pointer_cast<Node>(createDefaultParagraphElement(document())); - nestNewBlock = true; - } else if (shouldUseDefaultParagraphElement(startBlock)) - blockToInsert = static_pointer_cast<Node>(createDefaultParagraphElement(document())); - else - blockToInsert = startBlock->cloneNode(false); - - //--------------------------------------------------------------------- - // Handle case when position is in the last visible position in its block, - // including when the block is empty. - if (isLastInBlock) { - if (nestNewBlock) { - if (isFirstInBlock && !lineBreakExistsAtPosition(visiblePos)) { - // The block is empty. Create an empty block to - // represent the paragraph that we're leaving. - RefPtr<Node> extraBlock = createDefaultParagraphElement(document()); - appendNode(extraBlock.get(), startBlock); - appendBlockPlaceholder(extraBlock.get()); - } - appendNode(blockToInsert.get(), startBlock); - } else - insertNodeAfter(blockToInsert.get(), startBlock); - - appendBlockPlaceholder(blockToInsert.get()); - setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM)); - applyStyleAfterInsertion(startBlock); - return; - } - - //--------------------------------------------------------------------- - // Handle case when position is in the first visible position in its block, and - // similar case where previous position is in another, presumeably nested, block. - if (isFirstInBlock || !inSameBlock(visiblePos, visiblePos.previous())) { - Node *refNode; - if (isFirstInBlock && !nestNewBlock) - refNode = startBlock; - else if (pos.node() == startBlock && nestNewBlock) { - refNode = startBlock->childNode(pos.offset()); - ASSERT(refNode); // must be true or we'd be in the end of block case - } else - refNode = pos.node(); - - // find ending selection position easily before inserting the paragraph - pos = pos.downstream(); - - insertNodeBefore(blockToInsert.get(), refNode); - appendBlockPlaceholder(blockToInsert.get()); - setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM)); - applyStyleAfterInsertion(startBlock); - setEndingSelection(Selection(pos, DOWNSTREAM)); - return; - } - - //--------------------------------------------------------------------- - // Handle the (more complicated) general case, - - // All of the content in the current block after visiblePos is - // about to be wrapped in a new paragraph element. Add a br before - // it if visiblePos is at the start of a paragraph so that the - // content will move down a line. - if (isStartOfParagraph(visiblePos)) { - RefPtr<Element> br = createBreakElement(document()); - insertNodeAt(br.get(), pos); - pos = positionAfterNode(br.get()); - } - - // Move downstream. Typing style code will take care of carrying along the - // style of the upstream position. - pos = pos.downstream(); - startNode = pos.node(); - - // Build up list of ancestors in between the start node and the start block. - Vector<Node*> ancestors; - if (startNode != startBlock) - for (Node* n = startNode->parentNode(); n && n != startBlock; n = n->parentNode()) - ancestors.append(n); - - // Make sure we do not cause a rendered space to become unrendered. - // FIXME: We need the affinity for pos, but pos.downstream() does not give it - Position leadingWhitespace = pos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY); - // FIXME: leadingWhitespacePosition is returning the position before preserved newlines for positions - // after the preserved newline, causing the newline to be turned into a nbsp. - if (leadingWhitespace.isNotNull()) { - Text* textNode = static_cast<Text*>(leadingWhitespace.node()); - ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); - replaceTextInNode(textNode, leadingWhitespace.offset(), 1, nonBreakingSpaceString()); - } - - // Split at pos if in the middle of a text node. - if (startNode->isTextNode()) { - Text *textNode = static_cast<Text *>(startNode); - bool atEnd = (unsigned)pos.offset() >= textNode->length(); - if (pos.offset() > 0 && !atEnd) { - splitTextNode(textNode, pos.offset()); - pos = Position(startNode, 0); - visiblePos = VisiblePosition(pos); - splitText = true; - } - } - - // Put the added block in the tree. - if (nestNewBlock) - appendNode(blockToInsert.get(), startBlock); - else - insertNodeAfter(blockToInsert.get(), startBlock); - - updateLayout(); - - // Make clones of ancestors in between the start node and the start block. - RefPtr<Node> parent = blockToInsert; - for (size_t i = ancestors.size(); i != 0; --i) { - RefPtr<Node> child = ancestors[i - 1]->cloneNode(false); // shallow clone - appendNode(child.get(), parent.get()); - parent = child.release(); - } - - // If the paragraph separator was inserted at the end of a paragraph, an empty line must be - // created. All of the nodes, starting at visiblePos, are about to be added to the new paragraph - // element. If the first node to be inserted won't be one that will hold an empty line open, add a br. - if (isEndOfParagraph(visiblePos) && !lineBreakExistsAtPosition(visiblePos)) - appendNode(createBreakElement(document()).get(), blockToInsert.get()); - - // Move the start node and the siblings of the start node. - if (startNode != startBlock) { - Node *n = startNode; - if (pos.offset() >= caretMaxOffset(startNode)) - n = startNode->nextSibling(); - - while (n && n != blockToInsert) { - Node *next = n->nextSibling(); - removeNode(n); - appendNode(n, parent.get()); - n = next; - } - } - - // Move everything after the start node. - if (!ancestors.isEmpty()) { - Node* leftParent = ancestors.first(); - while (leftParent && leftParent != startBlock) { - parent = parent->parentNode(); - Node* n = leftParent->nextSibling(); - while (n && n != blockToInsert) { - Node* next = n->nextSibling(); - removeNode(n); - appendNode(n, parent.get()); - n = next; - } - leftParent = leftParent->parentNode(); - } - } - - // Handle whitespace that occurs after the split - if (splitText) { - updateLayout(); - pos = Position(startNode, 0); - if (!pos.isRenderedCharacter()) { - // Clear out all whitespace and insert one non-breaking space - ASSERT(startNode); - ASSERT(startNode->isTextNode()); - ASSERT(!startNode->renderer() || startNode->renderer()->style()->collapseWhiteSpace()); - deleteInsignificantTextDownstream(pos); - insertTextIntoNode(static_cast<Text*>(startNode), 0, nonBreakingSpaceString()); - } - } - - setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM)); - applyStyleAfterInsertion(startBlock); -} - -} // namespace WebCore |