X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FRow.cpp;h=b85ef3d18b05bfb853efe9ddd911c33ac9dcec89;hb=26ba2a65838731ce639a09539f617cb0f0be3b22;hp=d1921a6d3fed15b5af7e0e65d5a608fbaf6892c0;hpb=da3c3796ee96473fbcede4c33545dc16fe53580d;p=lyx.git diff --git a/src/Row.cpp b/src/Row.cpp index d1921a6d3f..b85ef3d18b 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -27,6 +27,7 @@ #include "support/lassert.h" #include "support/lstrings.h" #include "support/lyxlib.h" +#include "support/textutils.h" #include #include @@ -42,25 +43,17 @@ using frontend::FontMetrics; static double const MAX_SPACE_STRETCH = 1.5; //em -int Row::Element::countSeparators() const -{ - if (type != STRING) - return 0; - return count(str.begin(), str.end(), ' '); -} - - int Row::Element::countExpanders() const { - if (type != STRING) + if (type != STRING || font.fontInfo().family() == TYPEWRITER_FAMILY) return 0; - return theFontMetrics(font).countExpanders(str); + return support::countExpanders(str); } int Row::Element::expansionAmount() const { - if (type != STRING) + if (type != STRING || font.fontInfo().family() == TYPEWRITER_FAMILY) return 0; return countExpanders() * theFontMetrics(font).em(); } @@ -68,7 +61,7 @@ int Row::Element::expansionAmount() const void Row::Element::setExtra(double extra_per_em) { - if (type != STRING) + if (type != STRING || font.fontInfo().family() == TYPEWRITER_FAMILY) return; extra = extra_per_em * theFontMetrics(font).em(); } @@ -130,7 +123,7 @@ pos_type Row::Element::x2pos(int &x) const } -bool Row::Element::splitAt(int const width, int next_width, bool force, +bool Row::Element::splitAt(int const width, int next_width, SplitType split_type, Row::Elements & tail) { // Not a string or already OK. @@ -149,10 +142,14 @@ bool Row::Element::splitAt(int const width, int next_width, bool force, bool const wrap_any = !font.language()->wordWrap(); FontMetrics::Breaks breaks = fm.breakString(str, width, next_width, - isRTL(), wrap_any | force); + isRTL(), wrap_any || split_type == FORCE); - // if breaking did not really work, give up - if (!force && breaks.front().wid > width) { + /** if breaking did not really work, give up + * case 1: split type is FIT and the first element is longer than the limit; + * case 2: the first break occurs at the front of the string + */ + if ((split_type == FIT && breaks.front().nspc_wid > width) + || (breaks.size() > 1 && breaks.front().len == 0)) { if (dim.wid == 0) dim.wid = fm.width(str); return false; @@ -163,12 +160,6 @@ bool Row::Element::splitAt(int const width, int next_width, bool force, bool first = true; docstring::size_type i = 0; for (FontMetrics::Break const & brk : breaks) { - /* For some reason breakString can decide to break before the - * first character (normally we use a 0-width nbsp to prevent - * that). Skip leading empty elements, they are never wanted. - */ - if (first && brk.len == 0 && breaks.size() > 1) - continue; Element e(STRING, pos + i, font, change); e.str = str.substr(i, brk.len); e.endpos = e.pos + brk.len; @@ -198,8 +189,20 @@ bool Row::Element::splitAt(int const width, int next_width, bool force, // first_e row should be broken after the original element first_e.row_flags |= BreakAfter; } else { - // Restore the after flags of the original element. +#if 1 + // remove the BreakAfter that got added above. first_e.row_flags &= ~BreakAfter; +#else + // FIXME : the code below looks like a good idea, but I do not + // have a use case yet. The question is what happens + // when breaking at the end of a string with a + // trailing space. + // if it turns out that no breaking was necessary, remove the + // BreakAfter that got added above. + if (first_e.dim.wid <= width) + first_e.row_flags &= ~BreakAfter; +#endif + // Restore the after flags of the original element. first_e.row_flags |= row_flags & AfterFlags; } @@ -211,14 +214,13 @@ bool Row::Element::splitAt(int const width, int next_width, bool force, void Row::Element::rtrim() { - if (type != STRING) + if (type != STRING || str.empty() || !isSpace(str.back())) return; /* This is intended for strings that have been created by splitAt. - * They may have trailing spaces, but they are not counted in the - * string length (QTextLayout feature, actually). We remove them, - * and decrease endpos, since spaces at row break are invisible. + * If There is a trailing space, we remove it and decrease endpos, + * since spaces at row break are invisible. */ - str = support::rtrim(str); + str.pop_back(); endpos = pos + str.length(); dim.wid = nspc_wid; } @@ -336,7 +338,7 @@ ostream & operator<<(ostream & os, Row::Elements const & elts) ostream & operator<<(ostream & os, Row const & row) { - os << " pos: " << row.pos_ << " end: " << row.end_ + os << " pit: " << row.pit_ << " pos: " << row.pos_ << " end: " << row.end_ << " left_margin: " << row.left_margin << " width: " << row.dim_.wid << " right_margin: " << row.right_margin @@ -344,8 +346,9 @@ ostream & operator<<(ostream & os, Row const & row) << " descent: " << row.dim_.des << " separator: " << row.separator << " label_hfill: " << row.label_hfill - << " right_boundary: " << row.right_boundary() - << " flushed: " << row.flushed() << "\n"; + << " end_boundary: " << row.end_boundary() + << " flushed: " << row.flushed_ + << " rtl=" << row.rtl_ << "\n"; // We cannot use the operator above, unfortunately double x = row.left_margin; for (Row::Element const & e : row.elements_) { @@ -385,16 +388,6 @@ int Row::right_x() const } -int Row::countSeparators() const -{ - int n = 0; - const_iterator const end = elements_.end(); - for (const_iterator cit = elements_.begin() ; cit != end ; ++cit) - n += cit->countSeparators(); - return n; -} - - bool Row::setExtraWidth(int w) { if (w < 0) @@ -432,6 +425,7 @@ bool Row::sameString(Font const & f, Change const & ch) const } +// FIXME: remove this and move the changebar update to Row::push_back() void Row::finalizeLast() { if (elements_.empty()) @@ -537,28 +531,30 @@ void moveElements(Row::Elements & from, Row::Elements::iterator const & it, to.insert(to.end(), it, from.end()); from.erase(it, from.end()); if (!from.empty()) - from.back().row_flags = (from.back().row_flags & ~AfterFlags) | BreakAfter; + from.back().row_flags = (from.back().row_flags & ~AfterFlags) | AlwaysBreakAfter; } } -Row::Elements Row::shortenIfNeeded(int const w, int const next_width) +Row::Elements Row::shortenIfNeeded(int const max_width, int const next_width) { // FIXME: performance: if the last element is a string, we would // like to avoid computing its length. finalizeLast(); - if (empty() || width() <= w) + if (empty() || width() <= max_width) return Elements(); Elements::iterator const beg = elements_.begin(); Elements::iterator const end = elements_.end(); int wid = left_margin; + // the smallest row width we know we can achieve by breaking a string. + int min_row_wid = dim_.wid; // Search for the first element that goes beyond right margin Elements::iterator cit = beg; for ( ; cit != end ; ++cit) { - if (wid + cit->dim.wid > w) + if (wid + cit->dim.wid > max_width) break; wid += cit->dim.wid; } @@ -578,11 +574,11 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) --cit_brk; // make a copy of the element to work on it. Element brk = *cit_brk; - /* If the current element is an inset that allows breaking row - * after itself, and if the row is already short enough after - * this inset, then cut right after this element. + /* If the current element allows breaking row after itself, + * and if the row is already short enough after this element, + * then cut right after it. */ - if (wid_brk <= w && brk.row_flags & CanBreakAfter) { + if (wid_brk <= max_width && brk.row_flags & CanBreakAfter) { end_ = brk.endpos; dim_.wid = wid_brk; moveElements(elements_, cit_brk + 1, tail); @@ -590,23 +586,44 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) } // assume now that the current element is not there wid_brk -= brk.dim.wid; + /* If the current element allows breaking row before itself, + * and if the row is already short enough before this element, + * then cut right before it. + */ + if (wid_brk <= max_width && brk.row_flags & CanBreakBefore && cit_brk != beg) { + end_ = (cit_brk -1)->endpos; + dim_.wid = wid_brk; + moveElements(elements_, cit_brk, tail); + return tail; + } /* We have found a suitable separable element. This is the common case. * Try to break it cleanly at a length that is both * - less than the available space on the row * - shorter than the natural width of the element, in order to enforce * break-up. */ - if (brk.splitAt(min(w - wid_brk, brk.dim.wid - 2), next_width, false, tail)) { + int const split_width = min(max_width - wid_brk, brk.dim.wid - 2); + if (brk.splitAt(split_width, next_width, BEST_EFFORT, tail)) { /* if this element originally did not cause a row overflow * in itself, and the remainder of the row would still be * too large after breaking, then we will have issues in - * next row. Thus breaking does not help. + * next row. Thus breaking here does not help. */ - if (wid_brk + cit_brk->dim.wid < w - && dim_.wid - (wid_brk + brk.dim.wid) >= next_width) { + if (wid_brk + cit_brk->dim.wid < max_width + && min_row_wid - (wid_brk + brk.dim.wid) >= next_width) { tail.clear(); break; } + /* if we did not manage to fit a part of the element into + * the split_width limit, at least remember that we can + * shorten the row if needed. + */ + if (brk.dim.wid > split_width) { + min_row_wid = wid_brk + brk.dim.wid; + tail.clear(); + continue; + } + // We have found a proper place where to break this string element. end_ = brk.endpos; *cit_brk = brk; dim_.wid = wid_brk + brk.dim.wid; @@ -637,7 +654,7 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) * shorten the row. Let's try to break it again, but force * splitting this time. */ - if (cit->splitAt(w - wid, next_width, true, tail)) { + if (cit->splitAt(max_width - wid, next_width, FORCE, tail)) { end_ = cit->endpos; dim_.wid = wid + cit->dim.wid; // If there are other elements, they should be removed. @@ -645,7 +662,9 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) return tail; } - return Elements(); + // cit == beg; remove all elements after the first one. + moveElements(elements_, cit + 1, tail); + return tail; } @@ -704,10 +723,9 @@ Row::findElement(pos_type const pos, bool const boundary, double & x) const * to accept virtual elements, in which case the position * will be before the virtual element. */ - if (cit->isVirtual() && pos + boundary_corr == cit->pos) - break; - else if (pos + boundary_corr >= cit->pos - && pos + boundary_corr < cit->endpos) { + if ((pos + boundary_corr >= cit->pos && pos + boundary_corr < cit->endpos) + || (cit->isVirtual() && pos + boundary_corr == cit->pos)) { + // FIXME: shall we use `pos + boundary_corr' here? x += cit->pos2x(pos); break; }