X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FRow.cpp;h=1c17fb299283b7a66df9e7034c096d5b2136c026;hb=d4d4654d6325e92aff96523b64e1d403655a71cc;hp=8233a965a752c039d838be77460caf757d8020b6;hpb=4b69f5efa7825c3aa5464686f4ecaea212f73422;p=features.git diff --git a/src/Row.cpp b/src/Row.cpp index 8233a965a7..1c17fb2992 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -42,19 +42,11 @@ 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) return 0; - return theFontMetrics(font).countExpanders(str); + return support::countExpanders(str); } @@ -114,6 +106,7 @@ pos_type Row::Element::x2pos(int &x) const break; case INSET: case SPACE: + case MARGINSPACE: // those elements contain only one position. Round to // the closest side. if (x > (full_width() + 1) / 2) { @@ -123,41 +116,88 @@ pos_type Row::Element::x2pos(int &x) const x = 0; i = isRTL(); } - break; - case INVALID: - LYXERR0("x2pos: INVALID row element !"); } //lyxerr << "=> p=" << pos + i << " x=" << x << endl; return pos + i; } -Row::Element Row::Element::splitAt(int w, bool force) +bool Row::Element::splitAt(int const width, int next_width, bool force, + Row::Elements & tail) { - if (type != STRING || !(row_flags & CanBreakInside)) - return Element(); + // Not a string or already OK. + if (type != STRING || (dim.wid > 0 && dim.wid < width)) + return false; FontMetrics const & fm = theFontMetrics(font); - dim.wid = w; - int const i = fm.breakAt(str, dim.wid, isRTL(), force); - if (i != -1) { - //Create a second row element to return - Element ret(STRING, pos + i, font, change); - ret.str = str.substr(i); - ret.endpos = ret.pos + ret.str.length(); - // Copy the after flags of the original element to the second one. - ret.row_flags = row_flags & (CanBreakInside | AfterFlags); - - // Now update ourselves - str.erase(i); - endpos = pos + i; - // Row should be broken after the original element - row_flags = (row_flags & ~AfterFlags) | BreakAfter; - //LYXERR0("breakAt(" << w << ") Row element Broken at " << w << "(w(str)=" << fm.width(str) << "): e=" << *this); - return ret; + + // A a string that is not breakable + if (!(row_flags & CanBreakInside)) { + // has width been computed yet? + if (dim.wid == 0) + dim.wid = fm.width(str); + return false; } - return Element(); + bool const wrap_any = !font.language()->wordWrap(); + FontMetrics::Breaks breaks = fm.breakString(str, width, next_width, + isRTL(), wrap_any | force); + + // if breaking did not really work, give up + if (!force && breaks.front().nspc_wid > width) { + if (dim.wid == 0) + dim.wid = fm.width(str); + return false; + } + + Element first_e(STRING, pos, font, change); + // should next element eventually replace *this? + 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; + e.dim.wid = brk.wid; + e.nspc_wid = brk.nspc_wid; + e.row_flags = CanBreakInside | BreakAfter; + if (first) { + // this element eventually goes to *this + e.row_flags |= row_flags & ~AfterFlags; + first_e = e; + first = false; + } else + tail.push_back(e); + i += brk.len; + } + + if (!tail.empty()) { + // Avoid having a last empty element. This happens when + // breaking at the trailing space of string + if (tail.back().str.empty()) + tail.pop_back(); + else { + // Copy the after flags of the original element to the last one. + tail.back().row_flags &= ~BreakAfter; + tail.back().row_flags |= row_flags & AfterFlags; + } + // first_e row should be broken after the original element + first_e.row_flags |= BreakAfter; + } else { + // Restore the after flags of the original element. + first_e.row_flags &= ~BreakAfter; + first_e.row_flags |= row_flags & AfterFlags; + } + + // update ourselves + swap(first_e, *this); + return true; } @@ -172,6 +212,7 @@ void Row::Element::rtrim() */ str = support::rtrim(str); endpos = pos + str.length(); + dim.wid = nspc_wid; } @@ -266,15 +307,25 @@ ostream & operator<<(ostream & os, Row::Element const & e) case Row::SPACE: os << "SPACE: "; break; - case Row::INVALID: - os << "INVALID: "; - break; + case Row::MARGINSPACE: + os << "MARGINSPACE: "; } os << "width=" << e.full_width() << ", row_flags=" << e.row_flags; return os; } +ostream & operator<<(ostream & os, Row::Elements const & elts) +{ + double x = 0; + for (Row::Element const & e : elts) { + os << "x=" << x << " => " << e << endl; + x += e.full_width(); + } + return os; +} + + ostream & operator<<(ostream & os, Row const & row) { os << " pos: " << row.pos_ << " end: " << row.end_ @@ -285,12 +336,13 @@ ostream & operator<<(ostream & os, Row const & row) << " descent: " << row.dim_.des << " separator: " << row.separator << " label_hfill: " << row.label_hfill - << " row_boundary: " << row.right_boundary() << "\n"; + << " end_boundary: " << row.end_boundary() + << " flushed: " << row.flushed() << "\n"; + // We cannot use the operator above, unfortunately double x = row.left_margin; - Row::Elements::const_iterator it = row.elements_.begin(); - for ( ; it != row.elements_.end() ; ++it) { - os << "x=" << x << " => " << *it << endl; - x += it->full_width(); + for (Row::Element const & e : row.elements_) { + os << "x=" << x << " => " << e << endl; + x += e.full_width(); } return os; } @@ -325,16 +377,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) @@ -382,11 +424,6 @@ void Row::finalizeLast() elt.final = true; if (elt.change.changed()) changebar_ = true; - - if (elt.type == STRING && elt.dim.wid == 0) { - elt.dim.wid = theFontMetrics(elt.font).width(elt.str); - dim_.wid += elt.dim.wid; - } } @@ -405,12 +442,12 @@ void Row::add(pos_type const pos, Inset const * ins, Dimension const & dim, void Row::add(pos_type const pos, char_type const c, - Font const & f, Change const & ch, bool can_break) + Font const & f, Change const & ch) { if (!sameString(f, ch)) { finalizeLast(); Element e(STRING, pos, f, ch); - e.row_flags = can_break ? CanBreakInside : Inline; + e.row_flags = CanBreakInside; elements_.push_back(e); } back().str += c; @@ -447,6 +484,18 @@ void Row::addSpace(pos_type const pos, int const width, } +void Row::addMarginSpace(pos_type const pos, int const width, + Font const & f, Change const & ch) +{ + finalizeLast(); + Element e(MARGINSPACE, pos, f, ch); + e.dim.wid = width; + e.row_flags = NoBreakBefore; + elements_.push_back(e); + dim_.wid += e.dim.wid; +} + + void Row::push_back(Row::Element const & e) { dim_.wid += e.dim.wid; @@ -463,19 +512,14 @@ void Row::pop_back() namespace { -// Remove stuff after \c it from \c elts, and return it. -// if \c init is provided, it will prepended to the rest -Row::Elements splitFrom(Row::Elements & elts, Row::Elements::iterator const & it, - Row::Element const & init = Row::Element()) +// Move stuff after \c it from \c from and the end of \c to. +void moveElements(Row::Elements & from, Row::Elements::iterator const & it, + Row::Elements & to) { - Row::Elements ret; - if (init.isValid()) - ret.push_back(init); - ret.insert(ret.end(), it, elts.end()); - elts.erase(it, elts.end()); - if (!elts.empty()) - elts.back().row_flags = (elts.back().row_flags & ~AfterFlags) | BreakAfter; - return ret; + to.insert(to.end(), it, from.end()); + from.erase(it, from.end()); + if (!from.empty()) + from.back().row_flags = (from.back().row_flags & ~AfterFlags) | AlwaysBreakAfter; } } @@ -511,39 +555,40 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) Elements::iterator cit_brk = cit; int wid_brk = wid + cit_brk->dim.wid; ++cit_brk; + Elements tail; while (cit_brk != beg) { --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. + * this element, then cut right after it. */ if (wid_brk <= w && brk.row_flags & CanBreakAfter) { end_ = brk.endpos; dim_.wid = wid_brk; - return splitFrom(elements_, cit_brk + 1); + moveElements(elements_, cit_brk + 1, tail); + return tail; } // assume now that the current element is not there wid_brk -= brk.dim.wid; - /* - * Some Asian languages split lines anywhere (no notion of - * word). It seems that QTextLayout is not aware of this fact. - * See for reference: - * https://en.wikipedia.org/wiki/Line_breaking_rules_in_East_Asian_languages - * - * FIXME: Something shall be done about characters which are - * not allowed at the beginning or end of line. - */ - bool const word_wrap = brk.font.language()->wordWrap(); + /* If the current element is an inset that allows breaking row + * before itself, and if the row is already short enough before + * this element, then cut right before it. + */ + if (wid_brk <= w && 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 word boundary) at a length that is both + * 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. */ - Element remainder = brk.splitAt(min(w - wid_brk, brk.dim.wid - 2), !word_wrap); - if (brk.row_flags & BreakAfter) { + if (brk.splitAt(min(w - wid_brk, brk.dim.wid - 2), next_width, false, 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 @@ -551,18 +596,17 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) */ if (wid_brk + cit_brk->dim.wid < w && dim_.wid - (wid_brk + brk.dim.wid) >= next_width) { + tail.clear(); break; } end_ = brk.endpos; *cit_brk = brk; dim_.wid = wid_brk + brk.dim.wid; // If there are other elements, they should be removed. - // remainder can be empty when splitting at trailing space - if (remainder.str.empty()) - return splitFrom(elements_, next(cit_brk, 1)); - else - return splitFrom(elements_, next(cit_brk, 1), remainder); + moveElements(elements_, cit_brk + 1, tail); + return tail; } + LATTEST(tail.empty()); } if (cit != beg && cit->row_flags & NoBreakBefore) { @@ -577,25 +621,29 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) // been added. We can cut right here. end_ = cit->pos; dim_.wid = wid; - return splitFrom(elements_, cit); + moveElements(elements_, cit, tail); + return tail; } /* If we are here, it means that we have not found a separator to - * shorten the row. Let's try to break it again, but not at word - * boundary this time. + * shorten the row. Let's try to break it again, but force + * splitting this time. */ - Element remainder = cit->splitAt(w - wid, true); - if (cit->row_flags & BreakAfter) { + if (cit->splitAt(w - wid, next_width, true, tail)) { end_ = cit->endpos; dim_.wid = wid + cit->dim.wid; // If there are other elements, they should be removed. - return splitFrom(elements_, next(cit, 1), remainder); + moveElements(elements_, cit + 1, tail); + return tail; } - return Elements(); + + // cit == beg; remove all elements after the first one. + moveElements(elements_, cit + 1, tail); + return tail; } -void Row::reverseRTL(bool const rtl_par) +void Row::reverseRTL() { pos_type i = 0; pos_type const end = elements_.size(); @@ -607,14 +655,13 @@ void Row::reverseRTL(bool const rtl_par) ++j; // if the direction is not the same as the paragraph // direction, the sequence has to be reverted. - if (rtl != rtl_par) + if (rtl != rtl_) reverse(elements_.begin() + i, elements_.begin() + j); i = j; } // If the paragraph itself is RTL, reverse everything - if (rtl_par) + if (rtl_) reverse(elements_.begin(), elements_.end()); - rtl_ = rtl_par; } Row::const_iterator const