📄 deleteselectioncommand.cpp
字号:
// typing style at the start of the selection, nor is there a reason to // compute the style at the start of the selection after deletion (see the // early return in calculateTypingStyleAfterDelete). if (m_upstreamStart.node() == m_downstreamEnd.node() && m_upstreamStart.node()->isTextNode()) return; // Figure out the typing style in effect before the delete is done. RefPtr<CSSComputedStyleDeclaration> computedStyle = positionBeforeTabSpan(m_selectionToDelete.start()).computedStyle(); m_typingStyle = computedStyle->copyInheritableProperties(); removeEnclosingAnchorStyle(m_typingStyle.get(), m_selectionToDelete.start()); // If we're deleting into a Mail blockquote, save the style at end() instead of start() // We'll use this later in computeTypingStyleAfterDelete if we end up outside of a Mail blockquote if (nearestMailBlockquote(m_selectionToDelete.start().node())) { computedStyle = m_selectionToDelete.end().computedStyle(); m_deleteIntoBlockquoteStyle = computedStyle->copyInheritableProperties(); } else m_deleteIntoBlockquoteStyle = 0;}bool DeleteSelectionCommand::handleSpecialCaseBRDelete(){ // Check for special-case where the selection contains only a BR on a line by itself after another BR. bool upstreamStartIsBR = m_upstreamStart.node()->hasTagName(brTag); bool downstreamStartIsBR = m_downstreamStart.node()->hasTagName(brTag); bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.node() == m_upstreamEnd.node(); if (isBROnLineByItself) { removeNode(m_downstreamStart.node()); return true; } // Not a special-case delete per se, but we can detect that the merging of content between blocks // should not be done. if (upstreamStartIsBR && downstreamStartIsBR) { m_mergeBlocksAfterDelete = false; m_endingPosition = m_downstreamEnd; } return false;}static void updatePositionForNodeRemoval(Node* node, Position& position){ if (position.isNull()) return; if (node->parent() == position.node() && node->nodeIndex() < (unsigned)position.offset()) position = Position(position.node(), position.offset() - 1); if (position.node() == node || position.node()->isDescendantOf(node)) position = positionBeforeNode(node);}void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node){ if (!node) return; if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.get()) && node->isDescendantOf(m_endRoot.get()))) { // If a node is not in both the start and end editable roots, remove it only if its inside an editable region. if (!node->parentNode()->isContentEditable()) { // Don't remove non-editable atomic nodes. if (!node->firstChild()) return; // Search this non-editable region for editable regions to empty. RefPtr<Node> child = node->firstChild(); while (child) { RefPtr<Node> nextChild = child->nextSibling(); removeNode(child.get()); // Bail if nextChild is no longer node's child. if (nextChild && nextChild->parentNode() != node) return; child = nextChild; } // Don't remove editable regions that are inside non-editable ones, just clear them. return; } } if (isTableStructureNode(node.get()) || node == node->rootEditableElement()) { // Do not remove an element of table structure; remove its contents. // Likewise for the root editable element. Node* child = node->firstChild(); while (child) { Node* remove = child; child = child->nextSibling(); removeNode(remove); } // make sure empty cell has some height updateLayout(); RenderObject *r = node->renderer(); if (r && r->isTableCell() && static_cast<RenderTableCell*>(r)->contentHeight() <= 0) insertBlockPlaceholder(Position(node,0)); return; } if (node == m_startBlock && !isEndOfBlock(VisiblePosition(m_startBlock.get(), 0, DOWNSTREAM).previous())) m_needPlaceholder = true; else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(m_endBlock.get(), maxDeepOffset(m_endBlock.get()), DOWNSTREAM).next())) m_needPlaceholder = true; // FIXME: Update the endpoints of the range being deleted. updatePositionForNodeRemoval(node.get(), m_endingPosition); updatePositionForNodeRemoval(node.get(), m_leadingWhitespace); updatePositionForNodeRemoval(node.get(), m_trailingWhitespace); CompositeEditCommand::removeNode(node);}static void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position){ if (position.node() == node) { if (position.offset() > offset + count) position = Position(position.node(), position.offset() - count); else if (position.offset() > offset) position = Position(position.node(), offset); }}void DeleteSelectionCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count){ // FIXME: Update the endpoints of the range being deleted. updatePositionForTextRemoval(node.get(), offset, count, m_endingPosition); updatePositionForTextRemoval(node.get(), offset, count, m_leadingWhitespace); updatePositionForTextRemoval(node.get(), offset, count, m_trailingWhitespace); CompositeEditCommand::deleteTextFromNode(node, offset, count);}void DeleteSelectionCommand::handleGeneralDelete(){ int startOffset = m_upstreamStart.offset(); Node* startNode = m_upstreamStart.node(); // Never remove the start block unless it's a table, in which case we won't merge content in. if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditing(startNode) && !startNode->hasTagName(tableTag)) { startOffset = 0; startNode = startNode->traverseNextNode(); } if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) { Text *text = static_cast<Text *>(startNode); if (text->length() > (unsigned)caretMaxOffset(startNode)) deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode)); } if (startOffset >= maxDeepOffset(startNode)) { startNode = startNode->traverseNextSibling(); startOffset = 0; } // Done adjusting the start. See if we're all done. if (!startNode) return; if (startNode == m_downstreamEnd.node()) { // The selection to delete is all in one node. if (!startNode->renderer() || (startOffset == 0 && m_downstreamEnd.offset() >= maxDeepOffset(startNode))) { // just delete removeNode(startNode); } else if (m_downstreamEnd.offset() - startOffset > 0) { if (startNode->isTextNode()) { // in a text node that needs to be trimmed Text *text = static_cast<Text *>(startNode); deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset); } else { removeChildrenInRange(startNode, startOffset, m_downstreamEnd.offset()); m_endingPosition = m_upstreamStart; } } } else { bool startNodeWasDescendantOfEndNode = m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()); // The selection to delete spans more than one node. RefPtr<Node> node(startNode); if (startOffset > 0) { if (startNode->isTextNode()) { // in a text node that needs to be trimmed Text *text = static_cast<Text *>(node.get()); deleteTextFromNode(text, startOffset, text->length() - startOffset); node = node->traverseNextNode(); } else { node = startNode->childNode(startOffset); } } // handle deleting all nodes that are completely selected while (node && node != m_downstreamEnd.node()) { if (Range::compareBoundaryPoints(Position(node.get(), 0), m_downstreamEnd) >= 0) { // traverseNextSibling just blew past the end position, so stop deleting node = 0; } else if (!m_downstreamEnd.node()->isDescendantOf(node.get())) { RefPtr<Node> nextNode = node->traverseNextSibling(); // if we just removed a node from the end container, update end position so the // check above will work if (node->parentNode() == m_downstreamEnd.node()) { ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.offset()); m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.offset() - 1); } removeNode(node.get()); node = nextNode.get(); } else { Node* n = node->lastDescendant(); if (m_downstreamEnd.node() == n && m_downstreamEnd.offset() >= caretMaxOffset(n)) { removeNode(node.get()); node = 0; } else node = node->traverseNextNode(); } } if (m_downstreamEnd.node() != startNode && !m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()) && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.offset() >= caretMinOffset(m_downstreamEnd.node())) { if (m_downstreamEnd.offset() >= maxDeepOffset(m_downstreamEnd.node()) && !canHaveChildrenForEditing(m_downstreamEnd.node())) { // The node itself is fully selected, not just its contents. Delete it. removeNode(m_downstreamEnd.node()); } else { if (m_downstreamEnd.node()->isTextNode()) { // in a text node that needs to be trimmed Text *text = static_cast<Text *>(m_downstreamEnd.node()); if (m_downstreamEnd.offset() > 0) { deleteTextFromNode(text, 0, m_downstreamEnd.offset()); m_downstreamEnd = Position(text, 0); } // Remove children of m_downstreamEnd.node() that come after m_upstreamStart. // Don't try to remove children if m_upstreamStart was inside m_downstreamEnd.node() // and m_upstreamStart has been removed from the document, because then we don't // know how many children to remove. // FIXME: Make m_upstreamStart a position we update as we remove content, then we can // always know which children to remove. } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.node()->inDocument())) { int offset = 0; if (m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node())) { Node *n = m_upstreamStart.node(); while (n && n->parentNode() != m_downstreamEnd.node()) n = n->parentNode(); if (n) offset = n->nodeIndex() + 1; } removeChildrenInRange(m_downstreamEnd.node(), offset, m_downstreamEnd.offset()); m_downstreamEnd = Position(m_downstreamEnd.node(), offset); } } } }}void DeleteSelectionCommand::fixupWhitespace(){ updateLayout(); // FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter()) { Text* textNode = static_cast<Text*>(m_leadingWhitespace.node()); ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); replaceTextInNode(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString()); } if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter()) { Text* textNode = static_cast<Text*>(m_trailingWhitespace.node()); ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace()); replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString()); }}// If a selection starts in one block and ends in another, we have to merge to bring content before the// start together with content after the end.void DeleteSelectionCommand::mergeParagraphs()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -