From e1c4cb7146e0dfd25bb705178618da251bdfde8c Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Mon, 19 May 2014 11:35:15 +0200 Subject: [PATCH] Fix breaking of loooong word in RTL languages Moreover, breaks row at insets when there is no suitable separator. Also make the code of Row::shorten_if_needed somewhat simpler by using iterators and factoring the code. Fixes: #9120 --- src/Row.cpp | 112 ++++++++++++++++++++++++++------------------ src/Row.h | 14 ++++-- src/TextMetrics.cpp | 6 +-- 3 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/Row.cpp b/src/Row.cpp index a0c0dc4ef8..1336d05a20 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -28,6 +28,8 @@ #include #include +#include + using namespace std; namespace lyx { @@ -98,6 +100,27 @@ pos_type Row::Element::x2pos(double &x) const } +bool Row::Element::breakAt(double w) +{ + if (type != STRING || width() <= w) + return false; + + bool const rtl = font.isVisibleRightToLeft(); + if (rtl) + w = width() - w; + pos_type new_pos = x2pos(w); + if (new_pos == pos) + return false; + str = str.substr(0, new_pos - pos); + if (rtl) + dim.wid -= w; + else + dim.wid = w; + endpos = new_pos; + return true; +} + + pos_type Row::Element::left_pos() const { return font.isVisibleRightToLeft() ? endpos : pos; @@ -110,7 +133,6 @@ pos_type Row::Element::right_pos() const } - Row::Row() : separator(0), label_hfill(0), x(0), right_margin(0), sel_beg(-1), sel_end(-1), @@ -334,63 +356,63 @@ void Row::pop_back() } -void Row::shorten_if_needed(pos_type const keep, int const w) +void Row::shortenIfNeeded(pos_type const keep, int const w) { - if (empty() || width() < w) + if (empty() || width() <= w) return; /** First, we try to remove elements one by one from the end - * until a separator is found. + * until a separator is found. cit points to the first element + * we want to remove from the row. */ - int i = elements_.size(); + Elements::iterator const beg = elements_.begin(); + Elements::iterator const end = elements_.end(); + Elements::iterator cit = end; + Elements::iterator first_below = end; int new_end = end_; int new_wid = dim_.wid; - if (i > 0 && elements_[i - 1].type == SEPARATOR && new_end > keep) { - --i; - new_end = elements_[i].pos; - new_wid -= elements_[i].dim.wid; + // if the row ends with a separator, skip it. + if (cit != beg && boost::prior(cit)->type == SEPARATOR && new_end > keep) { + --cit; + new_end = cit->pos; + new_wid -= cit->dim.wid; } - while (i > 0 && elements_[i - 1].type != SEPARATOR && new_end > keep) { - --i; - new_end = elements_[i].pos; - new_wid -= elements_[i].dim.wid; + // Search for a separator where the row can be broken. + while (cit != beg && boost::prior(cit)->type != SEPARATOR && new_end > keep) { + --cit; + new_end = cit->pos; + new_wid -= cit->dim.wid; + if (new_wid < w && first_below == end) + first_below = cit; } - if (i == 0) { - /* If we are here, it means that we have not found a - * separator to shorten the row. There is one case - * where we can do something: when we have one big - * string, maybe with a paragraph marker after it. - */ - Element & front = elements_.front(); - if (!(front.type == STRING - && (elements_.size() == 1 - || (elements_.size() == 2 - && back().type == VIRTUAL)))) - return; - // If this is a string element, we can try to split it. - if (front.type != STRING) - return; - double xstr = w - x; - // If there is a paragraph marker, it should be taken in account - if (elements_.size() == 2) - xstr -= back().width(); - //FIXME: use FontMetrics::x2pos here?? handle rtl? - pos_type new_pos = front.x2pos(xstr); - front.str = front.str.substr(0, new_pos - pos_); - front.dim.wid = xstr; - front.endpos = new_pos; - end_ = new_pos; - dim_.wid = x + xstr; - // If there is a paragraph marker, it should be removed. - if (elements_.size() == 2) - elements_.pop_back(); + if (cit != beg) { + // We have found a suitable separator. This is the + // common case. + end_ = new_end; + dim_.wid = new_wid; + elements_.erase(cit, end); return; } - end_ = new_end; - dim_.wid = new_wid; - elements_.erase(elements_.begin() + i, elements_.end()); + + /* If we are here, it means that we have not found a separator + * to shorten the row. There is one case where we can do + * something: when we have one big string, maybe with some + * other things after it. + */ + double max_w = w - x; + if (first_below->breakAt(max_w)) { + end_ = first_below->endpos; + dim_.wid = x + first_below->width(); + // If there are other elements, they should be removed. + elements_.erase(boost::next(first_below), end); + } else if (first_below->pos > pos_) { + end_ = first_below->pos; + dim_.wid = new_wid; + // Remove all elements from first_below. + elements_.erase(first_below, end); + } } diff --git a/src/Row.h b/src/Row.h index 2aa09a697e..a1e707e744 100644 --- a/src/Row.h +++ b/src/Row.h @@ -63,17 +63,21 @@ public: : type(t), pos(p), endpos(p + 1), inset(0), extra(0), font(f), change(ch), final(false) {} - // returns total width of element, including separator overhead + // Return total width of element, including separator overhead double width() const { return dim.wid + extra; }; - // returns position in pixels (from the left) of position - // \param i in the row element. + /** Return position in pixels (from the left) of position + * \param i in the row element. + */ double pos2x(pos_type const i) const; - /** Return character position that is the closest to * pixel position \param x. The value \param x is * adjusted to the actual pixel position. */ pos_type x2pos(double &x) const; + /** Break the element if possible, so that its width is + * less then \param w. Returns true on success. + */ + bool breakAt(double w); // Returns the position on left side of the element. pos_type left_pos() const; @@ -207,7 +211,7 @@ public: * \param body_pos minimum amount of text to keep. * \param width maximum width of the row */ - void shorten_if_needed(pos_type const body_pos, int const width); + void shortenIfNeeded(pos_type const body_pos, int const width); /** * If last element of the row is a string, compute its width diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index 12a42e2d99..bda3b2f655 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -889,6 +889,8 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit ++i; ++fi; } + row.finalizeLast(); + row.endpos(i); // End of paragraph marker if (lyxrc.paragraph_markers @@ -904,10 +906,8 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit row.addVirtual(end, docstring(1, char_type(0x00B6)), f, Change()); } - row.finalizeLast(); - row.endpos(i); // if the row is too large, try to cut at last separator. - row.shorten_if_needed(body_pos, width); + row.shortenIfNeeded(body_pos, width); // if the row ends with a separator that is not at end of // paragraph, remove it