📄 render_text.cpp
字号:
RenderText::RenderText(DOM::NodeImpl* node, DOMStringImpl *_str)
: RenderObject(node), m_linesDirty(false)
{
// init RenderObject attributes
setRenderText(); // our object inherits from RenderText
m_minWidth = -1;
m_maxWidth = -1;
#ifdef APPLE_CHANGES
m_monospaceCharacterWidth = 0;
m_allAsciiChecked = false;
m_allAscii = false;
#endif
str = _str;
if (str) {
str = str->replace('\\', backslashAsCurrencySymbol());
str->ref();
}
KHTMLAssert(!str || !str->l || str->s);
m_firstTextBox = m_lastTextBox = 0;
m_selectionState = SelectionNone;
#ifdef DEBUG_LAYOUT
QConstString cstr(str->s, str->l);
kdDebug( 6040 ) << "RenderText ctr( "<< cstr.string().length() << " ) '" << cstr.string() << "'" << endl;
#endif
}
void RenderText::setStyle(RenderStyle *_style)
{
if ( style() != _style ) {
bool needToTransformText = (!style() && _style->textTransform() != TTNONE) ||
(style() && style()->textTransform() != _style->textTransform());
RenderObject::setStyle( _style );
if (needToTransformText) {
DOM::DOMStringImpl* textToTransform = originalString();
if (textToTransform)
setText(textToTransform, true);
}
#if APPLE_CHANGES
// setText also calls cacheWidths(), so there is no need to call it again in that case.
else
cacheWidths();
#endif
}
}
RenderText::~RenderText()
{
if(str) str->deref();
}
void RenderText::detach()
{
if (!documentBeingDestroyed()) {
if (firstTextBox()) {
if (isBR()) {
RootInlineBox* next = firstTextBox()->root()->nextRootBox();
if (next)
next->markDirty();
}
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
box->remove();
}
else if (parent())
parent()->dirtyLinesFromChangedChild(this, false);
}
deleteTextBoxes();
RenderObject::detach();
}
void RenderText::extractTextBox(InlineTextBox* box)
{
m_lastTextBox = box->prevTextBox();
if (box == m_firstTextBox)
m_firstTextBox = 0;
if (box->prevTextBox())
box->prevTextBox()->setNextLineBox(0);
box->setPreviousLineBox(0);
for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
curr->setExtracted();
}
void RenderText::attachTextBox(InlineTextBox* box)
{
if (m_lastTextBox) {
m_lastTextBox->setNextLineBox(box);
box->setPreviousLineBox(m_lastTextBox);
}
else
m_firstTextBox = box;
InlineTextBox* last = box;
for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
curr->setExtracted(false);
last = curr;
}
m_lastTextBox = last;
}
void RenderText::removeTextBox(InlineTextBox* box)
{
if (box == m_firstTextBox)
m_firstTextBox = box->nextTextBox();
if (box == m_lastTextBox)
m_lastTextBox = box->prevTextBox();
if (box->nextTextBox())
box->nextTextBox()->setPreviousLineBox(box->prevTextBox());
if (box->prevTextBox())
box->prevTextBox()->setNextLineBox(box->nextTextBox());
}
void RenderText::deleteTextBoxes()
{
if (firstTextBox()) {
RenderArena* arena = renderArena();
InlineTextBox *curr = firstTextBox(), *next = 0;
while (curr) {
next = curr->nextTextBox();
curr->detach(arena);
curr = next;
}
m_firstTextBox = m_lastTextBox = 0;
}
}
bool RenderText::isTextFragment() const
{
return false;
}
DOM::DOMStringImpl* RenderText::originalString() const
{
return element() ? element()->string() : 0;
}
void RenderText::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
{
for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
rects.append(QRect(_tx + box->xPos(),
_ty + box->yPos(),
box->width(),
box->height()));
}
InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos) const
{
// The text runs point to parts of the rendertext's str string
// (they don't include '\n')
// Find the text run that includes the character at @p offset
// and return pos, which is the position of the char in the run.
if (!m_firstTextBox)
return 0;
InlineTextBox* s = m_firstTextBox;
int off = s->m_len;
while (offset > off && s->nextTextBox())
{
s = s->nextTextBox();
off = s->m_start + s->m_len;
}
// we are now in the correct text run
pos = (offset > off ? s->m_len : s->m_len - (off - offset) );
return s;
}
VisiblePosition RenderText::positionForCoordinates(int _x, int _y)
{
if (!firstTextBox() || stringLength() == 0)
return VisiblePosition(element(), 0, DOWNSTREAM);
int absx, absy;
containingBlock()->absolutePosition(absx, absy);
if (firstTextBox() && _y < absy + firstTextBox()->root()->bottomOverflow() && _x < absx + firstTextBox()->m_x) {
// at the y coordinate of the first line or above
// and the x coordinate is to the left than the first text box left edge
return VisiblePosition(element(), firstTextBox()->m_start, DOWNSTREAM);
}
if (lastTextBox() && _y >= absy + lastTextBox()->root()->topOverflow() && _x >= absx + lastTextBox()->m_x + lastTextBox()->m_width) {
// at the y coordinate of the last line or below
// and the x coordinate is to the right than the last text box right edge
return VisiblePosition(element(), lastTextBox()->m_start + lastTextBox()->m_len, DOWNSTREAM);
}
for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
if (_y >= absy + box->root()->topOverflow() && _y < absy + box->root()->bottomOverflow()) {
if (_x < absx + box->m_x + box->m_width) {
// and the x coordinate is to the left of the right edge of this box
// check to see if position goes in this box
int offset = box->offsetForPosition(_x - absx);
if (offset != -1) {
EAffinity affinity = offset >= box->m_len && !box->nextOnLine() ? UPSTREAM : DOWNSTREAM;
return VisiblePosition(element(), offset + box->m_start, affinity);
}
}
else if (!box->prevOnLine() && _x < absx + box->m_x) {
// box is first on line
// and the x coordinate is to the left of the first text box left edge
return VisiblePosition(element(), box->m_start, DOWNSTREAM);
}
else if (!box->nextOnLine() && _x >= absx + box->m_x + box->m_width)
// box is last on line
// and the x coordinate is to the right of the last text box right edge
return VisiblePosition(element(), box->m_start + box->m_len, UPSTREAM);
}
}
return VisiblePosition(element(), 0, DOWNSTREAM);
}
static RenderObject *firstRendererOnNextLine(InlineBox *box)
{
if (!box)
return 0;
RootInlineBox *root = box->root();
if (!root)
return 0;
if (root->endsWithBreak())
return 0;
RootInlineBox *nextRoot = root->nextRootBox();
if (!nextRoot)
return 0;
InlineBox *firstChild = nextRoot->firstChild();
if (!firstChild)
return 0;
return firstChild->object();
}
static RenderObject *lastRendererOnPrevLine(InlineBox *box)
{
if (!box)
return 0;
RootInlineBox *root = box->root();
if (!root)
return 0;
if (root->endsWithBreak())
return 0;
RootInlineBox *prevRoot = root->prevRootBox();
if (!prevRoot)
return 0;
InlineBox *lastChild = prevRoot->lastChild();
if (!lastChild)
return 0;
return lastChild->object();
}
QRect RenderText::caretRect(int offset, EAffinity affinity, int *extraWidthToEndOfLine)
{
if (!firstTextBox() || stringLength() == 0) {
return QRect();
}
// Find the text box for the given offset
InlineTextBox *box = 0;
for (box = firstTextBox(); box; box = box->nextTextBox()) {
if ((offset >= box->m_start) && (offset <= box->m_start + box->m_len)) {
// Check if downstream affinity would make us move to the next line.
InlineTextBox *nextBox = box->nextTextBox();
if (offset == box->m_start + box->m_len && affinity == DOWNSTREAM && nextBox && !box->nextOnLine()) {
// We're at the end of a line broken on a word boundary and affinity is downstream.
// Try to jump down to the next line.
if (nextBox) {
// Use the next text box
box = nextBox;
offset = box->m_start;
} else {
// Look on the next line
RenderObject *object = firstRendererOnNextLine(box);
if (object)
return object->caretRect(0, affinity);
}
} else {
InlineTextBox *prevBox = box->prevTextBox();
if (offset == box->m_start && affinity == UPSTREAM && prevBox && !box->prevOnLine()) {
if (prevBox) {
box = prevBox;
offset = box->m_start + box->m_len;
} else {
RenderObject *object = lastRendererOnPrevLine(box);
if (object)
return object->caretRect(0, affinity);
}
}
}
break;
}
}
if (!box) {
return QRect();
}
int height = box->root()->bottomOverflow() - box->root()->topOverflow();
int top = box->root()->topOverflow();
const QFontMetrics &fm = metrics(box->isFirstLineStyle());
int left;
if (box->m_reversed) {
long len = box->m_start+box->m_len-offset;
QString string(str->s +offset,len);
left = box->m_x + fm.boundingRect(string,len).right();
} else {
long len = offset - box->m_start; // the number of characters we are into the string
QString string(str->s + box->m_start,len);
left = box->m_x + fm.boundingRect(string,len).right();
}
// FIXME: should we use the width of the root inline box or the
// width of the containing block for this?
if (extraWidthToEndOfLine)
*extraWidthToEndOfLine = (box->root()->width() + box->root()->xPos()) - (left + 1);
int absx, absy;
absolutePosition(absx,absy);
left += absx;
top += absy;
// FIXME: Need the +1 to match caret position of other programs on Macintosh.
// Would be better to somehow derive it once we understand exactly why it's needed.
left += 1;
RenderBlock *cb = containingBlock();
int availableWidth = cb->lineWidth(top);
if (style()->whiteSpace() == NORMAL)
left = kMin(left, absx + box->m_x + availableWidth - 1);
return QRect(left, top, 1, height);
}
void RenderText::posOfChar(int chr, int &x, int &y)
{
absolutePosition( x, y, false );
//if( chr > (int) str->l )
//chr = str->l;
int pos;
InlineTextBox * s = findNextInlineTextBox( chr, pos );
if ( s )
{
// s is the line containing the character
x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
y += s->m_y;
}
}
#ifdef NOKIA_CHANGES
void RenderText::getRenderersInRect(QPtrList<BoxInfo>& boxInfoList,int deltaX,int deltaY,const QRect& rect)
{
if(!firstTextBox()||!lastTextBox()) return;
if (deltaY + firstTextBox()->yPos() > rect.y() + rect.height()) return;
if (deltaY + lastTextBox()->yPos() + lastTextBox()->height() < rect.y()) return;
BoxInfo* sel = new BoxInfo;
sel->renderObject = this;
sel->absoluteXPos = deltaX;
sel->absoluteYPos = deltaY;
sel->width = width();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -