📄 khtml_caret.cpp
字号:
#endif // must be an inline-block, inline-table, or any RenderReplaced bool rtl = ps->direction() == RTL; const QFontMetrics &pfm = ps->fontMetrics(); if (coalesceOutsideBoxes) { if (sbp.equalsBox(box, true, false)) { sbp.it = lastCoalescedBox; Q_ASSERT(!sbp.found); sbp.found = true; } } else { addCreatedInlineBoxEdge(box, pfm, true, rtl); sbp.check(preEnd()); } caret_boxes.append(new CaretBox(box, false, false)); sbp.check(preEnd()); addCreatedInlineBoxEdge(box, pfm, false, rtl); lastCoalescedBox = preEnd(); sbp.check(lastCoalescedBox); coalesceOutsideBoxes = true; }/*end if*/ }/*next box*/}#undef DEBUG_ACIBvoid CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm) /*KDE_NO_EXPORT*/{ CaretBox *caretBox = new CaretBox(flowBox, false, false); caret_boxes.append(caretBox); // afaik an inner flow box can only have the width 0, therefore we don't // have to care for rtl or alignment // ### can empty inline elements have a width? css 2 spec isn't verbose about it caretBox->_y += flowBox->baseline() - fm.ascent(); caretBox->_h = fm.height();}void CaretBoxLine::addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/{ CaretBox *caretBox = new CaretBox(flowBox, true, !left); caret_boxes.append(caretBox); if (left ^ rtl) caretBox->_x -= flowBox->paddingLeft() + flowBox->borderLeft() + 1; else caretBox->_x += caretBox->_w + flowBox->paddingRight() + flowBox->borderRight(); caretBox->_y += flowBox->baseline() - fm.ascent(); caretBox->_h = fm.height(); caretBox->_w = 1;}void CaretBoxLine::addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/{ CaretBox *caretBox = new CaretBox(box, true, !left); caret_boxes.append(caretBox); if (left ^ rtl) caretBox->_x--; else caretBox->_x += caretBox->_w; caretBox->_y += box->baseline() - fm.ascent(); caretBox->_h = fm.height(); caretBox->_w = 1;}CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter, InlineFlowBox *basicFlowBox, InlineBox *seekBox, bool seekOutside, bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject)// KDE_NO_EXPORT{ // Iterate all inline boxes within this inline flow box. // Caret boxes will be created for each // - outside begin of an inline flow box (except for the basic inline flow box) // - outside end of an inline flow box (except for the basic inline flow box) // - inside of an empty inline flow box // - outside begin of an inline box resembling a replaced element // - outside end of an inline box resembling a replaced element // - inline text box // - inline replaced box CaretBoxLine *result = new CaretBoxLine(basicFlowBox); deleter->append(result); SeekBoxParams sbp(seekBox, seekOutside, seekOutsideEnd, seekObject, iter); // iterate recursively, I'm too lazy to do it iteratively result->addConvertedInlineBox(basicFlowBox, sbp); if (!sbp.found) sbp.it = result->end(); return result;}CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter, RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KDE_NO_EXPORT*/{ int _x = cb->xPos(); int _y = cb->yPos(); int height; int width = 1; // no override is indicated in boxes if (outside) { RenderStyle *s = cb->element() && cb->element()->parent() && cb->element()->parent()->renderer() ? cb->element()->parent()->renderer()->style() : cb->style(); bool rtl = s->direction() == RTL; const QFontMetrics &fm = s->fontMetrics(); height = fm.height(); if (!outsideEnd) { _x--; } else { _x += cb->width(); } int hl = fm.leading() / 2; int baseline = cb->baselinePosition(false); if (!cb->isReplaced() || cb->style()->display() == BLOCK) { if (!outsideEnd ^ rtl) _y -= fm.leading() / 2; else _y += kMax(cb->height() - fm.ascent() - hl, 0); } else { _y += baseline - fm.ascent() - hl; } } else { // !outside RenderStyle *s = cb->style(); const QFontMetrics &fm = s->fontMetrics(); height = fm.height(); _x += cb->borderLeft() + cb->paddingLeft(); _y += cb->borderTop() + cb->paddingTop(); // ### regard direction switch (s->textAlign()) { case LEFT: case KHTML_LEFT: case TAAUTO: // ### find out what this does case JUSTIFY: break; case CENTER: case KHTML_CENTER: _x += cb->contentWidth() / 2; break; case KHTML_RIGHT: case RIGHT: _x += cb->contentWidth(); break; }/*end switch*/ }/*end if*/ CaretBoxLine *result = new CaretBoxLine; deleter->append(result); result->caret_boxes.append(new CaretBox(_x, _y, width, height, cb, outside, outsideEnd)); iter = result->begin(); return result;}#if DEBUG_CARETMODE > 0void CaretBoxLine::dump(QTextStream &ts, const QString &ind) const{ ts << ind << "cbl: baseFlowBox@" << basefb << endl; QString ind2 = ind + " "; for (size_t i = 0; i < caret_boxes.size(); i++) { if (i > 0) ts << endl; caret_boxes[i]->dump(ts, ind2); }}#endif// == caret mode related helper functions/** seeks the root line box that is the parent of the given inline box. * @param b given inline box * @param base base render object which not to step over. If \c base's * inline flow box is hit before the root line box, the flow box * is returned instead. * @return the root line box or the base flow box. */inline InlineFlowBox *seekBaseFlowBox(InlineBox *b, RenderObject *base = 0){ // Seek root line box or base inline flow box, if \c base is interfering. while (b->parent() && b->object() != base) { b = b->parent(); }/*wend*/ Q_ASSERT(b->isInlineFlowBox()); return static_cast<InlineFlowBox *>(b);}/** determines whether the given element is a block level replaced element. */inline bool isBlockRenderReplaced(RenderObject *r){ return r->isRenderReplaced() && r->style()->display() == BLOCK;}/** determines the caret line box that contains the given position. * * If the node does not map to a render object, the function will snap to * the next suitable render object following it. * * @param node node to begin with * @param offset zero-based offset within node. * @param cblDeleter deleter for caret box lines * @param base base render object which the caret must not be placed beyond. * @param r_ofs adjusted offset within render object * @param caretBoxIt returns an iterator to the caret box that contains the * given position. * @return the determined caret box lineor 0 if either the node is 0 or * there is no inline flow box containing this node. The containing block * will still be set. If it is 0 too, @p node was invalid. */static CaretBoxLine* findCaretBoxLine(DOM::NodeImpl *node, long offset, CaretBoxLineDeleter *cblDeleter, RenderObject *base, long &r_ofs, CaretBoxIterator &caretBoxIt){ bool outside, outsideEnd; RenderObject *r = findRenderer(node, offset, base, r_ofs, outside, outsideEnd); if (!r) { return 0; }#if DEBUG_CARETMODE > 0 kdDebug(6200) << "=================== findCaretBoxLine" << endl; kdDebug(6200) << "node " << node << " offset: " << offset << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl;#endif // There are two strategies to find the correct line box. (The third is failsafe) // (A) First, if node's renderer is a RenderText, we only traverse its text // runs and return the root line box (saves much time for long blocks). // This should be the case 99% of the time. // (B) Second, we derive the inline flow box directly when the renderer is // a RenderBlock, RenderInline, or blocked RenderReplaced. // (C) Otherwise, we iterate linearly through all line boxes in order to find // the renderer. // (A) if (r->isText()) do { RenderText *t = static_cast<RenderText *>(r); int dummy; InlineBox *b = t->findInlineTextBox(offset, dummy, true); // Actually b should never be 0, but some render texts don't have text // boxes, so we insert the last run as an error correction. // If there is no last run, we resort to (B) if (!b) { if (t->m_lines.count() > 0) b = t->m_lines[t->m_lines.count() - 1]; else break; }/*end if*/ Q_ASSERT(b); outside = false; // text boxes cannot have outside positions InlineFlowBox *baseFlowBox = seekBaseFlowBox(b, base);#if DEBUG_CARETMODE > 2 kdDebug(6200) << "text-box b: " << b << " baseFlowBox: " << baseFlowBox << (b && b->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(b->object())->str->s+b->minOffset(), kMin(b->maxOffset() - b->minOffset(), 15L)).string()) : QString::null) << endl;#endif#if 0 if (t->containingBlock()->isListItem()) dumpLineBoxes(static_cast<RenderFlow *>(t->containingBlock()));#endif#if DEBUG_CARETMODE > 0 kdDebug(6200) << "=================== end findCaretBoxLine (renderText)" << endl;#endif return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox, b, outside, outsideEnd, caretBoxIt); } while(false);/*end if*/ // (B) bool isrepl = isBlockRenderReplaced(r); if (r->isRenderBlock() || r->isRenderInline() || isrepl) { RenderFlow *flow = static_cast<RenderFlow *>(r); InlineFlowBox *firstLineBox = isrepl ? 0 : flow->firstLineBox(); // On render blocks, if we are outside, or have a totally empty render // block, we simply construct a special caret box line. // The latter case happens only when the render block is a leaf object itself. if (isrepl || r->isRenderBlock() && (outside || !firstLineBox) || r->isRenderInline() && !firstLineBox) { #if DEBUG_CARETMODE > 0 kdDebug(6200) << "=================== end findCaretBoxLine (box " << (outside ? (outsideEnd ? "outside end" : "outside begin") : "inside") << ")" << endl; #endif Q_ASSERT(r->isBox()); return CaretBoxLine::constructCaretBoxLine(cblDeleter, static_cast<RenderBox *>(r), outside, outsideEnd, caretBoxIt); }/*end if*/ kdDebug(6200) << "firstlinebox " << firstLineBox << endl; InlineFlowBox *baseFlowBox = seekBaseFlowBox(firstLineBox, base); return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox, firstLineBox, outside, outsideEnd, caretBoxIt); }/*end if*/ RenderBlock *cb = r->containingBlock(); //if ( !cb ) return 0L; Q_ASSERT(cb); // ### which element doesn't have a block as its containing block? // Is it still possible after the RenderBlock/RenderInline merge? if (!cb->isRenderBlock()) { kdWarning() << "containing block is no render block!!! crash imminent" << endl; }/*end if*/ InlineFlowBox *flowBox = cb->firstLineBox(); // (C) // This case strikes when the element is replaced, but neither a // RenderBlock nor a RenderInline if (!flowBox) { // ### utter emergency (why is this possible at all?)// flowBox = generateDummyFlowBox(arena, cb, r);// if (ibox) *ibox = flowBox->firstChild();// outside = outside_end = true;// kdWarning() << "containing block contains no inline flow boxes!!! crash imminent" << endl;#if DEBUG_CARETMODE > 0 kdDebug(6200) << "=================== end findCaretBoxLine (2)" << endl;#endif return CaretBoxLine::constructCaretBoxLine(cblDeleter, cb, outside, outsideEnd, caretBoxIt); }/*end if*/ // We iterate the inline flow boxes of the containing block until // we find the given node. This has one major flaw: it is linear, and therefore // painfully slow for really large blocks. for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {#if DEBUG_CARETMODE > 0 kdDebug(6200) << "[scan line]" << endl;#endif // construct a caret line box and stop when the element is contained within InlineFlowBox *baseFlowBox = seekBaseFlowBox(flowBox, base); CaretBoxLine *cbl = CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox, 0, outside, outsideEnd, caretBoxIt, r);#if DEBUG_CARETMODE > 5 kdDebug(6200) << cbl->information() << endl;#endif if (caretBoxIt != cbl->end()) {#if DEBUG_CARETMODE > 0 kdDebug(6200) << "=================== end findCaretBoxLine (3)" << endl;#endif return cbl; } }/*next flowBox*/ // no inline flow box found, approximate to nearest following node. // Danger: this is O(n^2). It's only called to recover from // errors, that means, theoretically, never. (Practically, far too often :-( ) Q_ASSERT(!flowBox); CaretBoxLine *cbl = findCaretBoxLine(nextLeafNode(node, base ? base->element() : 0), 0, cblDeleter, base, r_ofs, caretBoxIt);#if DEBUG_CARETMODE > 0 kdDebug(6200) << "=================== end findCaretBoxLine" << endl;#endif return cbl;}/** finds the innermost table object @p r is contained within, but no * farther than @p cb. * @param r leaf element to begin with * @param cb bottom element where to stop search at least. * @return the table object or 0 if none found. */static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb){ while (r && r != cb && !r->isTable()) r = r->parent(); return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;}/** checks whether @p r is a descendant of @p cb, or r == cb
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -