X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FRow.cpp;h=16ae9aecb6e0f988535493cd8f5841f2ef2e039a;hb=5d96ee69eb9173779932575cb6d6ce70db9860b9;hp=16ce90d473175e44902d90642cc8c6f55b3c4dc3;hpb=8491962c6bc1c73df076fa4807cc6ee295ccce41;p=lyx.git diff --git a/src/Row.cpp b/src/Row.cpp index 16ce90d473..16ae9aecb6 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -19,14 +19,16 @@ #include "Row.h" #include "DocIterator.h" +#include "Language.h" #include "frontends/FontMetrics.h" #include "support/debug.h" #include "support/lassert.h" #include "support/lstrings.h" -#include "support/lyxalgo.h" +#include "support/lyxlib.h" +#include #include using namespace std; @@ -115,14 +117,13 @@ pos_type Row::Element::x2pos(int &x) const case SPACE: // those elements contain only one position. Round to // the closest side. - if (x > full_width()) { + if (x > (full_width() + 1) / 2) { x = int(full_width()); i = !isRTL(); } else { x = 0; i = isRTL(); } - } //lyxerr << "=> p=" << pos + i << " x=" << x << endl; return pos + i; @@ -163,43 +164,37 @@ Row::Row() : separator(0), label_hfill(0), left_margin(0), right_margin(0), sel_beg(-1), sel_end(-1), begin_margin_sel(false), end_margin_sel(false), - changed_(false), crc_(0), - pit_(0), pos_(0), end_(0), right_boundary_(false) + changed_(true), + pit_(0), pos_(0), end_(0), + right_boundary_(false), flushed_(false), rtl_(false), + changebar_(false) {} -void Row::setCrc(size_type crc) const -{ - changed_ = crc != crc_; - crc_ = crc; -} - - -bool Row::isMarginSelected(bool left_margin, DocIterator const & beg, +bool Row::isMarginSelected(bool left, DocIterator const & beg, DocIterator const & end) const { - pos_type const sel_pos = left_margin ? sel_beg : sel_end; - pos_type const margin_pos = left_margin ? pos_ : end_; - - // Is the chosen margin selected ? - if (sel_pos == margin_pos) { - if (beg.pos() == end.pos()) - // This is a special case in which the space between after - // pos i-1 and before pos i is selected, i.e. the margins - // (see DocIterator::boundary_). - return beg.boundary() && !end.boundary(); - else if (end.pos() == margin_pos) - // If the selection ends around the margin, it is only - // drawn if the cursor is after the margin. - return !end.boundary(); - else if (beg.pos() == margin_pos) - // If the selection begins around the margin, it is - // only drawn if the cursor is before the margin. - return beg.boundary(); - else - return true; - } - return false; + pos_type const sel_pos = left ? sel_beg : sel_end; + pos_type const margin_pos = left ? pos_ : end_; + + // Is there a selection and is the chosen margin selected ? + if (!selection() || sel_pos != margin_pos) + return false; + else if (beg.pos() == end.pos()) + // This is a special case in which the space between after + // pos i-1 and before pos i is selected, i.e. the margins + // (see DocIterator::boundary_). + return beg.boundary() && !end.boundary(); + else if (end.pos() == margin_pos) + // If the selection ends around the margin, it is only + // drawn if the cursor is after the margin. + return !end.boundary(); + else if (beg.pos() == margin_pos) + // If the selection begins around the margin, it is + // only drawn if the cursor is before the margin. + return beg.boundary(); + else + return true; } @@ -208,28 +203,35 @@ void Row::setSelectionAndMargins(DocIterator const & beg, { setSelection(beg.pos(), end.pos()); - if (selection()) { - end_margin_sel = isMarginSelected(false, beg, end); - begin_margin_sel = isMarginSelected(true, beg, end); - } + change(end_margin_sel, isMarginSelected(false, beg, end)); + change(begin_margin_sel, isMarginSelected(true, beg, end)); +} + + +void Row::clearSelectionAndMargins() const +{ + change(sel_beg, -1); + change(sel_end, -1); + change(end_margin_sel, false); + change(begin_margin_sel, false); } void Row::setSelection(pos_type beg, pos_type end) const { if (pos_ >= beg && pos_ <= end) - sel_beg = pos_; + change(sel_beg, pos_); else if (beg > pos_ && beg <= end_) - sel_beg = beg; + change(sel_beg, beg); else - sel_beg = -1; + change(sel_beg, -1); if (end_ >= beg && end_ <= end) - sel_end = end_; + change(sel_end,end_); else if (end < end_ && end >= pos_) - sel_end = end; + change(sel_end, end); else - sel_end = -1; + change(sel_end, -1); } @@ -275,7 +277,7 @@ ostream & operator<<(ostream & os, Row const & row) << " ascent: " << row.dim_.asc << " descent: " << row.dim_.des << " separator: " << row.separator - << " label_hfill: " << row.label_hfill + << " label_hfill: " << row.label_hfill << " row_boundary: " << row.right_boundary() << "\n"; double x = row.left_margin; Row::Elements::const_iterator it = row.elements_.begin(); @@ -296,7 +298,7 @@ int Row::left_x() const x += cit->full_width(); ++cit; } - return int(x + 0.5); + return support::iround(x); } @@ -312,7 +314,7 @@ int Row::right_x() const else break; } - return int(x + 0.5); + return support::iround(x); } @@ -334,7 +336,7 @@ bool Row::setExtraWidth(int w) // amount of expansion: number of expanders time the em value for each // string element int exp_amount = 0; - for (Row::Element const & e : elements_) + for (Element const & e : elements_) exp_amount += e.expansionAmount(); if (!exp_amount) return false; @@ -344,8 +346,8 @@ bool Row::setExtraWidth(int w) // do not stretch more than MAX_SPACE_STRETCH em per expander return false; // add extra length to each element proportionally to its em. - for (Row::Element & e : elements_) - if (e.type == Row::STRING) + for (Element & e : elements_) + if (e.type == STRING) e.setExtra(extra_per_em); // update row dimension dim_.wid += w; @@ -371,6 +373,8 @@ void Row::finalizeLast() if (elt.final) return; elt.final = true; + if (elt.change.changed()) + changebar_ = true; if (elt.type == STRING) { dim_.wid -= elt.dim.wid; @@ -389,6 +393,7 @@ void Row::add(pos_type const pos, Inset const * ins, Dimension const & dim, e.dim = dim; elements_.push_back(e); dim_.wid += dim.wid; + changebar_ |= ins->isChanged(); } @@ -476,8 +481,32 @@ bool Row::shortenIfNeeded(pos_type const keep, 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 it the row is already short enough after + * this inset, then cut right after this element. + */ + if (wid_brk <= w && brk.type == INSET + && brk.inset->rowFlags() & Inset::CanBreakAfter) { + end_ = brk.endpos; + dim_.wid = wid_brk; + elements_.erase(cit_brk + 1, end); + return true; + } + // assume now that the current element is not there wid_brk -= brk.dim.wid; - if (brk.countSeparators() == 0 || brk.pos < keep) + /* + * 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(); + // When there is text before the body part (think description + // environment), do not try to break. + if (brk.pos < keep) continue; /* 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 @@ -485,7 +514,7 @@ bool Row::shortenIfNeeded(pos_type const keep, int const w, int const next_width * - shorter than the natural width of the element, in order to enforce * break-up. */ - if (brk.breakAt(min(w - wid_brk, brk.dim.wid - 2), false)) { + if (brk.breakAt(min(w - wid_brk, brk.dim.wid - 2), !word_wrap)) { /* 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 @@ -565,6 +594,58 @@ void Row::reverseRTL(bool const rtl_par) // If the paragraph itself is RTL, reverse everything if (rtl_par) reverse(elements_.begin(), elements_.end()); + rtl_ = rtl_par; +} + +Row::const_iterator const +Row::findElement(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 = 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 (empty() + || (pos == begin()->left_pos() && !boundary + && !begin()->isVirtual())) + return begin(); + + const_iterator cit = begin(); + for ( ; cit != 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, 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) { + x += cit->pos2x(pos); + break; + } + x += cit->full_width(); + } + + if (cit == end()) + --cit; + + return cit; } + } // namespace lyx