X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FTextMetrics.cpp;h=0b3bfc1d16a79ce057eb979d2231dda30cfd9974;hb=b08a653f3549e08fffc5318c87da305651ecc197;hp=681985bd73c46033de29b0466fbca68e413b49c1;hpb=32b688225d49582066da3c2027ea8308915fef66;p=lyx.git diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index 681985bd73..0b3bfc1d16 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -20,36 +20,31 @@ #include "TextMetrics.h" #include "Buffer.h" -#include "buffer_funcs.h" #include "BufferParams.h" #include "BufferView.h" #include "CoordCache.h" #include "Cursor.h" #include "CutAndPaste.h" -#include "InsetList.h" -#include "Language.h" #include "Layout.h" #include "LyXRC.h" #include "MetricsInfo.h" #include "ParagraphParameters.h" #include "RowPainter.h" +#include "Session.h" #include "Text.h" #include "TextClass.h" #include "VSpace.h" #include "insets/InsetText.h" -#include "mathed/InsetMathMacroTemplate.h" +#include "mathed/MacroTable.h" #include "frontends/FontMetrics.h" -#include "frontends/Painter.h" #include "frontends/NullPainter.h" -#include "support/convert.h" #include "support/debug.h" #include "support/lassert.h" -#include "support/lyxlib.h" -#include "support/RefChanger.h" +#include "support/Changer.h" #include #include @@ -221,7 +216,7 @@ void TextMetrics::newParMetricsUp() } -bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width, +bool TextMetrics::metrics(MetricsInfo const & mi, Dimension & dim, int min_width, bool const expand_on_multipars) { LBUFERR(mi.base.textwidth > 0); @@ -388,13 +383,13 @@ bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos, || !contains(pit)) return false; - ParagraphMetrics & pm = par_metrics_[pit]; + ParagraphMetrics const & pm = par_metrics_[pit]; // no RTL boundary in empty paragraph if (pm.rows().empty()) return false; - pos_type endpos = pm.getRow(pos - 1, false).endpos(); - pos_type startpos = pm.getRow(pos, false).pos(); + pos_type const endpos = pm.getRow(pos - 1, false).endpos(); + pos_type const startpos = pm.getRow(pos, false).pos(); // no RTL boundary at line start: // abc\n -> toggle to RTL -> abc\n (and not: abc\n| // | | ) @@ -412,7 +407,7 @@ bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos, || par.isSeparator(pos - 1))) return false; - bool left = font.isVisibleRightToLeft(); + bool const left = font.isVisibleRightToLeft(); bool right; if (pos == par.size()) right = par.isRTL(bv_->buffer().params()); @@ -556,35 +551,60 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows) first = row.endpos(); ++row_index; - pm.dim().wid = max(pm.dim().wid, row.width()); + pm.dim().wid = max(pm.dim().wid, row.width() + row.right_margin); pm.dim().des += row.height(); } while (first < par.size() || need_new_row); if (row_index < pm.rows().size()) pm.rows().resize(row_index); - // The space above and below the paragraph. - int const top = parTopSpacing(pit); - pm.rows().front().dim().asc += top; - int const bottom = parBottomSpacing(pit); - pm.rows().back().dim().des += bottom; - pm.dim().des += top + bottom; + // This type of margin can only be handled at the global paragraph level + if (par.layout().margintype == MARGIN_RIGHT_ADDRESS_BOX) { + int offset = 0; + if (par.isRTL(buffer.params())) { + // globally align the paragraph to the left. + int minleft = max_width_; + for (Row const & row : pm.rows()) + minleft = min(minleft, row.left_margin); + offset = right_margin - minleft; + } else { + // globally align the paragraph to the right. + int maxwid = 0; + for (Row const & row : pm.rows()) + maxwid = max(maxwid, row.width()); + offset = max_width_ - right_margin - maxwid; + } + + for (Row & row : pm.rows()) { + row.left_margin += offset; + row.dim().wid += offset; + } + } - pm.dim().asc += pm.rows()[0].ascent(); - pm.dim().des -= pm.rows()[0].ascent(); + // The space above and below the paragraph. + int top = parTopSpacing(pit); + int bottom = parBottomSpacing(pit); // Top and bottom margin of the document (only at top-level) // FIXME: It might be better to move this in another method // specially tailored for the main text. if (text_->isMainText()) { if (pit == 0) - pm.dim().asc += bv_->topMargin(); - ParagraphList const & pars = text_->paragraphs(); - if (pit + 1 == pit_type(pars.size())) { - pm.dim().des += bv_->bottomMargin(); + top += bv_->topMargin(); + if (pit + 1 == pit_type(text_->paragraphs().size())) { + bottom += bv_->bottomMargin(); } } + // Add the top/bottom space to rows and paragraph metrics + pm.rows().front().dim().asc += top; + pm.rows().back().dim().des += bottom; + pm.dim().des += top + bottom; + + // Move the pm ascent to be the same as the first row ascent + pm.dim().asc += pm.rows().front().ascent(); + pm.dim().des -= pm.rows().front().ascent(); + changed |= old_dim.height() != pm.dim().height(); return changed; @@ -676,7 +696,7 @@ void TextMetrics::setRowAlignment(Row & row, int width) const } // are there any hfills in the row? - ParagraphMetrics & pm = par_metrics_[row.pit()]; + ParagraphMetrics const & pm = par_metrics_[row.pit()]; int nh = numberOfHfills(row, pm, par.beginOfBody()); int hfill = 0; int hfill_rem = 0; @@ -785,22 +805,6 @@ int TextMetrics::labelFill(Row const & row) const } -#if 0 -// Not used, see TextMetrics::breakRow -// this needs special handling - only newlines count as a break point -static pos_type addressBreakPoint(pos_type i, Paragraph const & par) -{ - pos_type const end = par.size(); - - for (; i < end; ++i) - if (par.isNewline(i)) - return i + 1; - - return end; -} -#endif - - int TextMetrics::labelEnd(pit_type const pit) const { // labelEnd is only needed if the layout fills a flushleft label. @@ -876,6 +880,10 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const { LATTEST(row.empty()); Paragraph const & par = text_->getPar(row.pit()); + Buffer const & buf = text_->inset().buffer(); + BookmarksSection::BookmarkPosList bpl = + theSession().bookmarks().bookmarksInPar(buf.fileName(), par.id()); + pos_type const end = par.size(); pos_type const pos = row.pos(); pos_type const body_pos = par.beginOfBody(); @@ -892,14 +900,6 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const // the width available for the row. int const width = max_width_ - row.right_margin; -#if 0 - //FIXME: As long as leftMargin() is not correctly implemented for - // MARGIN_RIGHT_ADDRESS_BOX, we should also not do this here. - // Otherwise, long rows will be painted off the screen. - if (par.layout().margintype == MARGIN_RIGHT_ADDRESS_BOX) - return addressBreakPoint(pos, par); -#endif - // check for possible inline completion DocIterator const & ic_it = bv_->inlineCompletionPos(); pos_type ic_pos = -1; @@ -910,7 +910,24 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const // or the end of the par, then build a representation of the row. pos_type i = pos; FontIterator fi = FontIterator(*this, par, row.pit(), pos); - while (i < end && (i == pos || row.width() <= width)) { + // The real stopping condition is a few lines below. + while (true) { + // Firstly, check whether there is a bookmark here. + if (lyxrc.bookmarks_visibility == LyXRC::BMK_INLINE) + for (auto const & bp_p : bpl) + if (bp_p.second == i) { + Font f = *fi; + f.fontInfo().setColor(Color_bookmark); + // ❶ U+2776 DINGBAT NEGATIVE CIRCLED DIGIT ONE + char_type const ch = 0x2775 + bp_p.first; + row.addVirtual(i, docstring(1, ch), f, Change()); + } + + // The stopping condition is here so that the display of a + // bookmark can take place at paragraph start too. + if (i >= end || (i != pos && row.width() > width)) + break; + char_type c = par.getChar(i); // The most special cases are handled first. if (par.isInset(i)) { @@ -1004,9 +1021,7 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const // in the paragraph. Font f(text_->layoutFont(row.pit())); f.fontInfo().setColor(Color_paragraphmarker); - BufferParams const & bparams - = text_->inset().buffer().params(); - f.setLanguage(par.getParLanguage(bparams)); + f.setLanguage(par.getParLanguage(buf.params())); // ¶ U+00B6 PILCROW SIGN row.addVirtual(end, docstring(1, char_type(0x00B6)), f, change); } @@ -1171,6 +1186,9 @@ void TextMetrics::setRowHeight(Row & row) const row.dim().asc = maxasc; row.dim().des = maxdes; + + // This is useful for selections + row.contents_dim() = row.dim(); } @@ -1337,7 +1355,7 @@ Row const & TextMetrics::getPitAndRowNearY(int & y, pit_type & pit, { ParagraphMetrics const & pm = par_metrics_[pit]; - int yy = pm.position() - pm.rows().front().ascent(); + int yy = pm.position() - pm.ascent(); LBUFERR(!pm.rows().empty()); RowList::const_iterator rit = pm.rows().begin(); RowList::const_iterator rlast = pm.rows().end(); @@ -1486,7 +1504,7 @@ InsetList::Element * TextMetrics::checkInsetHit(pit_type pit, int x, int y) } LYXERR(Debug::DEBUG, "No inset hit. "); - return 0; + return nullptr; } @@ -1604,6 +1622,8 @@ void TextMetrics::deleteLineForward(Cursor & cur) int TextMetrics::leftMargin(pit_type pit) const { + // FIXME: what is the semantics? It depends on whether the + // paragraph is empty! return leftMargin(pit, text_->paragraphs()[pit].size()); } @@ -1616,7 +1636,10 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const LASSERT(pit < int(pars.size()), return 0); Paragraph const & par = pars[pit]; LASSERT(pos >= 0, return 0); - LASSERT(pos <= par.size(), return 0); + // We do not really care whether pos > par.size(), since we do not + // access the data. It can be actually useful, when querying the + // margin without indentation (see leftMargin(pit_type). + Buffer const & buffer = bv_->buffer(); //lyxerr << "TextMetrics::leftMargin: pit: " << pit << " pos: " << pos << endl; DocumentClass const & tclass = buffer.params().documentClass(); @@ -1731,25 +1754,10 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const } break; - case MARGIN_RIGHT_ADDRESS_BOX: { -#if 0 - // The left margin depends on the widest row in this paragraph. - // This code is wrong because it depends on the rows, but at the - // same time this function is used in redoParagraph to construct - // the rows. - ParagraphMetrics const & pm = par_metrics_[pit]; - int minfill = max_width_; - for (row : pm.rows()) - if (row.fill() < minfill) - minfill = row.fill(); - l_margin += bfm.signedWidth(layout.leftmargin); - l_margin += minfill; -#endif - // also wrong, but much shorter. - l_margin += max_width_ / 2; + case MARGIN_RIGHT_ADDRESS_BOX: + // This is handled globally in redoParagraph(). break; } - } if (!par.params().leftIndent().zero()) l_margin += par.params().leftIndent().inPixels(max_width_, lfm.em()); @@ -1770,8 +1778,8 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const && !text_->inset().neverIndent() // display style insets do not need indentation && !(!par.empty() - && par.isInset(pos) - && par.getInset(pos)->rowFlags() & Inset::Display) + && par.isInset(0) + && par.getInset(0)->rowFlags() & Inset::Display) && (!(tclass.isDefaultLayout(par.layout()) || tclass.isPlainLayout(par.layout())) || buffer.params().paragraph_separation @@ -1818,8 +1826,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const return; size_t const nrows = pm.rows().size(); // Remember left and right margin for drawing math numbers - Changer changeleft = make_change(pi.leftx, x + leftMargin(pit)); - Changer changeright = make_change(pi.rightx, x + width() - rightMargin(pit)); + Changer changeleft = changeVar(pi.leftx, x + leftMargin(pit)); + Changer changeright = changeVar(pi.rightx, x + width() - rightMargin(pit)); // Use fast lane in nodraw stage. if (pi.pain.isNull()) { @@ -1869,6 +1877,9 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const if (text_->isRTL(pit)) swap(pi.leftx, pi.rightx); + BookmarksSection::BookmarkPosList bpl = + theSession().bookmarks().bookmarksInPar(bv_->buffer().fileName(), pm.par().id()); + for (size_t i = 0; i != nrows; ++i) { Row const & row = pm.rows()[i]; @@ -1913,6 +1924,9 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const // Paint only the insets if the text itself is // unchanged. rp.paintOnlyInsets(); + rp.paintTooLargeMarks( + row_x + row.left_x() < bv_->leftMargin(), + row_x + row.right_x() > bv_->workWidth() - bv_->rightMargin()); row.changed(false); y += row.descent(); continue; @@ -1924,16 +1938,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const LYXERR(Debug::PAINTING, "Clear rect@(" << max(row_x, 0) << ", " << y - row.ascent() << ")=" << width() << " x " << row.height()); - // FIXME: this is a hack. We clear an amount equal to - // cursor width. This will not work if the caret has a - // ridiculous width like 6. (see ticket #10797) - // This is the same formula as in GuiWorkArea. - int const caret_width = lyxrc.cursor_width - ? lyxrc.cursor_width - : 1 + int((lyxrc.currentZoom + 50) / 200.0); - pi.pain.fillRectangle(max(row_x, 0), y - row.ascent(), - width() + caret_width, - row.height(), pi.background_color); + pi.pain.fillRectangle(row_x, y - row.ascent(), + width(), row.height(), pi.background_color); } // Instrumentation for testing row cache (see also @@ -1963,8 +1969,15 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const if (i == nrows - 1) rp.paintLast(); rp.paintText(); - rp.paintTooLargeMarks(row_x + row.left_x() < 0, - row_x + row.right_x() > bv_->workWidth()); + rp.paintTooLargeMarks( + row_x + row.left_x() < bv_->leftMargin(), + row_x + row.right_x() > bv_->workWidth() - bv_->rightMargin()); + // indicate bookmarks presence in margin + if (lyxrc.bookmarks_visibility == LyXRC::BMK_MARGIN) + for (auto const & bp_p : bpl) + if (bp_p.second >= row.pos() && bp_p.second < row.endpos()) + rp.paintBookmark(bp_p.first); + y += row.descent(); #if 0