X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FTextMetrics.cpp;h=f38c78d3ea7d98e6799d17a41898eafe8bba1049;hb=a550f7d91e2f945517cf96ad5f68788944539ecb;hp=96c2fb52bd3feabcceb47dc862b4fe6163793754;hpb=8491962c6bc1c73df076fa4807cc6ee295ccce41;p=lyx.git diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index 96c2fb52bd..f38c78d3ea 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -26,7 +26,6 @@ #include "CoordCache.h" #include "Cursor.h" #include "CutAndPaste.h" -#include "HSpace.h" #include "InsetList.h" #include "Language.h" #include "Layout.h" @@ -421,10 +420,21 @@ bool TextMetrics::redoParagraph(pit_type const pit) parPos.pos()++; } + // If there is an end of paragraph marker, its size should be + // substracted to the available width. The logic here is + // almost the same as in breakRow, remember keep them in sync. + int eop = 0; + if (lyxrc.paragraph_markers && ii->pos + 1 == par.size() + && size_type(pit + 1) < text_->paragraphs().size()) { + Font f(text_->layoutFont(pit)); + // ¶ U+00B6 PILCROW SIGN + eop = theFontMetrics(f).width(char_type(0x00B6)); + } + // do the metric calculation Dimension dim; int const w = max_width_ - leftMargin(max_width_, pit, ii->pos) - - right_margin; + - right_margin - eop; Font const & font = ii->inset->inheritFont() ? displayFont(pit, ii->pos) : bufferfont; MacroContext mc(&buffer, parPos); @@ -480,19 +490,21 @@ bool TextMetrics::redoParagraph(pit_type const pit) // specially tailored for the main text. // Top and bottom margin of the document (only at top-level) if (text_->isMainText()) { + // original value was 20px, which is 0.2in at 100dpi + int const margin = Length(0.2, Length::IN).inPixels(0); if (pit == 0) { - pm.rows().front().dimension().asc += 20; - /* coverity[copy_paste_error]: coverity thinks that we - * should update pm.dim().asc below, but all the rows - * heights are actually counted as part of the paragraph metric - * descent see loop above). + pm.rows().front().dimension().asc += margin; + /* coverity thinks that we should update pm.dim().asc + * below, but all the rows heights are actually counted as + * part of the paragraph metric descent see loop above). */ - pm.dim().des += 20; + // coverity[copy_paste_error] + pm.dim().des += margin; } ParagraphList const & pars = text_->paragraphs(); if (pit + 1 == pit_type(pars.size())) { - pm.rows().back().dimension().des += 20; - pm.dim().des += 20; + pm.rows().back().dimension().des += margin; + pm.dim().des += margin; } } @@ -561,10 +573,9 @@ LyXAlignment TextMetrics::getAlign(Paragraph const & par, Row const & row) const // not justify stuff, then don't stretch. // A forced block alignment can only be overridden the 'no // justification on screen' setting. - if (((row.right_boundary() || row.endpos() == par.size()) - && !forced_block) + if ((row.flushed() && !forced_block) || !bv_->buffer().params().justification) - align = text_->isRTL(par) ? LYX_ALIGN_RIGHT : LYX_ALIGN_LEFT; + align = row.isRTL() ? LYX_ALIGN_RIGHT : LYX_ALIGN_LEFT; } return align; @@ -621,21 +632,31 @@ void TextMetrics::computeRowMetrics(Row & row, int width) const switch (getAlign(par, row)) { case LYX_ALIGN_BLOCK: // Expand expanding characters by a total of w - if (!row.setExtraWidth(w) && text_->isRTL(par)) { + if (!row.setExtraWidth(w) && row.isRTL()) { // Justification failed and the text is RTL: align to the right row.left_margin += w; row.dimension().wid += w; } break; + case LYX_ALIGN_LEFT: + // a displayed inset that is flushed + if (Inset const * inset = par.getInset(row.pos())) + row.left_margin += inset->indent(*bv_); + break; case LYX_ALIGN_RIGHT: - row.left_margin += w; - row.dimension().wid += w; + if (Inset const * inset = par.getInset(row.pos())) { + int const new_w = max(w - inset->indent(*bv_), 0); + row.left_margin += new_w; + row.dimension().wid += new_w; + } else { + row.left_margin += w; + row.dimension().wid += w; + } break; case LYX_ALIGN_CENTER: row.dimension().wid += w / 2; row.left_margin += w / 2; break; - case LYX_ALIGN_LEFT: case LYX_ALIGN_NONE: case LYX_ALIGN_LAYOUT: case LYX_ALIGN_SPECIAL: @@ -911,7 +932,7 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const && inset->display()) || (!row.empty() && row.back().inset && row.back().inset->display())) { - row.right_boundary(true); + row.flushed(true); need_new_row = par.isNewline(i); ++i; break; @@ -923,7 +944,8 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const row.finalizeLast(); row.endpos(i); - // End of paragraph marker + // End of paragraph marker. The logic here is almost the + // same as in redoParagraph, remember keep them in sync. ParagraphList const & pars = text_->paragraphs(); if (lyxrc.paragraph_markers && !need_new_row && i == end && size_type(row.pit() + 1) < pars.size()) { @@ -943,8 +965,13 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const // of success, reset indication that the row was broken abruptly. int const next_width = max_width_ - leftMargin(max_width_, row.pit(), row.endpos()) - rightMargin(row.pit()); - if (row.shortenIfNeeded(body_pos, width, next_width)) - row.right_boundary(!row.empty() && row.back().endpos == row.endpos()); + + row.shortenIfNeeded(body_pos, width, next_width); + row.right_boundary(!row.empty() && row.endpos() < end + && row.back().endpos == row.endpos()); + // Last row in paragraph is flushed + if (row.endpos() == end) + row.flushed(true); // make sure that the RTL elements are in reverse ordering row.reverseRTL(is_rtl); @@ -1136,8 +1163,10 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x, * how boundary helps here. */ else if (pos == cit->endpos - && cit + 1 != row.end() - && cit->isRTL() != (cit + 1)->isRTL()) + && ((!cit->isRTL() && cit + 1 != row.end() + && (cit + 1)->isRTL()) + || (cit->isRTL() && cit != row.begin() + && !(cit - 1)->isRTL()))) boundary = true; } @@ -1324,23 +1353,19 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y, } pit_type pit = getPitNearY(y); LASSERT(pit != -1, return 0); - - int yy = y; // is modified by getPitAndRowNearY - Row const & row = getPitAndRowNearY(yy, pit, assert_in_view, up); - + Row const & row = getPitAndRowNearY(y, pit, assert_in_view, up); cur.pit() = pit; // Do we cover an inset? - InsetList::InsetTable * it = checkInsetHit(pit, x, yy); + InsetList::InsetTable * it = checkInsetHit(pit, x, y); if (!it) { // No inset, set position in the text bool bound = false; // is modified by getPosNearX - int xx = x; // is modified by getPosNearX - cur.pos() = getPosNearX(row, xx, bound); + cur.pos() = getPosNearX(row, x, bound); cur.boundary(bound); cur.setCurrentFont(); - cur.setTargetX(xx); + cur.setTargetX(x); return 0; } @@ -1353,23 +1378,17 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y, cur.setTargetX(x); // Try to descend recursively inside the inset. - Inset * edited = inset->editXY(cur, x, yy); - if (edited == inset && cur.pos() == it->pos) { + Inset * edited = inset->editXY(cur, x, y); + // FIXME: it is not clear that the test on position is needed + // Remove it if/when semantics of editXY is clarified + if (cur.text() == text_ && cur.pos() == it->pos) { // non-editable inset, set cursor after the inset if x is // nearer to that position (bug 9628) - CoordCache::Insets const & insetCache = bv_->coordCache().getInsets(); - Dimension const & dim = insetCache.dim(inset); - Point p = insetCache.xy(inset); - bool const is_rtl = text_->isRTL(text_->getPar(pit)); - if (is_rtl) { - // "in front of" == "right of" - if (abs(p.x_ - x) < abs(p.x_ + dim.wid - x)) - cur.posForward(); - } else { - // "in front of" == "left of" - if (abs(p.x_ + dim.wid - x) < abs(p.x_ - x)) - cur.posForward(); - } + bool bound = false; // is modified by getPosNearX + cur.pos() = getPosNearX(row, x, bound); + cur.boundary(bound); + cur.setCurrentFont(); + cur.setTargetX(x); } if (cur.top().text() == text_) @@ -1423,28 +1442,12 @@ InsetList::InsetTable * TextMetrics::checkInsetHit(pit_type pit, int x, int y) LYXERR(Debug::DEBUG, "x: " << x << " y: " << y << " pit: " << pit); - InsetList::const_iterator iit = par.insetList().begin(); - InsetList::const_iterator iend = par.insetList().end(); - for (; iit != iend; ++iit) { - Inset * inset = iit->inset; + for (auto const & it : par.insetList()) { + LYXERR(Debug::DEBUG, "examining inset " << it.inset); - LYXERR(Debug::DEBUG, "examining inset " << inset); - - if (!insetCache.has(inset)) { - LYXERR(Debug::DEBUG, "inset has no cached position"); - return 0; - } - - Dimension const & dim = insetCache.dim(inset); - Point p = insetCache.xy(inset); - - LYXERR(Debug::DEBUG, "xo: " << p.x_ << "..." << p.x_ + dim.wid - << " yo: " << p.y_ - dim.asc << "..." << p.y_ + dim.des); - - if (x >= p.x_ && x <= p.x_ + dim.wid - && y >= p.y_ - dim.asc && y <= p.y_ + dim.des) { - LYXERR(Debug::DEBUG, "Hit inset: " << inset); - return const_cast(&(*iit)); + if (insetCache.covers(it.inset, x, y)) { + LYXERR(Debug::DEBUG, "Hit inset: " << it.inset); + return const_cast(&it); } } @@ -1467,56 +1470,6 @@ Inset * TextMetrics::checkInsetHit(int x, int y) } -Row::const_iterator const -TextMetrics::findRowElement(Row const & row, pos_type const pos, - bool const boundary, double & x) const -{ - /** - * When boundary is true, position i is in the row element (pos, endpos) - * if - * pos < i <= endpos - * whereas, when boundary is false, the test is - * pos <= i < endpos - * The correction below allows to handle both cases. - */ - int const boundary_corr = (boundary && pos) ? -1 : 0; - - x = row.left_margin; - - /** Early return in trivial cases - * 1) the row is empty - * 2) the position is the left-most position of the row; there - * is a quirk here however: if the first element is virtual - * (end-of-par marker for example), then we have to look - * closer - */ - if (row.empty() - || (pos == row.begin()->left_pos() && !boundary - && !row.begin()->isVirtual())) - return row.begin(); - - Row::const_iterator cit = row.begin(); - for ( ; cit != row.end() ; ++cit) { - /** Look whether the cursor is inside the element's - * span. Note that it is necessary to take the - * boundary into account, and to accept virtual - * elements, which have pos == endpos. - */ - if (pos + boundary_corr >= cit->pos - && (pos + boundary_corr < cit->endpos || cit->isVirtual())) { - x += cit->pos2x(pos); - break; - } - x += cit->full_width(); - } - - if (cit == row.end()) - --cit; - - return cit; -} - - int TextMetrics::cursorX(CursorSlice const & sl, bool boundary) const { @@ -1529,7 +1482,7 @@ int TextMetrics::cursorX(CursorSlice const & sl, pos_type const pos = sl.pos(); double x = 0; - findRowElement(row, pos, boundary, x); + row.findElement(pos, boundary, x); return int(x); } @@ -1789,32 +1742,32 @@ int TextMetrics::leftMargin(int max_width, // set the correct parindent if (pos == 0 && (layout.labeltype == LABEL_NO_LABEL - || layout.labeltype == LABEL_ABOVE - || layout.labeltype == LABEL_CENTERED - || (layout.labeltype == LABEL_STATIC - && layout.latextype == LATEX_ENVIRONMENT - && !text_->isFirstInSequence(pit))) + || layout.labeltype == LABEL_ABOVE + || layout.labeltype == LABEL_CENTERED + || (layout.labeltype == LABEL_STATIC + && layout.latextype == LATEX_ENVIRONMENT + && !text_->isFirstInSequence(pit))) && (align == LYX_ALIGN_BLOCK || align == LYX_ALIGN_LEFT) && !par.params().noindent() // in some insets, paragraphs are never indented && !text_->inset().neverIndent() // display style insets are always centered, omit indentation && !(!par.empty() - && par.isInset(pos) - && par.getInset(pos)->display()) + && par.isInset(pos) + && par.getInset(pos)->display()) && (!(tclass.isDefaultLayout(par.layout()) - || tclass.isPlainLayout(par.layout())) + || tclass.isPlainLayout(par.layout())) || buffer.params().paragraph_separation == BufferParams::ParagraphIndentSeparation)) { - // use the parindent of the layout when the - // default indentation is used otherwise use - // the indentation set in the document - // settings - if (buffer.params().getIndentation().asLyXCommand() == "default") - l_margin += bfm.signedWidth(parindent); - else - l_margin += buffer.params().getIndentation().inPixels(*bv_); - } + /* use the parindent of the layout when the default + * indentation is used otherwise use the indentation set in + * the document settings + */ + if (buffer.params().getParIndent().empty()) + l_margin += bfm.signedWidth(parindent); + else + l_margin += buffer.params().getParIndent().inPixels(max_width_, bfm.em()); + } return l_margin; } @@ -1984,15 +1937,14 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const rp.paintAppendix(); rp.paintDepthBar(); rp.paintChangeBar(); - bool const is_rtl = text_->isRTL(text_->getPar(pit)); - if (i == 0 && !is_rtl) + if (i == 0 && !row.isRTL()) rp.paintFirst(); - if (i == nrows - 1 && is_rtl) + if (i == nrows - 1 && row.isRTL()) rp.paintLast(); rp.paintText(); - if (i == nrows - 1 && !is_rtl) + if (i == nrows - 1 && !row.isRTL()) rp.paintLast(); - if (i == 0 && is_rtl) + if (i == 0 && row.isRTL()) rp.paintFirst(); rp.paintTooLargeMarks(row_x + row.left_x() < 0, row_x + row.right_x() > bv_->workWidth());