📄 dom_selection.cpp
字号:
break; case BASE: pos = base(); break; case EXTENT: pos = extent(); break; } KHTMLPart *part = pos.node()->getDocument()->part(); if (!part) return x; if (recalc || part->xPosForVerticalArrowNavigation() == KHTMLPart::NoXPosForVerticalArrowNavigation) { int y, w, h; pos.node()->renderer()->caretPos(pos.offset(), true, x, y, w, h); part->setXPosForVerticalArrowNavigation(x); } else { x = part->xPosForVerticalArrowNavigation(); } return x;}void Selection::clear(){ assignBaseAndExtent(emptyPosition(), emptyPosition()); validate();}void Selection::setBase(const Position &pos){ assignBase(pos); validate();}void Selection::setExtent(const Position &pos){ assignExtent(pos); validate();}void Selection::setBaseAndExtent(const Position &base, const Position &extent){ assignBaseAndExtent(base, extent); validate();}void Selection::setStart(const Position &pos){ assignStart(pos); validate();}void Selection::setEnd(const Position &pos){ assignEnd(pos); validate();}void Selection::setStartAndEnd(const Position &start, const Position &end){ assignStartAndEnd(start, end); validate();}void Selection::setNeedsLayout(bool flag){ m_needsCaretLayout = flag;}Range Selection::toRange() const{ if (isEmpty()) return Range(); // Make sure we have an updated layout since this function is called // in the course of running edit commands which modify the DOM. // Failing to call this can result in equivalentXXXPosition calls returning // incorrect results. start().node()->getDocument()->updateLayout(); Position s, e; if (state() == CARET) { // If the selection is a caret, move the range start upstream. This helps us match // the conventions of text editors tested, which make style determinations based // on the character before the caret, if any. s = start().equivalentUpstreamPosition().equivalentRangeCompliantPosition(); e = s; } else { // If the selection is a range, select the minimum range that encompasses the selection. // Again, this is to match the conventions of text editors tested, which make style // determinations based on the first character of the selection. // For instance, this operation helps to make sure that the "X" selected below is the // only thing selected. The range should not be allowed to "leak" out to the end of the // previous text node, or to the beginning of the next text node, each of which has a // different style. // // On a treasure map, <b>X</b> marks the spot. // ^ selected // ASSERT(state() == RANGE); s = start().equivalentDownstreamPosition(); e = end().equivalentUpstreamPosition(); if ((s.node() == e.node() && s.offset() > e.offset()) || !nodeIsBeforeNode(s.node(), e.node())) { // Make sure the start is before the end. // The end can wind up before the start if collapsed whitespace is the only thing selected. Position tmp = s; s = e; e = tmp; } s = s.equivalentRangeCompliantPosition(); e = e.equivalentRangeCompliantPosition(); } return Range(Node(s.node()), s.offset(), Node(e.node()), e.offset());}void Selection::layoutCaret(){ if (isEmpty() || !start().node()->renderer()) { m_caretX = m_caretY = m_caretSize = 0; } else { // EDIT FIXME: Enhance call to pass along selection // upstream/downstream affinity to get the right position. int w; start().node()->renderer()->caretPos(start().offset(), true, m_caretX, m_caretY, w, m_caretSize); } m_needsCaretLayout = false;}QRect Selection::getRepaintRect() const{ if (m_needsCaretLayout) { const_cast<Selection *>(this)->layoutCaret(); } // EDIT FIXME: fudge a bit to make sure we don't leave behind artifacts return QRect(m_caretX - 1, m_caretY - 1, 3, m_caretSize + 2);}void Selection::needsCaretRepaint(){ if (isEmpty()) return; if (!start().node()->getDocument()) return; KHTMLView *v = start().node()->getDocument()->view(); if (!v) return; if (m_needsCaretLayout) { // repaint old position and calculate new position v->updateContents(getRepaintRect(), false); layoutCaret(); // EDIT FIXME: This is an unfortunate hack. // Basically, we can't trust this layout position since we // can't guarantee that the check to see if we are in unrendered // content will work at this point. We may have to wait for // a layout and re-render of the document to happen. So, resetting this // flag will cause another caret layout to happen the first time // that we try to paint the caret after this call. That one will work since // it happens after the document has accounted for any editing // changes which may have been done. // And, we need to leave this layout here so the caret moves right // away after clicking. m_needsCaretLayout = true; } v->updateContents(getRepaintRect(), false);}void Selection::paintCaret(QPainter *p, const QRect &rect){ if (isEmpty()) return; if (m_state != CARET) return; if (m_needsCaretLayout) { Position pos = start(); if (!pos.inRenderedContent()) { moveToRenderedContent(); } layoutCaret(); } QRect caretRect(m_caretX, m_caretY, 1, m_caretSize); if (caretRect.intersects(rect)) { QPen pen = p->pen(); pen.setStyle(Qt::SolidLine); pen.setColor(Qt::black); pen.setWidth(1); p->setPen(pen); p->drawLine(caretRect.left(), caretRect.top(), caretRect.left(), caretRect.bottom()); }}void Selection::validate(ETextGranularity granularity){ // move the base and extent nodes to their equivalent leaf positions bool baseAndExtentEqual = base() == extent(); if (base().notEmpty()) { Position pos = base().equivalentLeafPosition(); assignBase(pos); if (baseAndExtentEqual) assignExtent(pos); } if (extent().notEmpty() && !baseAndExtentEqual) { assignExtent(extent().equivalentLeafPosition()); } // make sure we do not have a dangling start or end if (base().isEmpty() && extent().isEmpty()) { assignStartAndEnd(emptyPosition(), emptyPosition()); m_baseIsStart = true; } else if (base().isEmpty() || extent().isEmpty()) { m_baseIsStart = true; } else { // adjust m_baseIsStart as needed if (base().node() == extent().node()) { if (base().offset() > extent().offset()) m_baseIsStart = false; else m_baseIsStart = true; } else if (nodeIsBeforeNode(base().node(), extent().node())) m_baseIsStart = true; else m_baseIsStart = false; } // calculate the correct start and end positions if (granularity == CHARACTER) { if (m_baseIsStart) assignStartAndEnd(base(), extent()); else assignStartAndEnd(extent(), base()); } else if (granularity == WORD) { int baseStartOffset = base().offset(); int baseEndOffset = base().offset(); int extentStartOffset = extent().offset(); int extentEndOffset = extent().offset(); if (base().notEmpty() && (base().node()->nodeType() == Node::TEXT_NODE || base().node()->nodeType() == Node::CDATA_SECTION_NODE)) { DOMString t = base().node()->nodeValue(); QChar *chars = t.unicode(); uint len = t.length(); findWordBoundary(chars, len, base().offset(), &baseStartOffset, &baseEndOffset); } if (extent().notEmpty() && (extent().node()->nodeType() == Node::TEXT_NODE || extent().node()->nodeType() == Node::CDATA_SECTION_NODE)) { DOMString t = extent().node()->nodeValue(); QChar *chars = t.unicode(); uint len = t.length(); findWordBoundary(chars, len, extent().offset(), &extentStartOffset, &extentEndOffset); } if (m_baseIsStart) { assignStart(Position(base().node(), baseStartOffset)); assignEnd(Position(extent().node(), extentEndOffset)); } else { assignStart(Position(extent().node(), extentStartOffset)); assignEnd(Position(base().node(), baseEndOffset)); } } else { // granularity == LINE Selection baseSelection = *this; Selection extentSelection = *this; if (base().notEmpty() && (base().node()->nodeType() == Node::TEXT_NODE || base().node()->nodeType() == Node::CDATA_SECTION_NODE)) { if (startAndEndLineNodesIncludingNode(base().node(), base().offset(), baseSelection)) { assignStart(Position(baseSelection.base().node(), baseSelection.base().offset())); assignEnd(Position(baseSelection.extent().node(), baseSelection.extent().offset())); } } if (extent().notEmpty() && (extent().node()->nodeType() == Node::TEXT_NODE || extent().node()->nodeType() == Node::CDATA_SECTION_NODE)) { if (startAndEndLineNodesIncludingNode(extent().node(), extent().offset(), extentSelection)) { assignStart(Position(extentSelection.base().node(), extentSelection.base().offset())); assignEnd(Position(extentSelection.extent().node(), extentSelection.extent().offset())); } } if (m_baseIsStart) { assignStart(baseSelection.start()); assignEnd(extentSelection.end()); } else { assignStart(extentSelection.start()); assignEnd(baseSelection.end()); } } // adjust the state if (start().isEmpty() && end().isEmpty()) m_state = NONE; else if (start() == end()) m_state = CARET; else m_state = RANGE; m_needsCaretLayout = true; #if EDIT_DEBUG debugPosition();#endif}bool Selection::moveToRenderedContent(){ if (isEmpty()) return false; if (m_state != CARET) return false; Position pos = start();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -