📄 visible_text.cpp
字号:
case ID_TR:
case ID_UL:
if (m_lastCharacter != '\n' && m_lastTextNode) {
emitCharacter('\n', m_lastTextNode->parentNode(), m_lastTextNode, 0, 1);
}
break;
}
return true;
}
void TextIterator::exitNode()
{
bool endLine = false;
bool addNewline = false;
switch (m_node->id()) {
case ID_BLOCKQUOTE:
case ID_DD:
case ID_DIV:
case ID_DL:
case ID_DT:
case ID_HR:
case ID_LI:
case ID_OL:
case ID_PRE:
case ID_TR:
case ID_UL:
endLine = true;
break;
case ID_H1:
case ID_H2:
case ID_H3:
case ID_H4:
case ID_H5:
case ID_H6:
case ID_P: {
endLine = true;
// In certain cases, emit a new newline character for this node
// regardless of whether we emit another one.
// FIXME: Some day we could do this for other tags.
// However, doing it just for the tags above makes it more likely
// we'll end up getting the right result without margin collapsing.
// For example: <div><p>text</p></div> will work right even if both
// the <div> and the <p> have bottom margins.
RenderObject *renderer = m_node->renderer();
if (renderer) {
RenderStyle *style = renderer->style();
if (style) {
int bottomMargin = renderer->collapsedMarginBottom();
int fontSize = style->htmlFont().getFontDef().computedPixelSize();
if (bottomMargin * 2 >= fontSize) {
addNewline = true;
}
}
}
break;
}
}
// emit character(s) iff there is an earlier text node and we need at least one newline
if (m_lastTextNode && endLine) {
if (m_lastCharacter != '\n') {
// insert a newline with a position following this block
emitCharacter('\n', m_node->parentNode(), m_node, 1, 1);
//...and, if needed, set flag to later add a newline for the current node
assert(!m_needAnotherNewline);
m_needAnotherNewline = addNewline;
} else if (addNewline) {
// insert a newline with a position following this block
emitCharacter('\n', m_node->parentNode(), m_node, 1, 1);
}
}
}
void TextIterator::emitCharacter(QChar c, NodeImpl *textNode, NodeImpl *offsetBaseNode, long textStartOffset, long textEndOffset)
{
// remember information with which to construct the TextIterator::range()
// NOTE: textNode is often not a text node, so the range will specify child nodes of positionNode
m_positionNode = textNode;
m_positionOffsetBaseNode = offsetBaseNode;
m_positionStartOffset = textStartOffset;
m_positionEndOffset = textEndOffset;
// remember information with which to construct the TextIterator::characters() and length()
m_singleCharacterBuffer = c;
m_textCharacters = &m_singleCharacterBuffer;
m_textLength = 1;
// remember some iteration state
m_lastTextNodeEndedWithCollapsedSpace = false;
m_lastCharacter = c;
}
Range TextIterator::range() const
{
// use the current run information, if we have it
if (m_positionNode) {
if (m_positionOffsetBaseNode) {
long index = m_positionOffsetBaseNode->nodeIndex();
m_positionStartOffset += index;
m_positionEndOffset += index;
m_positionOffsetBaseNode = 0;
}
return Range(m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
}
// otherwise, return the end of the overall range we were given
if (m_endContainer)
return Range(m_endContainer, m_endOffset, m_endContainer, m_endOffset);
return Range();
}
SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator() : m_positionNode(0)
{
}
SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range &r)
{
if (r.isNull()) {
m_positionNode = 0;
return;
}
NodeImpl *startNode = r.startContainer().handle();
NodeImpl *endNode = r.endContainer().handle();
long startOffset = r.startOffset();
long endOffset = r.endOffset();
if (!offsetInCharacters(startNode->nodeType())) {
if (startOffset >= 0 && startOffset < static_cast<long>(startNode->childNodeCount())) {
startNode = startNode->childNode(startOffset);
startOffset = 0;
}
}
if (!offsetInCharacters(endNode->nodeType())) {
if (endOffset > 0 && endOffset <= static_cast<long>(endNode->childNodeCount())) {
endNode = endNode->childNode(endOffset - 1);
endOffset = endNode->hasChildNodes() ? endNode->childNodeCount() : endNode->maxOffset();
}
}
m_node = endNode;
m_offset = endOffset;
m_handledNode = false;
m_handledChildren = endOffset == 0;
m_startNode = startNode;
m_startOffset = startOffset;
#ifndef NDEBUG
// Need this just because of the assert.
m_positionNode = endNode;
#endif
m_lastTextNode = 0;
m_lastCharacter = '\n';
advance();
}
void SimplifiedBackwardsTextIterator::advance()
{
assert(m_positionNode);
m_positionNode = 0;
m_textLength = 0;
while (m_node) {
if (!m_handledNode) {
RenderObject *renderer = m_node->renderer();
if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) {
// FIXME: What about CDATA_SECTION_NODE?
if (renderer->style()->visibility() == VISIBLE && m_offset > 0) {
m_handledNode = handleTextNode();
}
} else if (renderer && (renderer->isImage() || renderer->isWidget())) {
if (renderer->style()->visibility() == VISIBLE && m_offset > 0) {
m_handledNode = handleReplacedElement();
}
} else {
m_handledNode = handleNonTextNode();
}
if (m_positionNode) {
return;
}
}
if (m_node == m_startNode)
return;
NodeImpl *next = 0;
if (!m_handledChildren) {
next = m_node->lastChild();
while (next && next->lastChild())
next = next->lastChild();
m_handledChildren = true;
}
if (!next && m_node != m_startNode) {
next = m_node->previousSibling();
if (next) {
exitNode();
while (next->lastChild())
next = next->lastChild();
}
else if (m_node->parentNode()) {
next = m_node->parentNode();
exitNode();
}
}
// Check for leaving a node and iterating backwards
// into a different block that is an descendent of the
// block containing the node (as in leaving
// the "bar" node in this example: <p>foo</p>bar).
// Must emit newline when leaving node containing "bar".
if (next && m_node->renderer() && m_node->renderer()->style()->visibility() == VISIBLE) {
NodeImpl *block = m_node->enclosingBlockFlowElement();
if (block) {
NodeImpl *nextBlock = next->enclosingBlockFlowElement();
if (nextBlock && nextBlock->isAncestor(block))
emitNewlineForBROrText();
}
}
m_node = next;
if (m_node)
m_offset = m_node->caretMaxOffset();
else
m_offset = 0;
m_handledNode = false;
if (m_positionNode) {
return;
}
}
}
bool SimplifiedBackwardsTextIterator::handleTextNode()
{
m_lastTextNode = m_node;
RenderText *renderer = static_cast<RenderText *>(m_node->renderer());
DOMString str = m_node->nodeValue();
if (!renderer->firstTextBox() && str.length() > 0) {
return true;
}
m_positionEndOffset = m_offset;
m_offset = (m_node == m_startNode) ? m_startOffset : 0;
m_positionNode = m_node;
m_positionStartOffset = m_offset;
m_textLength = m_positionEndOffset - m_positionStartOffset;
m_textCharacters = str.unicode() + m_positionStartOffset;
m_lastCharacter = str[m_positionEndOffset - 1];
return true;
}
bool SimplifiedBackwardsTextIterator::handleReplacedElement()
{
long offset = m_node->nodeIndex();
m_positionNode = m_node->parentNode();
m_positionStartOffset = offset;
m_positionEndOffset = offset + 1;
m_textCharacters = 0;
m_textLength = 0;
m_lastCharacter = 0;
return true;
}
bool SimplifiedBackwardsTextIterator::handleNonTextNode()
{
switch (m_node->id()) {
case ID_BR:
emitNewlineForBROrText();
break;
case ID_TD:
case ID_TH:
case ID_BLOCKQUOTE:
case ID_DD:
case ID_DIV:
case ID_DL:
case ID_DT:
case ID_H1:
case ID_H2:
case ID_H3:
case ID_H4:
case ID_H5:
case ID_H6:
case ID_HR:
case ID_P:
case ID_PRE:
case ID_TR:
case ID_OL:
case ID_UL:
case ID_LI:
// Emit a space to "break up" content. Any word break
// character will do.
emitCharacter(' ', m_node, 0, 0);
break;
}
return true;
}
void SimplifiedBackwardsTextIterator::exitNode()
{
// Left this function in so that the name and usage patters remain similar to
// TextIterator. However, the needs of this iterator are much simpler, and
// the handleNonTextNode() function does just what we want (i.e. insert a
// space for certain node types to "break up" text so that it does not seem
// like content is next to other text when it really isn't).
handleNonTextNode();
}
void SimplifiedBackwardsTextIterator::emitCharacter(QChar c, NodeImpl *node, long startOffset, long endOffset)
{
m_singleCharacterBuffer = c;
m_positionNode = node;
m_positionStartOffset = startOffset;
m_positionEndOffset = endOffset;
m_textCharacters = &m_singleCharacterBuffer;
m_textLength = 1;
m_lastCharacter = c;
}
void SimplifiedBackwardsTextIterator::emitNewlineForBROrText()
{
long offset;
if (m_lastTextNode) {
offset = m_lastTextNode->nodeIndex();
emitCharacter('\n', m_lastTextNode->parentNode(), offset, offset + 1);
} else {
offset = m_node->nodeIndex();
emitCharacter('\n', m_node->parentNode(), offset, offset + 1);
}
}
Range SimplifiedBackwardsTextIterator::range() const
{
if (m_positionNode) {
return Range(m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
} else {
return Range(m_startNode, m_startOffset, m_startNode, m_startOffset);
}
}
CharacterIterator::CharacterIterator()
: m_offset(0), m_runOffset(0), m_atBreak(true)
{
}
CharacterIterator::CharacterIterator(const Range &r)
: m_offset(0), m_runOffset(0), m_atBreak(true), m_textIterator(r, RUNFINDER)
{
while (!atEnd() && m_textIterator.length() == 0) {
m_textIterator.advance();
}
}
Range CharacterIterator::range() const
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -