X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Frowpainter.C;h=b992b00143a71425bccc3eff85218e728ef80add;hb=9ee46b846e5e84ad40ceda4f4af94aeb86cd90a2;hp=1c0e9dbf59ab5594775f54ec84d61b8d5c86aa4e;hpb=af5acb6dc7e2f77a1f7a69d4f1bbfa25c7b50ea3;p=lyx.git diff --git a/src/rowpainter.C b/src/rowpainter.C index 1c0e9dbf59..b992b00143 100644 --- a/src/rowpainter.C +++ b/src/rowpainter.C @@ -40,6 +40,8 @@ #include "support/textutils.h" +#include + using lyx::pos_type; using lyx::pit_type; @@ -81,16 +83,9 @@ private: /// return left margin int leftMargin() const; - /// return the font at the given pos - LyXFont const getFont(lyx::pos_type pos) const; - /// return the label font for this row LyXFont const getLabelFont() const; - /// return pixel width for the given pos - int singleWidth(lyx::pos_type pos) const; - int singleWidth(lyx::pos_type pos, char c) const; - /// bufferview to paint on BufferView const & bv_; @@ -108,6 +103,9 @@ private: pit_type const pit_; Paragraph const & par_; + /// is row erased? (change tracking) + bool erased_; + // Looks ugly - is double const xo_; int const yo_; // current baseline @@ -123,6 +121,7 @@ RowPainter::RowPainter(PainterInfo & pi, LyXText const & text, pit_type pit, Row const & row, int x, int y) : bv_(*pi.base.bv), pain_(pi.pain), text_(text), pars_(text.paragraphs()), row_(row), pit_(pit), par_(text.paragraphs()[pit]), + erased_(pi.erased_), xo_(x), yo_(y), width_(text_.width()) { RowMetrics m = text_.computeRowMetrics(pit, row_); @@ -140,28 +139,6 @@ RowPainter::RowPainter(PainterInfo & pi, } -/// "temporary" -LyXFont const RowPainter::getFont(pos_type pos) const -{ - LyXFont pf(text_.getFont(par_, pos)); - text_.applyOuterFont(pf); - return pf; -} - - -int RowPainter::singleWidth(lyx::pos_type pos) const -{ - return text_.singleWidth(par_, pos); -} - - -int RowPainter::singleWidth(lyx::pos_type pos, char c) const -{ - LyXFont const & font = text_.getFont(par_, pos); - return text_.singleWidth(par_, pos, c, font); -} - - LyXFont const RowPainter::getLabelFont() const { return text_.getLabelFont(par_); @@ -179,8 +156,13 @@ void RowPainter::paintInset(pos_type const pos, LyXFont const & font) InsetBase const * inset = par_.getInset(pos); BOOST_ASSERT(inset); PainterInfo pi(const_cast(&bv_), pain_); - pi.base.font = font; + // FIXME: We should always use font, see documentation of + // noFontChange() in insetbase.h. + pi.base.font = inset->noFontChange() ? + bv_.buffer()->params().getLyXTextClass().defaultfont() : + font; pi.ltr_pos = (text_.bidi.level(pos) % 2 == 0); + pi.erased_ = erased_ || isDeletedText(par_, pos); theCoords.insets().add(inset, int(x_), yo_); inset->drawSelection(pi, int(x_), yo_); inset->draw(pi, int(x_), yo_); @@ -206,7 +188,8 @@ void RowPainter::paintHebrewComposeChar(pos_type & vpos, LyXFont const & font) c = par_.getChar(i); if (!Encodings::IsComposeChar_hebrew(c)) { if (IsPrintableNonspace(c)) { - int const width2 = singleWidth(i, c); + int const width2 = + text_.singleWidth(par_, i, c, text_.getFont(par_, i)); // dalet / resh dx = (c == 'ø' || c == 'ã') ? width2 - width @@ -239,7 +222,8 @@ void RowPainter::paintArabicComposeChar(pos_type & vpos, LyXFont const & font) c = par_.getChar(i); if (!Encodings::IsComposeChar_arabic(c)) { if (IsPrintableNonspace(c)) { - int const width2 = singleWidth(i, c); + int const width2 = + text_.singleWidth(par_, i, c, text_.getFont(par_, i)); dx = (width2 - width) / 2; } break; @@ -255,8 +239,7 @@ void RowPainter::paintChars(pos_type & vpos, LyXFont font, { pos_type pos = text_.bidi.vis2log(vpos); pos_type const end = row_.endpos(); - std::pair const font_span - = par_.getFontSpan(pos); + FontSpan const font_span = par_.fontSpan(pos); Change::Type const prev_change = par_.lookupChange(pos); // first character @@ -270,7 +253,7 @@ void RowPainter::paintChars(pos_type & vpos, LyXFont font, // collect as much similar chars as we can for (++vpos ; vpos < end ; ++vpos) { pos = text_.bidi.vis2log(vpos); - if (pos < font_span.first || pos > font_span.second) + if (pos < font_span.first || pos > font_span.last) break; if (prev_change != par_.lookupChange(pos)) @@ -322,14 +305,12 @@ void RowPainter::paintForeignMark(double orig_x, LyXFont const & font) void RowPainter::paintFromPos(pos_type & vpos) { pos_type const pos = text_.bidi.vis2log(vpos); - - LyXFont const & orig_font = getFont(pos); + LyXFont orig_font = text_.getFont(par_, pos); + text_.applyOuterFont(orig_font); double const orig_x = x_; - char const c = par_.getChar(pos); - - if (c == Paragraph::META_INSET) { + if (par_.isInset(pos)) { paintInset(pos, orig_font); ++vpos; paintForeignMark(orig_x, orig_font); @@ -337,11 +318,12 @@ void RowPainter::paintFromPos(pos_type & vpos) } // usual characters, no insets + char const c = par_.getChar(pos); // special case languages - bool const hebrew = (orig_font.language()->lang() == "hebrew"); - bool const arabic = - orig_font.language()->lang() == "arabic" && + std::string const & lang = orig_font.language()->lang(); + bool const hebrew = lang == "hebrew"; + bool const arabic = lang == "arabic" && (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 || lyxrc.font_norm_type == LyXRC::ISO_10646_1); @@ -513,9 +495,7 @@ void RowPainter::paintFirst() } else { spacing_val = buffer.params().spacing().getValue(); } -#ifdef WITH_WARNINGS -#warning Look is this correct? -#endif + int const labeladdon = int(font_metrics::maxHeight(font) * layout->spacing.getValue() * spacing_val); int const maxdesc = int(font_metrics::maxDescent(font) * layout->spacing.getValue() * spacing_val) @@ -550,11 +530,12 @@ void RowPainter::paintFirst() if (!par_.getLabelstring().empty()) { string const str = par_.getLabelstring(); double spacing_val = 1.0; - if (!parparams.spacing().isDefault()) { + if (!parparams.spacing().isDefault()) spacing_val = parparams.spacing().getValue(); - } else { + else spacing_val = buffer.params().spacing().getValue(); - } + + int const labeladdon = int(font_metrics::maxHeight(font) * layout->spacing.getValue() * spacing_val); int maxdesc = int(font_metrics::maxDescent(font) * layout->spacing.getValue() * spacing_val @@ -562,14 +543,15 @@ void RowPainter::paintFirst() double x = x_; if (layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) { - x = ((is_rtl ? leftMargin() : x_) - + width_ - text_.rightMargin(par_)) / 2; + if (is_rtl) + x = leftMargin(); + x += (width_ - text_.rightMargin(par_) - leftMargin()) / 2; x -= font_metrics::width(str, font) / 2; } else if (is_rtl) { x = width_ - leftMargin() - font_metrics::width(str, font); } - pain_.text(int(x), yo_ - maxdesc, str, font); + pain_.text(int(x), yo_ - maxdesc - labeladdon, str, font); } } } @@ -630,6 +612,10 @@ void RowPainter::paintText() bool is_struckout = false; int last_strikeout_x = 0; + // Use font span to speed things up, see below + FontSpan font_span; + LyXFont font; + for (pos_type vpos = row_.pos(); vpos < end; ) { if (x_ > bv_.workWidth()) break; @@ -641,7 +627,15 @@ void RowPainter::paintText() continue; } - const int width_pos = singleWidth(pos); + // Use font span to speed things up, see above + if (vpos < font_span.first || vpos > font_span.last) { + font_span = par_.fontSpan(vpos); + font = text_.getFont(par_, vpos); + } + + const int width_pos = + text_.singleWidth(par_, pos, par_.getChar(pos), font); + if (x_ + width_pos < 0) { x_ += width_pos; ++vpos; @@ -658,10 +652,10 @@ void RowPainter::paintText() bool const highly_editable_inset = par_.isInset(pos) && isHighlyEditableInset(par_.getInset(pos)); - // if we reach the end of a struck out range, paint it - // we also don't paint across things like tables + // If we reach the end of a struck out range, paint it. + // We also don't paint across things like tables if (running_strikeout && (highly_editable_inset || !is_struckout)) { - // calculate 1/3 height of the buffer's default font + // Calculate 1/3 height of the buffer's default font int const middle = yo_ - font_metrics::maxAscent(text_.defaultfont_) / 3; pain_.line(last_strikeout_x, middle, int(x_), middle, @@ -724,8 +718,32 @@ void RowPainter::paintText() } +lyx::size_type calculateRowSignature(Row const & row, Paragraph const & par) +{ + boost::crc_32_type crc; + for (lyx::pos_type i = row.pos(); i < row.endpos(); ++i) { + const unsigned char b[] = { par.getChar(i) }; + crc.process_bytes(b, 1); + } + return crc.checksum(); +} + + +bool isCursorOnRow(PainterInfo & pi, pit_type pit, RowList::const_iterator rit) +{ + LCursor & cur = pi.base.bv->cursor(); + for (lyx::size_type d = 0; d < cur.depth(); d++) + if (cur[d].pit() == pit + && cur[d].pos() >= rit->pos() + && cur[d].pos() <= rit->endpos()) + return true; + return false; +} + + void paintPar - (PainterInfo & pi, LyXText const & text, pit_type pit, int x, int y) + (PainterInfo & pi, LyXText const & text, pit_type pit, int x, int y, + bool repaintAll) { // lyxerr << " paintPar: pit: " << pit << " at y: " << y << endl; static NullPainter nop; @@ -739,22 +757,49 @@ void paintPar theCoords.parPos()[&text][pit] = Point(x, y); y -= rb->ascent(); - for (RowList::const_iterator rit = rb; rit != re; ++rit) { + lyx::size_type rowno(0); + for (RowList::const_iterator rit = rb; rit != re; ++rit, ++rowno) { y += rit->ascent(); - bool const inside = (y + rit->descent() >= 0 - && y - rit->ascent() < ww); - RowPainter rp(inside ? pi : nullpi, text, pit, *rit, x, y); + // Row signature; has row changed since last paint? + lyx::size_type const row_sig = calculateRowSignature(*rit, par); + + bool cursor_on_row = isCursorOnRow(pi, pit, rit); + + // If selection is on, the current row signature differs from + // from cache, or cursor is inside an inset _on this row_, + // then paint the row + if (repaintAll || par.rowSignature()[rowno] != row_sig + || cursor_on_row) { + // Add to row signature cache + par.rowSignature()[rowno] = row_sig; + + bool const inside = (y + rit->descent() >= 0 + && y - rit->ascent() < ww); + RowPainter rp(inside ? pi : nullpi, text, pit, *rit, x, y); + // Clear background of this row + // (if paragraph background was not cleared) + if (!repaintAll) { + pi.pain.fillRectangle(x, y - rit->ascent(), + pi.base.bv->workWidth(), rit->height(), + text.backgroundColor()); + } + + // Instrumentation for testing row cache (see also + // 12 lines lower): + //lyxerr << "#"; + rp.paintAppendix(); + rp.paintDepthBar(); + rp.paintChangeBar(); + if (rit == rb) + rp.paintFirst(); + if (rit + 1 == re) + rp.paintLast(); + rp.paintText(); + } y += rit->descent(); - rp.paintAppendix(); - rp.paintDepthBar(); - rp.paintChangeBar(); - if (rit == rb) - rp.paintFirst(); - if (rit + 1 == re) - rp.paintLast(); - rp.paintText(); } + //lyxerr << "." << endl; } } // namespace anon @@ -764,48 +809,51 @@ void paintText(BufferView const & bv, ViewMetricsInfo const & vi) { Painter & pain = bv.painter(); LyXText * const text = bv.text(); + bool const select = bv.cursor().selection(); - // clear background - pain.fillRectangle(0, vi.y1, bv.workWidth(), vi.y2 - vi.y1, - LColor::background); - - // draw selection PainterInfo pi(const_cast(&bv), pain); - - text->drawSelection(pi, 0, 0); + if (select || !vi.singlepar) { + // Clear background (Delegated to rows if no selection) + pain.fillRectangle(0, vi.y1, bv.workWidth(), vi.y2 - vi.y1, + text->backgroundColor()); + } + if (select) { + text->drawSelection(pi, 0, 0); + } int yy = vi.y1; // draw contents for (pit_type pit = vi.p1; pit <= vi.p2; ++pit) { - yy += text->getPar(pit).ascent(); - paintPar(pi, *bv.text(), pit, 0, yy); - yy += text->getPar(pit).descent(); + Paragraph const & par = text->getPar(pit); + yy += par.ascent(); + paintPar(pi, *bv.text(), pit, 0, yy, select || !vi.singlepar); + yy += par.descent(); } - - // paint one paragraph above and one below + // Cache one paragraph above and one below // Note MV: this cannot be suppressed even for singlepar. // Try viewing the User Guide Mobius figure + if (vi.p1 > 0) { text->redoParagraph(vi.p1 - 1); - paintPar(pi, *bv.text(), vi.p1 - 1, 0, - vi.y1 - text->getPar(vi.p1 - 1).descent()); + theCoords.parPos()[bv.text()][vi.p1 - 1] = + Point(0, vi.y1 - text->getPar(vi.p1 - 1).descent()); } if (vi.p2 < lyx::pit_type(text->paragraphs().size()) - 1) { text->redoParagraph(vi.p2 + 1); - paintPar(pi, *bv.text(), vi.p2 + 1, 0, - vi.y2 + text->getPar(vi.p2 + 1).ascent()); + theCoords.parPos()[bv.text()][vi.p2 + 1] = + Point(0, vi.y2 + text->getPar(vi.p2 + 1).ascent()); } // and grey out above (should not happen later) // lyxerr << "par ascent: " << text->getPar(vi.p1).ascent() << endl; - if (vi.y1 > 0) + if (vi.y1 > 0 && !vi.singlepar) pain.fillRectangle(0, 0, bv.workWidth(), vi.y1, LColor::bottomarea); // and possibly grey out below // lyxerr << "par descent: " << text->getPar(vi.p1).ascent() << endl; - if (vi.y2 < bv.workHeight()) + if (vi.y2 < bv.workHeight() && !vi.singlepar) pain.fillRectangle(0, vi.y2, bv.workWidth(), bv.workHeight() - vi.y2, LColor::bottomarea); } @@ -817,7 +865,7 @@ void paintTextInset(LyXText const & text, PainterInfo & pi, int x, int y) y -= text.getPar(0).ascent(); for (int pit = 0; pit < int(text.paragraphs().size()); ++pit) { y += text.getPar(pit).ascent(); - paintPar(pi, text, pit, x, y); + paintPar(pi, text, pit, x, y, true); y += text.getPar(pit).descent(); } }