mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-26 11:16:55 +00:00
Merge branch 'breakrows'
With this merge, the way paragraphs are typeset changes. Paragraphs are first tolenized as row elements, and these elements are then broken into separate rows as needed to fit the margins. This allows to reduce the amount of metrics computation and make LyX much faster in the case of large insets. Moreover, the code relies more on RofFlags enum, which desribes how an inset or a row element should be typset. This aspect will be extended in the future. Some user for whom performance is very bad (which I cannot reproduce unfortunately) have reported a 4-fold speedup. In general cases, the speedup will be less impressive but still noticeable. Related to bugs #12297 and #5861.
This commit is contained in:
commit
bf9b4a0836
@ -11,7 +11,7 @@ Please keep this file up to date as the code evolves!!!
|
||||
Abbreviations:
|
||||
bv: BufferView
|
||||
pm: ParagraphMetrics
|
||||
tm::TextMetrics
|
||||
tm: TextMetrics
|
||||
|
||||
* Questions / Ideas
|
||||
|
||||
@ -76,11 +76,6 @@ a lot the amount of stuff to redraw.
|
||||
It should not be necessary to access the Paragraph object to draw.
|
||||
Adding the static elements to Row is a lot of work, but worth it IMO.
|
||||
|
||||
** Create a unique row by paragraph and break it afterwards
|
||||
|
||||
This should be a performance gain (only if paragraph breaking still
|
||||
shows as expensive after the rest is done)
|
||||
|
||||
** do not add the vertical margin of main text to first/last row
|
||||
|
||||
Would make code cleaner. Probably no so difficult.
|
||||
|
@ -273,6 +273,7 @@ HEADERFILESCORE = \
|
||||
ParIterator.h \
|
||||
PDFOptions.h \
|
||||
Row.h \
|
||||
RowFlags.h \
|
||||
RowPainter.h \
|
||||
Server.h \
|
||||
ServerSocket.h \
|
||||
|
@ -20,17 +20,8 @@
|
||||
#include "Dimension.h"
|
||||
#include "Row.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace lyx {
|
||||
|
||||
/**
|
||||
* Each paragraph is broken up into a number of rows on the screen.
|
||||
* This is a list of such on-screen rows, ordered from the top row
|
||||
* downwards.
|
||||
*/
|
||||
typedef std::vector<Row> RowList;
|
||||
|
||||
class BufferView;
|
||||
class Paragraph;
|
||||
|
||||
|
260
src/Row.cpp
260
src/Row.cpp
@ -35,7 +35,6 @@ using namespace std;
|
||||
|
||||
namespace lyx {
|
||||
|
||||
using support::rtrim;
|
||||
using frontend::FontMetrics;
|
||||
|
||||
|
||||
@ -130,47 +129,92 @@ pos_type Row::Element::x2pos(int &x) const
|
||||
}
|
||||
|
||||
|
||||
bool Row::Element::breakAt(int w, bool force)
|
||||
bool Row::Element::splitAt(int const width, int next_width, bool force,
|
||||
Row::Elements & tail)
|
||||
{
|
||||
if (type != STRING || dim.wid <= w)
|
||||
// Not a string or already OK.
|
||||
if (type != STRING || (dim.wid > 0 && dim.wid < width))
|
||||
return false;
|
||||
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
int x = w;
|
||||
if(fm.breakAt(str, x, isRTL(), force)) {
|
||||
dim.wid = x;
|
||||
endpos = pos + str.length();
|
||||
//lyxerr << "breakAt(" << w << ") Row element Broken at " << x << "(w(str)=" << fm.width(str) << "): e=" << *this << endl;
|
||||
return true;
|
||||
|
||||
// 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 false;
|
||||
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().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) {
|
||||
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.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;
|
||||
}
|
||||
|
||||
|
||||
pos_type Row::Element::left_pos() const
|
||||
void Row::Element::rtrim()
|
||||
{
|
||||
return isRTL() ? endpos : pos;
|
||||
if (type != STRING)
|
||||
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.
|
||||
*/
|
||||
str = support::rtrim(str);
|
||||
endpos = pos + str.length();
|
||||
}
|
||||
|
||||
|
||||
pos_type Row::Element::right_pos() const
|
||||
{
|
||||
return isRTL() ? pos : endpos;
|
||||
}
|
||||
|
||||
|
||||
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_(true),
|
||||
pit_(0), pos_(0), end_(0),
|
||||
right_boundary_(false), flushed_(false), rtl_(false),
|
||||
changebar_(false)
|
||||
{}
|
||||
|
||||
|
||||
bool Row::isMarginSelected(bool left, DocIterator const & beg,
|
||||
DocIterator const & end) const
|
||||
{
|
||||
@ -261,9 +305,19 @@ ostream & operator<<(ostream & os, Row::Element const & e)
|
||||
break;
|
||||
case Row::SPACE:
|
||||
os << "SPACE: ";
|
||||
break;
|
||||
}
|
||||
os << "width=" << e.full_width();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -279,11 +333,11 @@ ostream & operator<<(ostream & os, Row const & row)
|
||||
<< " separator: " << row.separator
|
||||
<< " label_hfill: " << row.label_hfill
|
||||
<< " row_boundary: " << row.right_boundary() << "\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;
|
||||
}
|
||||
@ -375,22 +429,17 @@ void Row::finalizeLast()
|
||||
elt.final = true;
|
||||
if (elt.change.changed())
|
||||
changebar_ = true;
|
||||
|
||||
if (elt.type == STRING) {
|
||||
dim_.wid -= elt.dim.wid;
|
||||
elt.dim.wid = theFontMetrics(elt.font).width(elt.str);
|
||||
dim_.wid += elt.dim.wid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Row::add(pos_type const pos, Inset const * ins, Dimension const & dim,
|
||||
Font const & f, Change const & ch)
|
||||
Font const & f, Change const & ch)
|
||||
{
|
||||
finalizeLast();
|
||||
Element e(INSET, pos, f, ch);
|
||||
e.inset = ins;
|
||||
e.dim = dim;
|
||||
e.row_flags = ins->rowFlags();
|
||||
elements_.push_back(e);
|
||||
dim_.wid += dim.wid;
|
||||
changebar_ |= ins->isChanged();
|
||||
@ -398,23 +447,16 @@ 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)
|
||||
Font const & f, Change const & ch, bool can_break)
|
||||
{
|
||||
if (!sameString(f, ch)) {
|
||||
finalizeLast();
|
||||
Element e(STRING, pos, f, ch);
|
||||
e.row_flags = can_break ? CanBreakInside : Inline;
|
||||
elements_.push_back(e);
|
||||
}
|
||||
if (back().str.length() % 30 == 0) {
|
||||
dim_.wid -= back().dim.wid;
|
||||
back().str += c;
|
||||
back().endpos = pos + 1;
|
||||
back().dim.wid = theFontMetrics(back().font).width(back().str);
|
||||
dim_.wid += back().dim.wid;
|
||||
} else {
|
||||
back().str += c;
|
||||
back().endpos = pos + 1;
|
||||
}
|
||||
back().str += c;
|
||||
back().endpos = pos + 1;
|
||||
}
|
||||
|
||||
|
||||
@ -427,6 +469,10 @@ void Row::addVirtual(pos_type const pos, docstring const & s,
|
||||
e.dim.wid = theFontMetrics(f).width(s);
|
||||
dim_.wid += e.dim.wid;
|
||||
e.endpos = pos;
|
||||
// Copy after* flags from previous elements, forbid break before element
|
||||
int const prev_row_flags = elements_.empty() ? Inline : elements_.back().row_flags;
|
||||
int const can_inherit = AfterFlags & ~AlwaysBreakAfter;
|
||||
e.row_flags = (prev_row_flags & can_inherit) | NoBreakBefore;
|
||||
elements_.push_back(e);
|
||||
finalizeLast();
|
||||
}
|
||||
@ -443,6 +489,13 @@ void Row::addSpace(pos_type const pos, int const width,
|
||||
}
|
||||
|
||||
|
||||
void Row::push_back(Row::Element const & e)
|
||||
{
|
||||
dim_.wid += e.dim.wid;
|
||||
elements_.push_back(e);
|
||||
}
|
||||
|
||||
|
||||
void Row::pop_back()
|
||||
{
|
||||
dim_.wid -= elements_.back().dim.wid;
|
||||
@ -450,10 +503,28 @@ void Row::pop_back()
|
||||
}
|
||||
|
||||
|
||||
bool Row::shortenIfNeeded(pos_type const keep, int const w, int const next_width)
|
||||
namespace {
|
||||
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Row::Elements Row::shortenIfNeeded(int const w, 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)
|
||||
return false;
|
||||
return Elements();
|
||||
|
||||
Elements::iterator const beg = elements_.begin();
|
||||
Elements::iterator const end = elements_.end();
|
||||
@ -469,52 +540,38 @@ bool Row::shortenIfNeeded(pos_type const keep, int const w, int const next_width
|
||||
|
||||
if (cit == end) {
|
||||
// This should not happen since the row is too long.
|
||||
LYXERR0("Something is wrong cannot shorten row: " << *this);
|
||||
return false;
|
||||
LYXERR0("Something is wrong, cannot shorten row: " << *this);
|
||||
return Elements();
|
||||
}
|
||||
|
||||
// Iterate backwards over breakable elements and try to break them
|
||||
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 it the row is already short enough after
|
||||
* after itself, and if 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) {
|
||||
if (wid_brk <= w && brk.row_flags & CanBreakAfter) {
|
||||
end_ = brk.endpos;
|
||||
dim_.wid = wid_brk;
|
||||
elements_.erase(cit_brk + 1, end);
|
||||
return true;
|
||||
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();
|
||||
// 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
|
||||
* 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.breakAt(min(w - wid_brk, brk.dim.wid - 2), !word_wrap)) {
|
||||
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
|
||||
@ -525,25 +582,18 @@ bool Row::shortenIfNeeded(pos_type const keep, int const w, int const next_width
|
||||
break;
|
||||
}
|
||||
end_ = brk.endpos;
|
||||
/* after breakAt, there may be spaces at the end of the
|
||||
* string, but they are not counted in the string length
|
||||
* (QTextLayout feature, actually). We remove them, but do
|
||||
* not change the end of the row, since spaces at row
|
||||
* break are invisible.
|
||||
*/
|
||||
brk.str = rtrim(brk.str);
|
||||
brk.endpos = brk.pos + brk.str.length();
|
||||
*cit_brk = brk;
|
||||
dim_.wid = wid_brk + brk.dim.wid;
|
||||
// If there are other elements, they should be removed.
|
||||
elements_.erase(cit_brk + 1, end);
|
||||
return true;
|
||||
moveElements(elements_, cit_brk + 1, tail);
|
||||
return tail;
|
||||
}
|
||||
LATTEST(tail.empty());
|
||||
}
|
||||
|
||||
if (cit != beg && cit->type == VIRTUAL) {
|
||||
// It is not possible to separate a virtual element from the
|
||||
// previous one.
|
||||
if (cit != beg && cit->row_flags & NoBreakBefore) {
|
||||
// It is not possible to separate this element from the
|
||||
// previous one. (e.g. VIRTUAL)
|
||||
--cit;
|
||||
wid -= cit->dim.wid;
|
||||
}
|
||||
@ -553,29 +603,28 @@ bool Row::shortenIfNeeded(pos_type const keep, int const w, int const next_width
|
||||
// been added. We can cut right here.
|
||||
end_ = cit->pos;
|
||||
dim_.wid = wid;
|
||||
elements_.erase(cit, end);
|
||||
return true;
|
||||
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.
|
||||
*/
|
||||
if (cit->breakAt(w - wid, true)) {
|
||||
if (cit->splitAt(w - wid, next_width, true, tail)) {
|
||||
LYXERR0(*cit);
|
||||
end_ = cit->endpos;
|
||||
// See comment above.
|
||||
cit->str = rtrim(cit->str);
|
||||
cit->endpos = cit->pos + cit->str.length();
|
||||
dim_.wid = wid + cit->dim.wid;
|
||||
// If there are other elements, they should be removed.
|
||||
elements_.erase(next(cit, 1), end);
|
||||
return true;
|
||||
moveElements(elements_, cit + 1, tail);
|
||||
return tail;
|
||||
}
|
||||
return false;
|
||||
|
||||
return Elements();
|
||||
}
|
||||
|
||||
|
||||
void Row::reverseRTL(bool const rtl_par)
|
||||
void Row::reverseRTL()
|
||||
{
|
||||
pos_type i = 0;
|
||||
pos_type const end = elements_.size();
|
||||
@ -587,14 +636,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
|
||||
|
109
src/Row.h
109
src/Row.h
@ -18,6 +18,7 @@
|
||||
#include "Changes.h"
|
||||
#include "Dimension.h"
|
||||
#include "Font.h"
|
||||
#include "RowFlags.h"
|
||||
|
||||
#include "support/docstring.h"
|
||||
#include "support/types.h"
|
||||
@ -57,9 +58,10 @@ public:
|
||||
* by other methods that need to parse the Row contents.
|
||||
*/
|
||||
struct Element {
|
||||
//
|
||||
Element(Type const t, pos_type p, Font const & f, Change const & ch)
|
||||
: type(t), pos(p), endpos(p + 1), inset(0),
|
||||
extra(0), font(f), change(ch), final(false) {}
|
||||
: type(t), pos(p), endpos(p + 1), font(f), change(ch) {}
|
||||
|
||||
|
||||
// Return the number of separator in the element (only STRING type)
|
||||
int countSeparators() const;
|
||||
@ -86,24 +88,30 @@ public:
|
||||
* adjusted to the actual pixel position.
|
||||
*/
|
||||
pos_type x2pos(int &x) const;
|
||||
/** Break the element if possible, so that its width is less
|
||||
* than \param w. Returns true on success. When \param force
|
||||
* is true, the string is cut at any place, other wise it
|
||||
* respects the row breaking rules of characters.
|
||||
/** Break the element in two if possible, so that its width is less
|
||||
* than \param w.
|
||||
* \return a vector of elements containing the remainder of
|
||||
* the text (empty if nothing happened).
|
||||
* \param width maximum width of the row.
|
||||
* \param next_width available width on next row.
|
||||
* \param force: if true, cut string at any place, even for
|
||||
* languages that wrap at word delimiters; if false, do not
|
||||
* break at all if first element would larger than \c width.
|
||||
*/
|
||||
bool breakAt(int w, bool force);
|
||||
|
||||
// Returns the position on left side of the element.
|
||||
pos_type left_pos() const;
|
||||
// Returns the position on right side of the element.
|
||||
pos_type right_pos() const;
|
||||
// FIXME: ideally last parameter should be Elements&, but it is not possible.
|
||||
bool splitAt(int width, int next_width, bool force, std::vector<Element> & tail);
|
||||
// remove trailing spaces (useful for end of row)
|
||||
void rtrim();
|
||||
|
||||
//
|
||||
bool isRTL() const { return font.isVisibleRightToLeft(); }
|
||||
// This is true for virtual elements.
|
||||
// Note that we do not use the type here. The two definitions
|
||||
// should be equivalent
|
||||
bool isVirtual() const { return pos == endpos; }
|
||||
bool isVirtual() const { return type == VIRTUAL; }
|
||||
|
||||
// Returns the position on left side of the element.
|
||||
pos_type left_pos() const { return isRTL() ? endpos : pos; };
|
||||
// Returns the position on right side of the element.
|
||||
pos_type right_pos() const { return isRTL() ? pos : endpos; };
|
||||
|
||||
// The kind of row element
|
||||
Type type;
|
||||
@ -116,10 +124,10 @@ public:
|
||||
Dimension dim;
|
||||
|
||||
// Non-zero only if element is an inset
|
||||
Inset const * inset;
|
||||
Inset const * inset = nullptr;
|
||||
|
||||
// Only non-null for justified rows
|
||||
double extra;
|
||||
double extra = 0;
|
||||
|
||||
// Non-empty if element is a string or is virtual
|
||||
docstring str;
|
||||
@ -128,14 +136,19 @@ public:
|
||||
//
|
||||
Change change;
|
||||
// is it possible to add contents to this element?
|
||||
bool final;
|
||||
bool final = false;
|
||||
// properties with respect to row breaking (made of RowFlag enums)
|
||||
int row_flags = Inline;
|
||||
|
||||
friend std::ostream & operator<<(std::ostream & os, Element const & row);
|
||||
};
|
||||
|
||||
///
|
||||
typedef Element value_type;
|
||||
|
||||
///
|
||||
Row();
|
||||
Row() {}
|
||||
|
||||
/**
|
||||
* Helper function: set variable \c var to value \c val, and mark
|
||||
* row as changed is the values were different. This is intended
|
||||
@ -232,7 +245,7 @@ public:
|
||||
Font const & f, Change const & ch);
|
||||
///
|
||||
void add(pos_type pos, char_type const c,
|
||||
Font const & f, Change const & ch);
|
||||
Font const & f, Change const & ch, bool can_break);
|
||||
///
|
||||
void addVirtual(pos_type pos, docstring const & s,
|
||||
Font const & f, Change const & ch);
|
||||
@ -264,18 +277,19 @@ public:
|
||||
Element & back() { return elements_.back(); }
|
||||
///
|
||||
Element const & back() const { return elements_.back(); }
|
||||
/// remove last element
|
||||
/// add element at the end and update width
|
||||
void push_back(Element const &);
|
||||
/// remove last element and update width
|
||||
void pop_back();
|
||||
/**
|
||||
* if row width is too large, remove all elements after last
|
||||
* separator and update endpos if necessary. If all that
|
||||
* remains is a large word, cut it to \param width.
|
||||
* \param body_pos minimum amount of text to keep.
|
||||
* \param width maximum width of the row.
|
||||
* \param available width on next row.
|
||||
* \return true if the row has been shortened.
|
||||
* \param next_width available width on next row.
|
||||
* \return list of elements remaining after breaking.
|
||||
*/
|
||||
bool shortenIfNeeded(pos_type const body_pos, int const width, int const next_width);
|
||||
Elements shortenIfNeeded(int const width, int const next_width);
|
||||
|
||||
/**
|
||||
* If last element of the row is a string, compute its width
|
||||
@ -287,10 +301,12 @@ public:
|
||||
* Find sequences of right-to-left elements and reverse them.
|
||||
* This should be called once the row is completely built.
|
||||
*/
|
||||
void reverseRTL(bool rtl_par);
|
||||
void reverseRTL();
|
||||
///
|
||||
bool isRTL() const { return rtl_; }
|
||||
///
|
||||
void setRTL(bool rtl) { rtl_ = rtl; }
|
||||
///
|
||||
bool needsChangeBar() const { return changebar_; }
|
||||
///
|
||||
void needsChangeBar(bool ncb) { changebar_ = ncb; }
|
||||
@ -301,21 +317,21 @@ public:
|
||||
friend std::ostream & operator<<(std::ostream & os, Row const & row);
|
||||
|
||||
/// additional width for separators in justified rows (i.e. space)
|
||||
double separator;
|
||||
double separator = 0;
|
||||
/// width of hfills in the label
|
||||
double label_hfill;
|
||||
double label_hfill = 0;
|
||||
/// the left margin position of the row
|
||||
int left_margin;
|
||||
int left_margin = 0;
|
||||
/// the right margin of the row
|
||||
int right_margin;
|
||||
int right_margin = 0;
|
||||
///
|
||||
mutable pos_type sel_beg;
|
||||
mutable pos_type sel_beg = -1;
|
||||
///
|
||||
mutable pos_type sel_end;
|
||||
mutable pos_type sel_end = -1;
|
||||
///
|
||||
mutable bool begin_margin_sel;
|
||||
mutable bool begin_margin_sel = false;
|
||||
///
|
||||
mutable bool end_margin_sel;
|
||||
mutable bool end_margin_sel = false;
|
||||
|
||||
private:
|
||||
/// Decides whether the margin is selected.
|
||||
@ -340,27 +356,36 @@ private:
|
||||
Elements elements_;
|
||||
|
||||
/// has the Row appearance changed since last drawing?
|
||||
mutable bool changed_;
|
||||
mutable bool changed_ = true;
|
||||
/// Index of the paragraph that contains this row
|
||||
pit_type pit_;
|
||||
pit_type pit_ = 0;
|
||||
/// first pos covered by this row
|
||||
pos_type pos_;
|
||||
pos_type pos_ = 0;
|
||||
/// one behind last pos covered by this row
|
||||
pos_type end_;
|
||||
pos_type end_ = 0;
|
||||
// Is there a boundary at the end of the row (display inset...)
|
||||
bool right_boundary_;
|
||||
bool right_boundary_ = false;
|
||||
// Shall the row be flushed when it is supposed to be justified?
|
||||
bool flushed_;
|
||||
bool flushed_ = false;
|
||||
/// Row dimension.
|
||||
Dimension dim_;
|
||||
/// Row contents dimension. Does not contain the space above/below row.
|
||||
Dimension contents_dim_;
|
||||
/// true when this row lives in a right-to-left paragraph
|
||||
bool rtl_;
|
||||
bool rtl_ = false;
|
||||
/// true when a changebar should be drawn in the margin
|
||||
bool changebar_;
|
||||
bool changebar_ = false;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, Row::Elements const & elts);
|
||||
|
||||
|
||||
/**
|
||||
* Each paragraph is broken up into a number of rows on the screen.
|
||||
* This is a list of such on-screen rows, ordered from the top row
|
||||
* downwards.
|
||||
*/
|
||||
typedef std::vector<Row> RowList;
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
|
61
src/RowFlags.h
Normal file
61
src/RowFlags.h
Normal file
@ -0,0 +1,61 @@
|
||||
// -*- C++ -*-
|
||||
/**
|
||||
* \file RowFlags.h
|
||||
* This file is part of LyX, the document processor.
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author Jean-Marc Lasgouttes
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
|
||||
#ifndef ROWFLAGS_H
|
||||
#define ROWFLAGS_H
|
||||
|
||||
// Do not include anything here
|
||||
|
||||
namespace lyx {
|
||||
|
||||
/* The list of possible flags, that can be combined.
|
||||
* Some flags that should logically be here (e.g.,
|
||||
* CanBreakBefore), do not exist. This is because the need has not
|
||||
* been identitfied yet.
|
||||
*
|
||||
* Priorities when before/after disagree:
|
||||
* AlwaysBreak* > NoBreak* > Break* or CanBreak*.
|
||||
*/
|
||||
enum RowFlags {
|
||||
// Do not break before or after this element, except if really
|
||||
// needed (between NoBreak* and CanBreak*).
|
||||
Inline = 0,
|
||||
// break row before this element if the row is not empty
|
||||
BreakBefore = 1 << 0,
|
||||
// Avoid breaking row before this element
|
||||
NoBreakBefore = 1 << 1,
|
||||
// flush the row before this element (useful with BreakBefore)
|
||||
FlushBefore = 1 << 2,
|
||||
// force new (maybe empty) row after this element
|
||||
AlwaysBreakAfter = 1 << 3,
|
||||
// break row after this element if there are more elements
|
||||
BreakAfter = 1 << 4,
|
||||
// break row whenever needed after this element
|
||||
CanBreakAfter = 1 << 5,
|
||||
// Avoid breaking row after this element
|
||||
NoBreakAfter = 1 << 6,
|
||||
// The contents of the row may be broken in two (e.g. string)
|
||||
CanBreakInside = 1 << 7,
|
||||
// Flush the row that ends with this element
|
||||
Flush = 1 << 8,
|
||||
// specify an alignment (left, right) for a display element
|
||||
// (default is center)
|
||||
AlignLeft = 1 << 9,
|
||||
AlignRight = 1 << 10,
|
||||
// A display element breaks row at both ends
|
||||
Display = FlushBefore | BreakBefore | BreakAfter,
|
||||
// Flags that concern breaking after element
|
||||
AfterFlags = AlwaysBreakAfter | BreakAfter | CanBreakAfter | NoBreakAfter
|
||||
};
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
#endif
|
@ -491,7 +491,7 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows)
|
||||
|
||||
// If there is an end of paragraph marker, its size should be
|
||||
// substracted to the available width. The logic here is
|
||||
// almost the same as in breakRow, remember keep them in sync.
|
||||
// almost the same as in tokenizeParagraph, remember keep them in sync.
|
||||
int eop = 0;
|
||||
if (e.pos + 1 == par.size()
|
||||
&& (lyxrc.paragraph_markers || par.lookupChange(par.size()).changed())
|
||||
@ -516,43 +516,28 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows)
|
||||
}
|
||||
}
|
||||
|
||||
pos_type first = 0;
|
||||
size_t row_index = 0;
|
||||
bool need_new_row = false;
|
||||
// maximum pixel width of a row
|
||||
do {
|
||||
if (row_index == pm.rows().size())
|
||||
pm.rows().push_back(Row());
|
||||
else
|
||||
pm.rows()[row_index] = Row();
|
||||
Row & row = pm.rows()[row_index];
|
||||
row.pit(pit);
|
||||
row.pos(first);
|
||||
need_new_row = breakRow(row, right_margin);
|
||||
// Transform the paragraph into a single row containing all the elements.
|
||||
Row const bigrow = tokenizeParagraph(pit);
|
||||
// Split the row in several rows fitting in available width
|
||||
pm.rows() = breakParagraph(bigrow);
|
||||
|
||||
/* If there is more than one row, expand the text to the full
|
||||
* allowable width. This setting here is needed for the
|
||||
* setRowAlignment() below. We do nothing when tight insets are
|
||||
* requested.
|
||||
*/
|
||||
if (pm.rows().size() > 1 && !tight_ && dim_.wid < max_width_)
|
||||
dim_.wid = max_width_;
|
||||
|
||||
// Compute height and alignment of the rows.
|
||||
for (Row & row : pm.rows()) {
|
||||
setRowHeight(row);
|
||||
row.changed(true);
|
||||
if ((row_index || row.endpos() < par.size() || row.right_boundary())
|
||||
&& !tight_) {
|
||||
/* If there is more than one row or the row has been
|
||||
* broken by a display inset or a newline, expand the text
|
||||
* to the full allowable width. This setting here is
|
||||
* needed for the setRowAlignment() below.
|
||||
* We do nothing when tight insets are requested.
|
||||
*/
|
||||
if (dim_.wid < max_width_)
|
||||
dim_.wid = max_width_;
|
||||
}
|
||||
if (align_rows)
|
||||
setRowAlignment(row, max(dim_.wid, row.width()));
|
||||
first = row.endpos();
|
||||
++row_index;
|
||||
|
||||
pm.dim().wid = max(pm.dim().wid, row.width() + row.right_margin);
|
||||
pm.dim().des += row.height();
|
||||
} while (first < par.size() || need_new_row);
|
||||
|
||||
if (row_index < pm.rows().size())
|
||||
pm.rows().resize(row_index);
|
||||
}
|
||||
|
||||
// This type of margin can only be handled at the global paragraph level
|
||||
if (par.layout().margintype == MARGIN_RIGHT_ADDRESS_BOX) {
|
||||
@ -634,10 +619,10 @@ LyXAlignment TextMetrics::getAlign(Paragraph const & par, Row const & row) const
|
||||
|
||||
// Display-style insets should always be on a centered row
|
||||
if (Inset const * inset = par.getInset(row.pos())) {
|
||||
if (inset->rowFlags() & Inset::Display) {
|
||||
if (inset->rowFlags() & Inset::AlignLeft)
|
||||
if (inset->rowFlags() & Display) {
|
||||
if (inset->rowFlags() & AlignLeft)
|
||||
align = LYX_ALIGN_BLOCK;
|
||||
else if (inset->rowFlags() & Inset::AlignRight)
|
||||
else if (inset->rowFlags() & AlignRight)
|
||||
align = LYX_ALIGN_RIGHT;
|
||||
else
|
||||
align = LYX_ALIGN_CENTER;
|
||||
@ -868,44 +853,29 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
/** This is the function where the hard work is done. The code here is
|
||||
* very sensitive to small changes :) Note that part of the
|
||||
* intelligence is also in Row::shortenIfNeeded.
|
||||
*/
|
||||
bool TextMetrics::breakRow(Row & row, int const right_margin) const
|
||||
|
||||
Row TextMetrics::tokenizeParagraph(pit_type const pit) const
|
||||
{
|
||||
LATTEST(row.empty());
|
||||
Paragraph const & par = text_->getPar(row.pit());
|
||||
Row row;
|
||||
row.pit(pit);
|
||||
Paragraph const & par = text_->getPar(pit);
|
||||
Buffer const & buf = text_->inset().buffer();
|
||||
BookmarksSection::BookmarkPosList bpl =
|
||||
theSession().bookmarks().bookmarksInPar(buf.fileName(), par.id());
|
||||
|
||||
pos_type const end = par.size();
|
||||
pos_type const pos = row.pos();
|
||||
pos_type const body_pos = par.beginOfBody();
|
||||
bool const is_rtl = text_->isRTL(row.pit());
|
||||
bool need_new_row = false;
|
||||
|
||||
row.left_margin = leftMargin(row.pit(), pos);
|
||||
row.right_margin = right_margin;
|
||||
if (is_rtl)
|
||||
swap(row.left_margin, row.right_margin);
|
||||
// Remember that the row width takes into account the left_margin
|
||||
// but not the right_margin.
|
||||
row.dim().wid = row.left_margin;
|
||||
// the width available for the row.
|
||||
int const width = max_width_ - row.right_margin;
|
||||
|
||||
// check for possible inline completion
|
||||
DocIterator const & ic_it = bv_->inlineCompletionPos();
|
||||
pos_type ic_pos = -1;
|
||||
if (ic_it.inTexted() && ic_it.text() == text_ && ic_it.pit() == row.pit())
|
||||
if (ic_it.inTexted() && ic_it.text() == text_ && ic_it.pit() == pit)
|
||||
ic_pos = ic_it.pos();
|
||||
|
||||
// Now we iterate through until we reach the right margin
|
||||
// or the end of the par, then build a representation of the row.
|
||||
pos_type i = pos;
|
||||
FontIterator fi = FontIterator(*this, par, row.pit(), pos);
|
||||
pos_type i = 0;
|
||||
FontIterator fi = FontIterator(*this, par, pit, 0);
|
||||
// The real stopping condition is a few lines below.
|
||||
while (true) {
|
||||
// Firstly, check whether there is a bookmark here.
|
||||
@ -921,7 +891,7 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
|
||||
|
||||
// The stopping condition is here so that the display of a
|
||||
// bookmark can take place at paragraph start too.
|
||||
if (i >= end || (i != pos && row.width() > width))
|
||||
if (i >= end)
|
||||
break;
|
||||
|
||||
char_type c = par.getChar(i);
|
||||
@ -939,11 +909,11 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
|
||||
// this is needed to make sure that the row width is correct
|
||||
row.finalizeLast();
|
||||
int const add = max(fm.width(par.layout().labelsep),
|
||||
labelEnd(row.pit()) - row.width());
|
||||
labelEnd(pit) - row.width());
|
||||
row.addSpace(i, add, *fi, par.lookupChange(i));
|
||||
} else if (c == '\t')
|
||||
row.addSpace(i, theFontMetrics(*fi).width(from_ascii(" ")),
|
||||
*fi, par.lookupChange(i));
|
||||
*fi, par.lookupChange(i));
|
||||
else if (c == 0x2028 || c == 0x2029) {
|
||||
/**
|
||||
* U+2028 LINE SEPARATOR
|
||||
@ -959,9 +929,10 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
|
||||
// ⤶ U+2936 ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS
|
||||
// ¶ U+00B6 PILCROW SIGN
|
||||
char_type const screen_char = (c == 0x2028) ? 0x2936 : 0x00B6;
|
||||
row.add(i, screen_char, *fi, par.lookupChange(i));
|
||||
row.add(i, screen_char, *fi, par.lookupChange(i), i >= body_pos);
|
||||
} else
|
||||
row.add(i, c, *fi, par.lookupChange(i));
|
||||
// row elements before body are unbreakable
|
||||
row.add(i, c, *fi, par.lookupChange(i), i >= body_pos);
|
||||
|
||||
// add inline completion width
|
||||
// draw logically behind the previous character
|
||||
@ -978,74 +949,221 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
|
||||
row.addVirtual(i + 1, comp.substr(uniqueTo), f, Change());
|
||||
}
|
||||
|
||||
// Handle some situations that abruptly terminate the row
|
||||
// - Before an inset with BreakBefore
|
||||
// - After an inset with BreakAfter
|
||||
Inset const * prevInset = !row.empty() ? row.back().inset : 0;
|
||||
Inset const * nextInset = (i + 1 < end) ? par.getInset(i + 1) : 0;
|
||||
if ((nextInset && nextInset->rowFlags() & Inset::BreakBefore)
|
||||
|| (prevInset && prevInset->rowFlags() & Inset::BreakAfter)) {
|
||||
row.flushed(true);
|
||||
// Force a row creation after this one if it is ended by
|
||||
// an inset that either
|
||||
// - has row flag RowAfter that enforces that;
|
||||
// - or (1) did force the row breaking, (2) is at end of
|
||||
// paragraph and (3) the said paragraph has an end label.
|
||||
need_new_row = prevInset &&
|
||||
(prevInset->rowFlags() & Inset::RowAfter
|
||||
|| (prevInset->rowFlags() & Inset::BreakAfter && i + 1 == end
|
||||
&& text_->getEndLabel(row.pit()) != END_LABEL_NO_LABEL));
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
++fi;
|
||||
}
|
||||
row.finalizeLast();
|
||||
row.endpos(i);
|
||||
row.endpos(end);
|
||||
|
||||
// End of paragraph marker. The logic here is almost the
|
||||
// End of paragraph marker, either if LyXRc requires it, or there
|
||||
// is an end of paragraph change. The logic here is almost the
|
||||
// same as in redoParagraph, remember keep them in sync.
|
||||
ParagraphList const & pars = text_->paragraphs();
|
||||
Change const & change = par.lookupChange(i);
|
||||
if ((lyxrc.paragraph_markers || change.changed())
|
||||
&& !need_new_row
|
||||
&& i == end && size_type(row.pit() + 1) < pars.size()) {
|
||||
Change const & endchange = par.lookupChange(end);
|
||||
if (endchange.changed())
|
||||
row.needsChangeBar(true);
|
||||
if ((lyxrc.paragraph_markers || endchange.changed())
|
||||
&& size_type(pit + 1) < pars.size()) {
|
||||
// add a virtual element for the end-of-paragraph
|
||||
// marker; it is shown on screen, but does not exist
|
||||
// in the paragraph.
|
||||
Font f(text_->layoutFont(row.pit()));
|
||||
Font f(text_->layoutFont(pit));
|
||||
f.fontInfo().setColor(Color_paragraphmarker);
|
||||
f.setLanguage(par.getParLanguage(buf.params()));
|
||||
// ¶ U+00B6 PILCROW SIGN
|
||||
row.addVirtual(end, docstring(1, char_type(0x00B6)), f, change);
|
||||
row.addVirtual(end, docstring(1, char_type(0x00B6)), f, endchange);
|
||||
}
|
||||
|
||||
// Is there a end-of-paragaph change?
|
||||
if (i == end && par.lookupChange(end).changed() && !need_new_row)
|
||||
row.needsChangeBar(true);
|
||||
|
||||
// if the row is too large, try to cut at last separator. In case
|
||||
// of success, reset indication that the row was broken abruptly.
|
||||
int const next_width = max_width_ - leftMargin(row.pit(), row.endpos())
|
||||
- rightMargin(row.pit());
|
||||
|
||||
if (row.shortenIfNeeded(body_pos, width, next_width))
|
||||
row.flushed(false);
|
||||
row.right_boundary(!row.empty() && row.endpos() < end
|
||||
&& row.back().endpos == row.endpos());
|
||||
// Last row in paragraph is flushed
|
||||
if (row.endpos() == end)
|
||||
row.flushed(true);
|
||||
|
||||
// make sure that the RTL elements are in reverse ordering
|
||||
row.reverseRTL(is_rtl);
|
||||
//LYXERR0("breakrow: row is " << row);
|
||||
|
||||
return need_new_row;
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/** Helper template flexible_const_iterator<T>
|
||||
* A way to iterate over a const container, but insert fake elements in it.
|
||||
* In the case of a row, we will have to break some elements, which
|
||||
* create new ones. This class allows to abstract this.
|
||||
* Only the required parts are implemented for now.
|
||||
*/
|
||||
template<class T>
|
||||
class flexible_const_iterator {
|
||||
typedef typename T::value_type value_type;
|
||||
public:
|
||||
|
||||
//
|
||||
flexible_const_iterator operator++() {
|
||||
if (pile_.empty())
|
||||
++cit_;
|
||||
else
|
||||
pile_.pop_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*() const { return pile_.empty() ? *cit_ : pile_.back(); }
|
||||
|
||||
value_type const * operator->() const { return pile_.empty() ? &*cit_ : &pile_.back(); }
|
||||
|
||||
void put(value_type const & e) { pile_.push_back(e); }
|
||||
|
||||
// Put a sequence of elements on the pile (in reverse order!)
|
||||
void put(vector<value_type> const & elts) {
|
||||
pile_.insert(pile_.end(), elts.rbegin(), elts.rend());
|
||||
}
|
||||
|
||||
// This should be private, but declaring the friend functions is too much work
|
||||
//private:
|
||||
typename T::const_iterator cit_;
|
||||
// A vector that is used as like a pile to store the elements to
|
||||
// consider before incrementing the underlying iterator.
|
||||
vector<value_type> pile_;
|
||||
};
|
||||
|
||||
|
||||
template<class T>
|
||||
flexible_const_iterator<T> flexible_begin(T const & t)
|
||||
{
|
||||
return { t.begin(), vector<typename T::value_type>() };
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
flexible_const_iterator<T> flexible_end(T const & t)
|
||||
{
|
||||
return { t.end(), vector<typename T::value_type>() };
|
||||
}
|
||||
|
||||
|
||||
// Equality is only possible if respective piles are empty
|
||||
template<class T>
|
||||
bool operator==(flexible_const_iterator<T> const & t1,
|
||||
flexible_const_iterator<T> const & t2)
|
||||
{
|
||||
return t1.cit_ == t2.cit_ && t1.pile_.empty() && t2.pile_.empty();
|
||||
}
|
||||
|
||||
|
||||
Row newRow(TextMetrics const & tm, pit_type pit, pos_type pos, bool is_rtl)
|
||||
{
|
||||
Row nrow;
|
||||
nrow.pit(pit);
|
||||
nrow.pos(pos);
|
||||
nrow.left_margin = tm.leftMargin(pit, pos);
|
||||
nrow.right_margin = tm.rightMargin(pit);
|
||||
nrow.setRTL(is_rtl);
|
||||
if (is_rtl)
|
||||
swap(nrow.left_margin, nrow.right_margin);
|
||||
// Remember that the row width takes into account the left_margin
|
||||
// but not the right_margin.
|
||||
nrow.dim().wid = nrow.left_margin;
|
||||
return nrow;
|
||||
}
|
||||
|
||||
|
||||
void cleanupRow(Row & row, bool at_end)
|
||||
{
|
||||
if (row.empty()) {
|
||||
row.endpos(0);
|
||||
return;
|
||||
}
|
||||
|
||||
row.endpos(row.back().endpos);
|
||||
// remove trailing spaces on row break
|
||||
if (!at_end)
|
||||
row.back().rtrim();
|
||||
// boundary exists when there was no space at the end of row
|
||||
row.right_boundary(!at_end && row.back().endpos == row.endpos());
|
||||
// make sure that the RTL elements are in reverse ordering
|
||||
row.reverseRTL();
|
||||
}
|
||||
|
||||
|
||||
// Implement the priorities described in RowFlags.h.
|
||||
bool needsRowBreak(int f1, int f2)
|
||||
{
|
||||
if (f1 & AlwaysBreakAfter /*|| f2 & AlwaysBreakBefore*/)
|
||||
return true;
|
||||
if (f1 & NoBreakAfter || f2 & NoBreakBefore)
|
||||
return false;
|
||||
if (f1 & BreakAfter || f2 & BreakBefore)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
RowList TextMetrics::breakParagraph(Row const & bigrow) const
|
||||
{
|
||||
RowList rows;
|
||||
bool const is_rtl = text_->isRTL(bigrow.pit());
|
||||
bool const end_label = text_->getEndLabel(bigrow.pit()) != END_LABEL_NO_LABEL;
|
||||
int const next_width = max_width_ - leftMargin(bigrow.pit(), bigrow.endpos())
|
||||
- rightMargin(bigrow.pit());
|
||||
|
||||
int width = 0;
|
||||
flexible_const_iterator<Row> fcit = flexible_begin(bigrow);
|
||||
flexible_const_iterator<Row> const end = flexible_end(bigrow);
|
||||
while (true) {
|
||||
bool const row_empty = rows.empty() || rows.back().empty();
|
||||
// The row flags of previous element, if there is one.
|
||||
// Otherwise we use NoBreakAfter to avoid an empty row before
|
||||
// e.g. a displayed equation.
|
||||
int const f1 = row_empty ? NoBreakAfter : rows.back().back().row_flags;
|
||||
// The row flags of next element, if there is one.
|
||||
// Otherwise we use NoBreakBefore (see above), unless the
|
||||
// paragraph has an end label (for which an empty row is OK).
|
||||
int const f2 = (fcit == end) ? (end_label ? Inline : NoBreakBefore)
|
||||
: fcit->row_flags;
|
||||
if (rows.empty() || needsRowBreak(f1, f2)) {
|
||||
if (!rows.empty()) {
|
||||
cleanupRow(rows.back(), false);
|
||||
// Flush row as requested by row flags
|
||||
rows.back().flushed((f1 & Flush) || (f2 & FlushBefore));
|
||||
}
|
||||
pos_type pos = rows.empty() ? 0 : rows.back().endpos();
|
||||
rows.push_back(newRow(*this, bigrow.pit(), pos, is_rtl));
|
||||
// the width available for the row.
|
||||
width = max_width_ - rows.back().right_margin;
|
||||
}
|
||||
|
||||
// The stopping condition is here because we may need a new
|
||||
// empty row at the end.
|
||||
if (fcit == end)
|
||||
break;
|
||||
|
||||
// Next element to consider is either the top of the temporary
|
||||
// pile, or the place when we were in main row
|
||||
Row::Element elt = *fcit;
|
||||
Row::Elements tail;
|
||||
elt.splitAt(width - rows.back().width(), next_width, false, tail);
|
||||
Row & rb = rows.back();
|
||||
rb.push_back(elt);
|
||||
rb.finalizeLast();
|
||||
if (rb.width() > width) {
|
||||
LATTEST(tail.empty());
|
||||
// if the row is too large, try to cut at last separator.
|
||||
tail = rb.shortenIfNeeded(width, next_width);
|
||||
}
|
||||
|
||||
// Go to next element
|
||||
++fcit;
|
||||
|
||||
// Handle later the elements returned by splitAt or shortenIfNeeded.
|
||||
fcit.put(tail);
|
||||
}
|
||||
|
||||
if (!rows.empty()) {
|
||||
cleanupRow(rows.back(), true);
|
||||
// Last row in paragraph is flushed
|
||||
rows.back().flushed(true);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
int TextMetrics::parTopSpacing(pit_type const pit) const
|
||||
{
|
||||
Paragraph const & par = text_->getPar(pit);
|
||||
@ -1775,7 +1893,7 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const
|
||||
// display style insets do not need indentation
|
||||
&& !(!par.empty()
|
||||
&& par.isInset(0)
|
||||
&& par.getInset(0)->rowFlags() & Inset::Display)
|
||||
&& par.getInset(0)->rowFlags() & Display)
|
||||
&& (!(tclass.isDefaultLayout(par.layout())
|
||||
|| tclass.isPlainLayout(par.layout()))
|
||||
|| buffer.params().paragraph_separation
|
||||
|
@ -151,10 +151,11 @@ private:
|
||||
/// FIXME??
|
||||
int labelEnd(pit_type const pit) const;
|
||||
|
||||
/// sets row.end to the pos value *after* which a row should break.
|
||||
/// for example, the pos after which isNewLine(pos) == true
|
||||
/// \return true when another row is required (after a newline)
|
||||
bool breakRow(Row & row, int right_margin) const;
|
||||
// Turn paragraph oh index \c pit into a single row
|
||||
Row tokenizeParagraph(pit_type pit) const;
|
||||
|
||||
// Break the row produced by tokenizeParagraph() into a list of rows.
|
||||
RowList breakParagraph(Row const & row) const;
|
||||
|
||||
// Expands the alignment of row \param row in paragraph \param par
|
||||
LyXAlignment getAlign(Paragraph const & par, Row const & row) const;
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
#include "support/strfwd.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* A class holding helper functions for determining
|
||||
* the screen dimensions of fonts.
|
||||
@ -121,14 +123,27 @@ public:
|
||||
* \param ws is the amount of extra inter-word space applied text justification.
|
||||
*/
|
||||
virtual int x2pos(docstring const & s, int & x, bool rtl, double ws) const = 0;
|
||||
|
||||
// The places where to break a string and the width of the resulting lines.
|
||||
struct Break {
|
||||
Break(int l, int w) : len(l), wid(w) {}
|
||||
int len = 0;
|
||||
int wid = 0;
|
||||
};
|
||||
typedef std::vector<Break> Breaks;
|
||||
/**
|
||||
* Break string at width at most x.
|
||||
* \return true if successful
|
||||
* \param rtl is true for right-to-left layout
|
||||
* Break a string in multiple fragments according to width limits.
|
||||
* \return a sequence of Break elements.
|
||||
* \param s is the string to break.
|
||||
* \param first_wid is the available width for first line.
|
||||
* \param wid is the available width for the next lines.
|
||||
* \param rtl is true for right-to-left layout.
|
||||
* \param force is false for breaking at word separator, true for
|
||||
* arbitrary position.
|
||||
*/
|
||||
virtual bool breakAt(docstring & s, int & x, bool rtl, bool force) const = 0;
|
||||
virtual Breaks
|
||||
breakString(docstring const & s, int first_wid, int wid, bool rtl, bool force) const = 0;
|
||||
|
||||
/// return char dimension for the font.
|
||||
virtual Dimension const dimension(char_type c) const = 0;
|
||||
/**
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "support/convert.h"
|
||||
#include "support/lassert.h"
|
||||
#include "support/lstrings.h"
|
||||
#include "support/lyxlib.h"
|
||||
#include "support/debug.h"
|
||||
|
||||
@ -85,25 +86,27 @@ namespace lyx {
|
||||
namespace frontend {
|
||||
|
||||
|
||||
/*
|
||||
* Limit (strwidth|breakat)_cache_ size to 512kB of string data.
|
||||
* Limit qtextlayout_cache_ size to 500 elements (we do not know the
|
||||
* size of the QTextLayout objects anyway).
|
||||
* Note that all these numbers are arbitrary.
|
||||
* Also, setting size to 0 is tantamount to disabling the cache.
|
||||
*/
|
||||
int cache_metrics_width_size = 1 << 19;
|
||||
int cache_metrics_breakat_size = 1 << 19;
|
||||
namespace {
|
||||
// Maximal size/cost for various caches. See QCache documentation to
|
||||
// see what cost means.
|
||||
|
||||
// Limit strwidth_cache_ total cost to 1MB of string data.
|
||||
int const strwidth_cache_max_cost = 1024 * 1024;
|
||||
// Limit breakat_cache_ total cost to 10MB of string data.
|
||||
// This is useful for documents with very large insets.
|
||||
int const breakstr_cache_max_cost = 10 * 1024 * 1024;
|
||||
// Qt 5.x already has its own caching of QTextLayout objects
|
||||
// but it does not seem to work well on MacOS X.
|
||||
#if (QT_VERSION < 0x050000) || defined(Q_OS_MAC)
|
||||
int cache_metrics_qtextlayout_size = 500;
|
||||
// Limit qtextlayout_cache_ size to 500 elements (we do not know the
|
||||
// size of the QTextLayout objects anyway).
|
||||
int const qtextlayout_cache_max_size = 500;
|
||||
#else
|
||||
int cache_metrics_qtextlayout_size = 0;
|
||||
// Disable the cache
|
||||
int const qtextlayout_cache_max_size = 0;
|
||||
#endif
|
||||
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Convert a UCS4 character into a QChar.
|
||||
* This is a hack (it does only make sense for the common part of the UCS4
|
||||
@ -127,9 +130,9 @@ inline QChar const ucs4_to_qchar(char_type const ucs4)
|
||||
|
||||
GuiFontMetrics::GuiFontMetrics(QFont const & font)
|
||||
: font_(font), metrics_(font, 0),
|
||||
strwidth_cache_(cache_metrics_width_size),
|
||||
breakat_cache_(cache_metrics_breakat_size),
|
||||
qtextlayout_cache_(cache_metrics_qtextlayout_size)
|
||||
strwidth_cache_(strwidth_cache_max_cost),
|
||||
breakstr_cache_(breakstr_cache_max_cost),
|
||||
qtextlayout_cache_(qtextlayout_cache_max_size)
|
||||
{
|
||||
// Determine italic slope
|
||||
double const defaultSlope = tan(qDegreesToRadians(19.0));
|
||||
@ -485,11 +488,13 @@ int GuiFontMetrics::countExpanders(docstring const & str) const
|
||||
}
|
||||
|
||||
|
||||
pair<int, int>
|
||||
GuiFontMetrics::breakAt_helper(docstring const & s, int const x,
|
||||
bool const rtl, bool const force) const
|
||||
namespace {
|
||||
|
||||
const int brkStrOffset = 1 + BIDI_OFFSET;
|
||||
|
||||
|
||||
QString createBreakableString(docstring const & s, bool rtl, QTextLayout & tl)
|
||||
{
|
||||
QTextLayout tl;
|
||||
/* Qt will not break at a leading or trailing space, and we need
|
||||
* that sometimes, see http://www.lyx.org/trac/ticket/9921.
|
||||
*
|
||||
@ -518,31 +523,23 @@ GuiFontMetrics::breakAt_helper(docstring const & s, int const x,
|
||||
// Left-to-right override: forces to draw text left-to-right
|
||||
qs = QChar(0x202D) + qs;
|
||||
#endif
|
||||
int const offset = 1 + BIDI_OFFSET;
|
||||
return qs;
|
||||
}
|
||||
|
||||
tl.setText(qs);
|
||||
tl.setFont(font_);
|
||||
QTextOption to;
|
||||
to.setWrapMode(force ? QTextOption::WrapAtWordBoundaryOrAnywhere
|
||||
: QTextOption::WordWrap);
|
||||
tl.setTextOption(to);
|
||||
tl.beginLayout();
|
||||
QTextLine line = tl.createLine();
|
||||
line.setLineWidth(x);
|
||||
tl.createLine();
|
||||
tl.endLayout();
|
||||
int const line_wid = iround(line.horizontalAdvance());
|
||||
if ((force && line.textLength() == offset) || line_wid > x)
|
||||
return {-1, -1};
|
||||
|
||||
docstring::size_type brkstr2str_pos(QString brkstr, docstring const & str, int pos)
|
||||
{
|
||||
/* Since QString is UTF-16 and docstring is UCS-4, the offsets may
|
||||
* not be the same when there are high-plan unicode characters
|
||||
* (bug #10443).
|
||||
*/
|
||||
// The variable `offset' is here to account for the extra leading characters.
|
||||
// The variable `brkStrOffset' is here to account for the extra leading characters.
|
||||
// The ending character zerow_nbsp has to be ignored if the line is complete.
|
||||
int const qlen = line.textLength() - offset - (line.textLength() == qs.length());
|
||||
int const qlen = pos - brkStrOffset - (pos == brkstr.length());
|
||||
#if QT_VERSION < 0x040801 || QT_VERSION >= 0x050100
|
||||
int len = qstring_to_ucs4(qs.mid(offset, qlen)).length();
|
||||
auto const len = qstring_to_ucs4(brkstr.mid(brkStrOffset, qlen)).length();
|
||||
// Avoid warning
|
||||
(void)str;
|
||||
#else
|
||||
/* Due to QTBUG-25536 in 4.8.1 <= Qt < 5.1.0, the string returned
|
||||
* by QString::toUcs4 (used by qstring_to_ucs4) may have wrong
|
||||
@ -552,42 +549,108 @@ GuiFontMetrics::breakAt_helper(docstring const & s, int const x,
|
||||
* worthwhile to implement a dichotomy search if this shows up
|
||||
* under a profiler.
|
||||
*/
|
||||
int len = min(qlen, static_cast<int>(s.length()));
|
||||
while (len >= 0 && toqstr(s.substr(0, len)).length() != qlen)
|
||||
int len = min(qlen, static_cast<int>(str.length()));
|
||||
while (len >= 0 && toqstr(str.substr(0, len)).length() != qlen)
|
||||
--len;
|
||||
LASSERT(len > 0 || qlen == 0, /**/);
|
||||
#endif
|
||||
return {len, line_wid};
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FontMetrics::Breaks
|
||||
GuiFontMetrics::breakString_helper(docstring const & s, int first_wid, int wid,
|
||||
bool rtl, bool force) const
|
||||
{
|
||||
QTextLayout tl;
|
||||
QString qs = createBreakableString(s, rtl, tl);
|
||||
tl.setText(qs);
|
||||
tl.setFont(font_);
|
||||
QTextOption to;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
to.setWrapMode(force ? QTextOption::WrapAtWordBoundaryOrAnywhere
|
||||
: QTextOption::WordWrap);
|
||||
// Let QTextLine::naturalTextWidth() account for trailing spaces
|
||||
// (horizontalAdvance() still does not).
|
||||
to.setFlags(QTextOption::IncludeTrailingSpaces);
|
||||
tl.setTextOption(to);
|
||||
|
||||
bool first = true;
|
||||
tl.beginLayout();
|
||||
while(true) {
|
||||
QTextLine line = tl.createLine();
|
||||
if (!line.isValid())
|
||||
break;
|
||||
line.setLineWidth(first ? first_wid : wid);
|
||||
tl.createLine();
|
||||
first = false;
|
||||
}
|
||||
tl.endLayout();
|
||||
|
||||
Breaks breaks;
|
||||
int pos = 0;
|
||||
for (int i = 0 ; i < tl.lineCount() ; ++i) {
|
||||
QTextLine const & line = tl.lineAt(i);
|
||||
int const epos = brkstr2str_pos(qs, s, line.textStart() + line.textLength());
|
||||
#if QT_VERSION >= 0x050000
|
||||
int const wid = i + 1 < tl.lineCount() ? iround(line.horizontalAdvance())
|
||||
: iround(line.naturalTextWidth());
|
||||
#else
|
||||
// With some monospace fonts, the value of horizontalAdvance()
|
||||
// can be wrong with Qt4. One hypothesis is that the invisible
|
||||
// characters that we use are given a non-null width.
|
||||
// FIXME: this is slower than it could be but we'll get rid of Qt4 anyway
|
||||
int const wid = i + 1 < tl.lineCount() ? width(rtrim(s.substr(pos, epos - pos)))
|
||||
: width(s.substr(pos, epos - pos));
|
||||
#endif
|
||||
breaks.emplace_back(epos - pos, wid);
|
||||
pos = epos;
|
||||
#if 0
|
||||
// FIXME: should it be kept in some form?
|
||||
if ((force && line.textLength() == brkStrOffset) || line_wid > x)
|
||||
return {-1, line_wid};
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return breaks;
|
||||
}
|
||||
|
||||
|
||||
uint qHash(BreakAtKey const & key)
|
||||
uint qHash(BreakStringKey const & key)
|
||||
{
|
||||
int params = key.force + 2 * key.rtl + 4 * key.x;
|
||||
// assume widths are less than 10000. This fits in 32 bits.
|
||||
uint params = key.force + 2 * key.rtl + 4 * key.first_wid + 10000 * key.wid;
|
||||
return std::qHash(key.s) ^ ::qHash(params);
|
||||
}
|
||||
|
||||
|
||||
bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const force) const
|
||||
FontMetrics::Breaks GuiFontMetrics::breakString(docstring const & s, int first_wid, int wid,
|
||||
bool rtl, bool force) const
|
||||
{
|
||||
PROFILE_THIS_BLOCK(breakAt);
|
||||
PROFILE_THIS_BLOCK(breakString);
|
||||
if (s.empty())
|
||||
return false;
|
||||
return Breaks();
|
||||
|
||||
BreakAtKey key{s, x, rtl, force};
|
||||
pair<int, int> pp;
|
||||
if (auto * pp_ptr = breakat_cache_.object_ptr(key))
|
||||
pp = *pp_ptr;
|
||||
BreakStringKey key{s, first_wid, wid, rtl, force};
|
||||
Breaks brks;
|
||||
if (auto * brks_ptr = breakstr_cache_.object_ptr(key))
|
||||
brks = *brks_ptr;
|
||||
else {
|
||||
PROFILE_CACHE_MISS(breakAt);
|
||||
pp = breakAt_helper(s, x, rtl, force);
|
||||
breakat_cache_.insert(key, pp, sizeof(key) + s.size() * sizeof(char_type));
|
||||
PROFILE_CACHE_MISS(breakString);
|
||||
brks = breakString_helper(s, first_wid, wid, rtl, force);
|
||||
breakstr_cache_.insert(key, brks, sizeof(key) + s.size() * sizeof(char_type));
|
||||
}
|
||||
if (pp.first == -1)
|
||||
return false;
|
||||
s = s.substr(0, pp.first);
|
||||
x = pp.second;
|
||||
return true;
|
||||
return brks;
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,14 +27,16 @@
|
||||
namespace lyx {
|
||||
namespace frontend {
|
||||
|
||||
struct BreakAtKey
|
||||
struct BreakStringKey
|
||||
{
|
||||
bool operator==(BreakAtKey const & key) const {
|
||||
return key.s == s && key.x == x && key.rtl == rtl && key.force == force;
|
||||
bool operator==(BreakStringKey const & key) const {
|
||||
return key.s == s && key.first_wid == first_wid && key.wid == wid
|
||||
&& key.rtl == rtl && key.force == force;
|
||||
}
|
||||
|
||||
docstring s;
|
||||
int x;
|
||||
int first_wid;
|
||||
int wid;
|
||||
bool rtl;
|
||||
bool force;
|
||||
};
|
||||
@ -77,7 +79,7 @@ public:
|
||||
int signedWidth(docstring const & s) const override;
|
||||
int pos2x(docstring const & s, int pos, bool rtl, double ws) const override;
|
||||
int x2pos(docstring const & s, int & x, bool rtl, double ws) const override;
|
||||
bool breakAt(docstring & s, int & x, bool rtl, bool force) const override;
|
||||
Breaks breakString(docstring const & s, int first_wid, int wid, bool rtl, bool force) const override;
|
||||
Dimension const dimension(char_type c) const override;
|
||||
|
||||
void rectText(docstring const & str,
|
||||
@ -101,8 +103,8 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
std::pair<int, int> breakAt_helper(docstring const & s, int const x,
|
||||
bool const rtl, bool const force) const;
|
||||
Breaks breakString_helper(docstring const & s, int first_wid, int wid,
|
||||
bool rtl, bool force) const;
|
||||
|
||||
/// The font
|
||||
QFont font_;
|
||||
@ -117,8 +119,8 @@ private:
|
||||
mutable QHash<char_type, int> width_cache_;
|
||||
/// Cache of string widths
|
||||
mutable Cache<docstring, int> strwidth_cache_;
|
||||
/// Cache for breakAt
|
||||
mutable Cache<BreakAtKey, std::pair<int, int>> breakat_cache_;
|
||||
/// Cache for breakString
|
||||
mutable Cache<BreakStringKey, Breaks> breakstr_cache_;
|
||||
/// Cache for QTextLayout
|
||||
mutable Cache<TextLayoutKey, std::shared_ptr<QTextLayout>> qtextlayout_cache_;
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "LayoutEnums.h"
|
||||
#include "OutputEnums.h"
|
||||
#include "OutputParams.h"
|
||||
#include "RowFlags.h"
|
||||
|
||||
#include "support/docstring.h"
|
||||
#include "support/strfwd.h"
|
||||
@ -478,26 +479,8 @@ public:
|
||||
|
||||
virtual CtObject getCtObject(OutputParams const &) const;
|
||||
|
||||
enum RowFlags {
|
||||
Inline = 0,
|
||||
// break row before this inset
|
||||
BreakBefore = 1 << 0,
|
||||
// break row after this inset
|
||||
BreakAfter = 1 << 1,
|
||||
// it is possible to break after this inset
|
||||
CanBreakAfter = 1 << 2,
|
||||
// force new (maybe empty) row after this inset
|
||||
RowAfter = 1 << 3,
|
||||
// specify an alignment (left, right) for a display inset
|
||||
// (default is center)
|
||||
AlignLeft = 1 << 4,
|
||||
AlignRight = 1 << 5,
|
||||
// A display inset breaks row at both ends
|
||||
Display = BreakBefore | BreakAfter
|
||||
};
|
||||
|
||||
/// How should this inset be displayed in its row?
|
||||
virtual RowFlags rowFlags() const { return Inline; }
|
||||
// properties with respect to row breaking (made of RowFLag enums)
|
||||
virtual int rowFlags() const { return Inline; }
|
||||
/// indentation before this inset (only needed for displayed hull insets with fleqn option)
|
||||
virtual int indent(BufferView const &) const { return 0; }
|
||||
///
|
||||
@ -655,20 +638,6 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
inline Inset::RowFlags operator|(Inset::RowFlags const d1,
|
||||
Inset::RowFlags const d2)
|
||||
{
|
||||
return static_cast<Inset::RowFlags>(int(d1) | int(d2));
|
||||
}
|
||||
|
||||
|
||||
inline Inset::RowFlags operator&(Inset::RowFlags const d1,
|
||||
Inset::RowFlags const d2)
|
||||
{
|
||||
return static_cast<Inset::RowFlags>(int(d1) & int(d2));
|
||||
}
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
#endif
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
///
|
||||
InsetCode lyxCode() const override { return BIBTEX_CODE; }
|
||||
///
|
||||
RowFlags rowFlags() const override { return Display; }
|
||||
int rowFlags() const override { return Display; }
|
||||
///
|
||||
void latex(otexstream &, OutputParams const &) const override;
|
||||
///
|
||||
|
@ -40,7 +40,7 @@ private:
|
||||
///
|
||||
void write(std::ostream & os) const override;
|
||||
///
|
||||
RowFlags rowFlags() const override { return Display; }
|
||||
int rowFlags() const override { return Display; }
|
||||
///
|
||||
bool neverIndent() const override { return true; }
|
||||
///
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
///
|
||||
InsetCode lyxCode() const override { return FLOAT_LIST_CODE; }
|
||||
///
|
||||
RowFlags rowFlags() const override { return Display; }
|
||||
int rowFlags() const override { return Display; }
|
||||
///
|
||||
void write(std::ostream &) const override;
|
||||
///
|
||||
|
@ -1251,7 +1251,7 @@ string InsetInclude::contextMenuName() const
|
||||
}
|
||||
|
||||
|
||||
Inset::RowFlags InsetInclude::rowFlags() const
|
||||
int InsetInclude::rowFlags() const
|
||||
{
|
||||
return type(params()) == INPUT ? Inline : Display;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public:
|
||||
///
|
||||
void draw(PainterInfo & pi, int x, int y) const override;
|
||||
///
|
||||
RowFlags rowFlags() const override;
|
||||
int rowFlags() const override;
|
||||
///
|
||||
InsetCode lyxCode() const override { return INCLUDE_CODE; }
|
||||
///
|
||||
|
@ -119,7 +119,7 @@ public:
|
||||
///
|
||||
bool hasSettings() const override;
|
||||
///
|
||||
RowFlags rowFlags() const override { return Display; }
|
||||
int rowFlags() const override { return Display; }
|
||||
//@}
|
||||
|
||||
/// \name Static public methods obligated for InsetCommand derived classes
|
||||
|
@ -64,7 +64,7 @@ InsetListings::~InsetListings()
|
||||
}
|
||||
|
||||
|
||||
Inset::RowFlags InsetListings::rowFlags() const
|
||||
int InsetListings::rowFlags() const
|
||||
{
|
||||
return params().isInline() || params().isFloat() ? Inline : Display | AlignLeft;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ private:
|
||||
///
|
||||
InsetCode lyxCode() const override { return LISTINGS_CODE; }
|
||||
/// lstinline is inlined, normal listing is displayed
|
||||
RowFlags rowFlags() const override;
|
||||
int rowFlags() const override;
|
||||
///
|
||||
docstring layoutName() const override;
|
||||
///
|
||||
|
@ -39,6 +39,15 @@ InsetNewline::InsetNewline() : Inset(nullptr)
|
||||
{}
|
||||
|
||||
|
||||
int InsetNewline::rowFlags() const
|
||||
{
|
||||
if (params_.kind == InsetNewlineParams::LINEBREAK)
|
||||
return AlwaysBreakAfter;
|
||||
else
|
||||
return AlwaysBreakAfter | Flush;
|
||||
}
|
||||
|
||||
|
||||
void InsetNewlineParams::write(ostream & os) const
|
||||
{
|
||||
switch (kind) {
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
explicit InsetNewline(InsetNewlineParams par) : Inset(0)
|
||||
{ params_.kind = par.kind; }
|
||||
///
|
||||
RowFlags rowFlags() const override { return BreakAfter | RowAfter; }
|
||||
int rowFlags() const override;
|
||||
///
|
||||
static void string2params(std::string const &, InsetNewlineParams &);
|
||||
///
|
||||
|
@ -76,7 +76,7 @@ private:
|
||||
///
|
||||
void write(std::ostream & os) const override;
|
||||
///
|
||||
RowFlags rowFlags() const override { return (params_.kind == InsetNewpageParams::NOPAGEBREAK) ? Inline : Display; }
|
||||
int rowFlags() const override { return (params_.kind == InsetNewpageParams::NOPAGEBREAK) ? Inline : Display; }
|
||||
///
|
||||
docstring insetLabel() const;
|
||||
///
|
||||
|
@ -94,7 +94,7 @@ public:
|
||||
///
|
||||
bool hasSettings() const override { return true; }
|
||||
///
|
||||
RowFlags rowFlags() const override { return Display; }
|
||||
int rowFlags() const override { return Display; }
|
||||
///
|
||||
void latex(otexstream &, OutputParams const &) const override;
|
||||
///
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
return docstring();
|
||||
}
|
||||
///
|
||||
RowFlags rowFlags() const override { return BreakAfter; }
|
||||
int rowFlags() const override { return BreakAfter | Flush; }
|
||||
private:
|
||||
///
|
||||
InsetCode lyxCode() const override { return SEPARATOR_CODE; }
|
||||
|
@ -192,7 +192,7 @@ bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
}
|
||||
|
||||
|
||||
Inset::RowFlags InsetSpace::rowFlags() const
|
||||
int InsetSpace::rowFlags() const
|
||||
{
|
||||
switch (params_.kind) {
|
||||
case InsetSpaceParams::PROTECTED:
|
||||
|
@ -115,7 +115,7 @@ public:
|
||||
///
|
||||
docstring toolTip(BufferView const & bv, int x, int y) const override;
|
||||
/// unprotected spaces allow line breaking after them
|
||||
RowFlags rowFlags() const override;
|
||||
int rowFlags() const override;
|
||||
///
|
||||
void metrics(MetricsInfo &, Dimension &) const override;
|
||||
///
|
||||
|
@ -83,7 +83,7 @@ docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const
|
||||
}
|
||||
|
||||
|
||||
Inset::RowFlags InsetSpecialChar::rowFlags() const
|
||||
int InsetSpecialChar::rowFlags() const
|
||||
{
|
||||
switch (kind_) {
|
||||
case ALLOWBREAK:
|
||||
|
@ -63,7 +63,7 @@ public:
|
||||
///
|
||||
docstring toolTip(BufferView const & bv, int x, int y) const override;
|
||||
/// some special chars allow line breaking after them
|
||||
RowFlags rowFlags() const override;
|
||||
int rowFlags() const override;
|
||||
///
|
||||
void metrics(MetricsInfo &, Dimension &) const override;
|
||||
///
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
///
|
||||
docstring layoutName() const override;
|
||||
///
|
||||
RowFlags rowFlags() const override { return Display; }
|
||||
int rowFlags() const override { return Display; }
|
||||
///
|
||||
void validate(LaTeXFeatures &) const override;
|
||||
///
|
||||
|
@ -6144,21 +6144,21 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
}
|
||||
|
||||
|
||||
Inset::RowFlags InsetTabular::rowFlags() const
|
||||
int InsetTabular::rowFlags() const
|
||||
{
|
||||
if (tabular.is_long_tabular) {
|
||||
switch (tabular.longtabular_alignment) {
|
||||
case Tabular::LYX_LONGTABULAR_ALIGN_LEFT:
|
||||
return Display | AlignLeft;
|
||||
case Tabular::LYX_LONGTABULAR_ALIGN_CENTER:
|
||||
return Display;
|
||||
case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT:
|
||||
return Display | AlignRight;
|
||||
default:
|
||||
return Display;
|
||||
}
|
||||
} else
|
||||
return Inline;
|
||||
if (tabular.is_long_tabular) {
|
||||
switch (tabular.longtabular_alignment) {
|
||||
case Tabular::LYX_LONGTABULAR_ALIGN_LEFT:
|
||||
return Display | AlignLeft;
|
||||
case Tabular::LYX_LONGTABULAR_ALIGN_CENTER:
|
||||
return Display;
|
||||
case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT:
|
||||
return Display | AlignRight;
|
||||
default:
|
||||
return Display;
|
||||
}
|
||||
} else
|
||||
return Inline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -989,7 +989,7 @@ public:
|
||||
//
|
||||
bool isTable() const override { return true; }
|
||||
///
|
||||
RowFlags rowFlags() const override;
|
||||
int rowFlags() const override;
|
||||
///
|
||||
void latex(otexstream &, OutputParams const &) const override;
|
||||
///
|
||||
|
@ -62,7 +62,7 @@ private:
|
||||
///
|
||||
void write(std::ostream & os) const override;
|
||||
///
|
||||
RowFlags rowFlags() const override { return Display; }
|
||||
int rowFlags() const override { return Display; }
|
||||
///
|
||||
void doDispatch(Cursor & cur, FuncRequest & cmd) override;
|
||||
///
|
||||
|
@ -988,7 +988,7 @@ bool InsetMathHull::outerDisplay() const
|
||||
}
|
||||
|
||||
|
||||
Inset::RowFlags InsetMathHull::rowFlags() const
|
||||
int InsetMathHull::rowFlags() const
|
||||
{
|
||||
switch (type_) {
|
||||
case hullUnknown:
|
||||
|
@ -288,7 +288,7 @@ public:
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y) override;
|
||||
///
|
||||
RowFlags rowFlags() const override;
|
||||
int rowFlags() const override;
|
||||
/// helper function
|
||||
bool display() const { return rowFlags() & Display; }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user