#include "support/lassert.h"
#include "support/lstrings.h"
#include "support/lyxlib.h"
+#include "support/textutils.h"
#include <algorithm>
#include <ostream>
int Row::Element::countExpanders() const
{
- if (type != STRING)
+ if (type != STRING || font.fontInfo().family() == TYPEWRITER_FAMILY)
return 0;
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();
}
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();
}
}
-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.
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;
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;
// 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;
}
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;
}
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
<< " 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_) {
}
+// FIXME: remove this and move the changebar update to Row::push_back()
void Row::finalizeLast()
{
if (elements_.empty())
}
-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;
}
--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);
}
// 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;
* 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.
* 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;
}