📄 htmlediting_impl.cpp
字号:
ASSERT(startNode); ASSERT(endNode); ASSERT(element); NodeImpl *node = startNode; while (1) { NodeImpl *next = node->traverseNextNode(); if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) { removeNode(node); appendNode(element, node); } if (node == endNode) break; node = next; }}void ApplyStyleCommandImpl::applyStyleIfNeeded(DOM::NodeImpl *startNode, DOM::NodeImpl *endNode){ StyleChange styleChange = computeStyleChange(Position(startNode, 0), style()); int exceptionCode = 0; if (styleChange.cssStyle.length() > 0) { ElementImpl *styleElement = document()->createHTMLElement("SPAN", exceptionCode); ASSERT(exceptionCode == 0); styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle); styleElement->setAttribute(ATTR_CLASS, styleSpanClassString()); insertNodeBefore(styleElement, startNode); surroundNodeRangeWithElement(startNode, endNode, styleElement); } if (styleChange.applyBold) { ElementImpl *boldElement = document()->createHTMLElement("B", exceptionCode); ASSERT(exceptionCode == 0); insertNodeBefore(boldElement, startNode); surroundNodeRangeWithElement(startNode, endNode, boldElement); } if (styleChange.applyItalic) { ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode); ASSERT(exceptionCode == 0); insertNodeBefore(italicElement, startNode); surroundNodeRangeWithElement(startNode, endNode, italicElement); }}bool ApplyStyleCommandImpl::currentlyHasStyle(const Position &pos, const CSSProperty *property) const{ ASSERT(pos.notEmpty()); CSSStyleDeclarationImpl *decl = document()->defaultView()->getComputedStyle(pos.element(), 0); ASSERT(decl); CSSValueImpl *value = decl->getPropertyCSSValue(property->id()); return strcasecmp(value->cssText(), property->value()->cssText()) == 0;}ApplyStyleCommandImpl::StyleChange ApplyStyleCommandImpl::computeStyleChange(const Position &insertionPoint, CSSStyleDeclarationImpl *style){ ASSERT(insertionPoint.notEmpty()); ASSERT(style); StyleChange styleChange; for (QPtrListIterator<CSSProperty> it(*(style->values())); it.current(); ++it) { CSSProperty *property = it.current(); if (!currentlyHasStyle(insertionPoint, property)) { switch (property->id()) { case CSS_PROP_FONT_WEIGHT: if (strcasecmp(property->value()->cssText(), "bold") == 0) styleChange.applyBold = true; else styleChange.cssStyle += property->cssText(); break; case CSS_PROP_FONT_STYLE: { DOMString cssText(property->value()->cssText()); if (strcasecmp(cssText, "italic") == 0 || strcasecmp(cssText, "oblique") == 0) styleChange.applyItalic = true; else styleChange.cssStyle += property->cssText(); } break; default: styleChange.cssStyle += property->cssText(); break; } } } return styleChange;}Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos){ if (pos.node()->isTextNode() && (pos.offset() > 0 && pos.offset() < pos.node()->maxOffset())) { SplitTextNodeCommand split(document(), static_cast<TextImpl *>(pos.node()), pos.offset()); split.apply(); pos = Position(split.node(), 0); }#if 0 // EDIT FIXME: If modified to work with the internals of applying style, // this code can work to optimize cases where a style change is taking place on // a boundary between nodes where one of the nodes has the desired style. In other // words, it is possible for content to be merged into existing nodes rather than adding // additional markup. if (currentlyHasStyle(pos)) return pos; // try next node if (pos.offset() >= pos.node()->caretMaxOffset()) { NodeImpl *nextNode = pos.node()->traverseNextNode(); if (nextNode) { Position next = Position(nextNode, 0); if (currentlyHasStyle(next)) return next; } } // try previous node if (pos.offset() <= pos.node()->caretMinOffset()) { NodeImpl *prevNode = pos.node()->traversePreviousNode(); if (prevNode) { Position prev = Position(prevNode, prevNode->maxOffset()); if (currentlyHasStyle(prev)) return prev; } }#endif return pos;}//------------------------------------------------------------------------------------------// DeleteCollapsibleWhitespaceCommandImplDeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document) : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false){}DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const Selection &selection) : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true){}DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl(){}int DeleteCollapsibleWhitespaceCommandImpl::commandID() const{ return DeleteCollapsibleWhitespaceCommandID;}static bool shouldDeleteUpstreamPosition(const Position &pos){ if (!pos.node()->isTextNode()) return false; RenderObject *renderer = pos.node()->renderer(); if (!renderer) return true; TextImpl *textNode = static_cast<TextImpl *>(pos.node()); if (pos.offset() >= (long)textNode->length()) return false; if (pos.isLastRenderedPositionInEditableBlock()) return false; if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine()) return false; RenderText *textRenderer = static_cast<RenderText *>(renderer); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (pos.offset() < box->m_start) { return true; } if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len) return false; } return true;}Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const Position &pos){ Position upstream = pos.equivalentUpstreamPosition(); Position downstream = pos.equivalentDownstreamPosition(); bool del = shouldDeleteUpstreamPosition(upstream); LOG(Editing, "pos: %s [%p:%d]\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset()); if (upstream == downstream) { LOG(Editing, "same: %s [%p:%d]\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset()); } else { LOG(Editing, "upstream: %s %s [%p:%d]\n", del ? "DELETE" : "SKIP", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset()); PositionIterator it(upstream); for (it.next(); it.current() != downstream; it.next()) { if (it.current().node()->isTextNode() && (long)static_cast<TextImpl *>(it.current().node())->length() == it.current().offset()) LOG(Editing, " node: AT END %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset()); else LOG(Editing, " node: DELETE %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset()); } LOG(Editing, "downstream: %s [%p:%d]\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset()); } if (upstream == downstream) return upstream; PositionIterator it(upstream); Position deleteStart = upstream; if (!del) { deleteStart = it.peekNext(); if (deleteStart == downstream) return upstream; } Position endingPosition = upstream; while (it.current() != downstream) { Position next = it.peekNext(); if (next.node() != deleteStart.node()) { 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); if (textNode == endingPosition.node()) endingPosition = Position(next.node(), next.node()->caretMinOffset()); removeNodeAndPrune(textNode); } else { LOG(Editing, " deleteText 1: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), it.current().offset() - deleteStart.offset()); deleteText(textNode, deleteStart.offset(), count); } deleteStart = next; } else if (next == downstream) { 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 = Position(downstream.node(), downstream.offset() - m_charactersDeleted); } } it.setPosition(next); } return endingPosition;}void DeleteCollapsibleWhitespaceCommandImpl::doApply(){ // If selection has not been set to a custom selection when the command was created, // use the current ending selection. if (!m_hasSelectionToCollapse) m_selectionToCollapse = endingSelection(); int state = m_selectionToCollapse.state(); if (state == Selection::CARET) { Position endPosition = deleteWhitespace(m_selectionToCollapse.start()); setEndingSelection(endPosition); LOG(Editing, "-----------------------------------------------------\n"); } else if (state == Selection::RANGE) { Position startPosition = deleteWhitespace(m_selectionToCollapse.start()); LOG(Editing, "-----------------------------------------------------\n"); Position endPosition = m_selectionToCollapse.end(); if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) { LOG(Editing, "adjust end position by %d\n", m_charactersDeleted); endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted); } endPosition = deleteWhitespace(endPosition); setEndingSelection(Selection(startPosition, endPosition)); LOG(Editing, "=====================================================\n"); }}//------------------------------------------------------------------------------------------// DeleteSelectionCommandImplDeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document) : CompositeEditCommandImpl(document), m_hasSelectionToDelete(false){}DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document, const Selection &selection) : CompositeEditCommandImpl(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true){}DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl(){} int DeleteSelectionCommandImpl::commandID() const{ return DeleteSelectionCommandID;}void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle(){ Selection selection = endingSelection(); if (selection.state() != Selection::CARET) return; Position pos(selection.start()); if (!pos.node()->isTextNode()) return; TextImpl *textNode = static_cast<TextImpl *>(pos.node()); if (pos.offset() == 0) { PositionIterator it(pos); Position prev = it.previous(); if (prev == pos) return; if (prev.node()->isTextNode()) { TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node()); if (textNodesAreJoinable(prevTextNode, textNode)) { joinTextNodes(prevTextNode, textNode); setEndingSelection(Position(textNode, prevTextNode->length())); LOG(Editing, "joinTextNodesWithSameStyle [1]\n"); } } } else if (pos.offset() == (long)textNode->length()) { PositionIterator it(pos); Position next = it.next(); if (next == pos) return; if (next.node()->isTextNode()) { TextImpl *nextTextNode = static_cast<TextImpl *>(next.node()); if (textNodesAreJoinable(textNode, nextTextNode)) { joinTextNodes(textNode, nextTextNode); setEndingSelection(Position(nextTextNode, pos.offset())); LOG(Editing, "joinTextNodesWithSameStyle [2]\n"); } } }}bool DeleteSelectionCommandImpl::containsOnlyWhitespace(const Position &start, const Position &end){ // Returns whether the range contains only whitespace characters. // This is inclusive of the start, but not of the end. PositionIterator it(start); while (!it.atEnd()) { if (!it.current().node()->isTextNode()) return false; const DOMString &text = static_cast<TextImpl *>(it.current().node())->data(); // EDIT FIXME: signed/unsigned mismatch if (text.length() > INT_MAX) return false; if (it.current().offset() < (int)text.length() && !isWS(text[it.current().offset()])) return false; it.next(); if (it.current() == end) break; } return true;}void DeleteSelectionCommandImpl::doApply(){ // If selection has not been set to a custom selection when the command was created, // use the current ending selection. if (!m_hasSelectionToDelete) m_selectionToDelete = endingSelection(); if (m_selectionToDelete.state() != Selection::RANGE) return; deleteCollapsibleWhitespace(m_selectionToDelete); Selection selection = endingSelection(); Position upstreamStart(selection.start().equivalentUpstreamPosition());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -