mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 13:18:28 +00:00
Merge branch 'rowpainter2'
This commit is contained in:
commit
b63421b7dc
136
src/Cursor.cpp
136
src/Cursor.cpp
@ -1018,139 +1018,17 @@ bool Cursor::posVisToNewRow(bool movingLeft)
|
||||
|
||||
void Cursor::posVisToRowExtremity(bool left)
|
||||
{
|
||||
// prepare bidi tables
|
||||
Paragraph const & par = paragraph();
|
||||
Buffer const & buf = *buffer();
|
||||
Row const & row = textRow();
|
||||
Bidi bidi;
|
||||
bidi.computeTables(par, buf, row);
|
||||
|
||||
LYXERR(Debug::RTL, "entering extremity: " << pit() << "," << pos() << ","
|
||||
<< (boundary() ? 1 : 0));
|
||||
|
||||
if (left) { // move to leftmost position
|
||||
// if this is an RTL paragraph, and we're at the last row in the
|
||||
// paragraph, move to lastpos
|
||||
if (par.isRTL(buf.params()) && row.endpos() == lastpos())
|
||||
pos() = lastpos();
|
||||
else {
|
||||
pos() = bidi.vis2log(row.pos());
|
||||
TextMetrics const & tm = bv_->textMetrics(text());
|
||||
// Looking for extremities is like clicking on the left or the
|
||||
// right of the row.
|
||||
int x = tm.origin().x_ + (left ? 0 : textRow().width());
|
||||
bool b = false;
|
||||
pos() = tm.getPosNearX(textRow(), x, b);
|
||||
boundary(b);
|
||||
|
||||
// Moving to the leftmost position in the row,
|
||||
// the cursor should normally be placed to the
|
||||
// *left* of the leftmost position. A very
|
||||
// common exception, though, is if the
|
||||
// leftmost character also happens to be the
|
||||
// separator at the (logical) end of the row
|
||||
// --- in this case, the separator is
|
||||
// positioned beyond the left margin, and we
|
||||
// don't want to move the cursor there (moving
|
||||
// to the left of the separator is equivalent
|
||||
// to moving to the next line). So, in this
|
||||
// case we actually want to place the cursor
|
||||
// to the *right* of the leftmost position
|
||||
// (the separator). Another exception is if
|
||||
// we're moving to the logically last position
|
||||
// in the row, which is *not* a separator:
|
||||
// this means that the entire row has no
|
||||
// separators (if there were any, the row
|
||||
// would have been broken there); and
|
||||
// therefore in this case we also move to the
|
||||
// *right* of the last position (this
|
||||
// indicates to the user that there is no
|
||||
// space after this position, and is
|
||||
// consistent with the behavior in the middle
|
||||
// of a row --- moving right or left moves to
|
||||
// the next/previous character; if we were to
|
||||
// move to the *left* of this position, that
|
||||
// would simulate a separator which is not
|
||||
// really there!). Finally, there is an
|
||||
// exception to the previous exception: if
|
||||
// this non-separator-but-last-position-in-row
|
||||
// is an inset, then we *do* want to stay to
|
||||
// the left of it anyway: this is the
|
||||
// "boundary" which we simulate at insets.
|
||||
|
||||
// Another exception is when row.endpos() is
|
||||
// 0.
|
||||
|
||||
// do we want to be to the right of pos?
|
||||
// as explained above, if at last pos in row, stay to the right
|
||||
bool const right_of_pos = row.endpos() > 0
|
||||
&& pos() == row.endpos() - 1 && !par.isInset(pos());
|
||||
|
||||
// Now we know if we want to be to the left or to the right of pos,
|
||||
// let's make sure we are where we want to be.
|
||||
bool const new_pos_is_RTL =
|
||||
par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
|
||||
|
||||
if (new_pos_is_RTL != right_of_pos) {
|
||||
++pos();
|
||||
boundary(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// move to rightmost position
|
||||
// if this is an LTR paragraph, and we're at the last row in the
|
||||
// paragraph, move to lastpos
|
||||
if (!par.isRTL(buf.params()) && row.endpos() == lastpos())
|
||||
pos() = lastpos();
|
||||
else {
|
||||
pos() = row.endpos() > 0 ? bidi.vis2log(row.endpos() - 1) : 0;
|
||||
|
||||
// Moving to the rightmost position in the
|
||||
// row, the cursor should normally be placed
|
||||
// to the *right* of the rightmost position. A
|
||||
// very common exception, though, is if the
|
||||
// rightmost character also happens to be the
|
||||
// separator at the (logical) end of the row
|
||||
// --- in this case, the separator is
|
||||
// positioned beyond the right margin, and we
|
||||
// don't want to move the cursor there (moving
|
||||
// to the right of the separator is equivalent
|
||||
// to moving to the next line). So, in this
|
||||
// case we actually want to place the cursor
|
||||
// to the *left* of the rightmost position
|
||||
// (the separator). Another exception is if
|
||||
// we're moving to the logically last position
|
||||
// in the row, which is *not* a separator:
|
||||
// this means that the entire row has no
|
||||
// separators (if there were any, the row
|
||||
// would have been broken there); and
|
||||
// therefore in this case we also move to the
|
||||
// *left* of the last position (this indicates
|
||||
// to the user that there is no space after
|
||||
// this position, and is consistent with the
|
||||
// behavior in the middle of a row --- moving
|
||||
// right or left moves to the next/previous
|
||||
// character; if we were to move to the
|
||||
// *right* of this position, that would
|
||||
// simulate a separator which is not really
|
||||
// there!). Finally, there is an exception to
|
||||
// the previous exception: if this
|
||||
// non-separator-but-last-position-in-row is
|
||||
// an inset, then we *do* want to stay to the
|
||||
// right of it anyway: this is the "boundary"
|
||||
// which we simulate at insets. Another
|
||||
// exception is when row.endpos() is 0.
|
||||
|
||||
// do we want to be to the left of pos?
|
||||
// as explained above, if at last pos in row, stay to the left,
|
||||
// unless the last position is the same as the first.
|
||||
bool const left_of_pos = row.endpos() > 0
|
||||
&& pos() == row.endpos() - 1 && !par.isInset(pos());
|
||||
|
||||
// Now we know if we want to be to the left or to the right of pos,
|
||||
// let's make sure we are where we want to be.
|
||||
bool const new_pos_is_RTL =
|
||||
par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
|
||||
|
||||
if (new_pos_is_RTL == left_of_pos) {
|
||||
++pos();
|
||||
boundary(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
LYXERR(Debug::RTL, "leaving extremity: " << pit() << "," << pos() << ","
|
||||
<< (boundary() ? 1 : 0));
|
||||
}
|
||||
|
139
src/Row.cpp
139
src/Row.cpp
@ -24,6 +24,7 @@
|
||||
|
||||
#include "support/debug.h"
|
||||
#include "support/lassert.h"
|
||||
#include "support/lstrings.h"
|
||||
#include "support/lyxalgo.h"
|
||||
|
||||
#include <ostream>
|
||||
@ -32,8 +33,18 @@ using namespace std;
|
||||
|
||||
namespace lyx {
|
||||
|
||||
using support::rtrim;
|
||||
using frontend::FontMetrics;
|
||||
|
||||
|
||||
int Row::Element::countSeparators() const
|
||||
{
|
||||
if (type != STRING)
|
||||
return 0;
|
||||
return count(str.begin(), str.end(), ' ');
|
||||
}
|
||||
|
||||
|
||||
double Row::Element::pos2x(pos_type const i) const
|
||||
{
|
||||
// This can happen with inline completion when clicking on the
|
||||
@ -52,7 +63,7 @@ double Row::Element::pos2x(pos_type const i) const
|
||||
w = rtl ? full_width() : 0;
|
||||
else {
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
w = fm.pos2x(str, i - pos, font.isVisibleRightToLeft());
|
||||
w = fm.pos2x(str, i - pos, font.isVisibleRightToLeft(), extra);
|
||||
}
|
||||
|
||||
return w;
|
||||
@ -68,7 +79,7 @@ pos_type Row::Element::x2pos(int &x) const
|
||||
switch (type) {
|
||||
case STRING: {
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
i = fm.x2pos(str, x, rtl);
|
||||
i = fm.x2pos(str, x, rtl, extra);
|
||||
break;
|
||||
}
|
||||
case VIRTUAL:
|
||||
@ -76,7 +87,6 @@ pos_type Row::Element::x2pos(int &x) const
|
||||
i = 0;
|
||||
x = rtl ? int(full_width()) : 0;
|
||||
break;
|
||||
case SEPARATOR:
|
||||
case INSET:
|
||||
case SPACE:
|
||||
// those elements contain only one position. Round to
|
||||
@ -96,24 +106,21 @@ pos_type Row::Element::x2pos(int &x) const
|
||||
}
|
||||
|
||||
|
||||
bool Row::Element::breakAt(int w)
|
||||
bool Row::Element::breakAt(int w, bool force)
|
||||
{
|
||||
if (type != STRING || dim.wid <= w)
|
||||
return false;
|
||||
|
||||
bool const rtl = font.isVisibleRightToLeft();
|
||||
if (rtl)
|
||||
w = dim.wid - 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;
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
int x = w;
|
||||
if(fm.breakAt(str, x, rtl, 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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -225,9 +232,6 @@ ostream & operator<<(ostream & os, Row::Element const & e)
|
||||
case Row::INSET:
|
||||
os << "INSET: " << to_utf8(e.inset->layoutName()) << ", ";
|
||||
break;
|
||||
case Row::SEPARATOR:
|
||||
os << "SEPARATOR: extra=" << e.extra << ", ";
|
||||
break;
|
||||
case Row::SPACE:
|
||||
os << "SPACE: ";
|
||||
break;
|
||||
@ -258,6 +262,26 @@ ostream & operator<<(ostream & os, Row const & row)
|
||||
}
|
||||
|
||||
|
||||
int Row::countSeparators() const
|
||||
{
|
||||
int n = 0;
|
||||
const_iterator const end = elements_.end();
|
||||
for (const_iterator cit = elements_.begin() ; cit != end ; ++cit)
|
||||
n += cit->countSeparators();
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void Row::setSeparatorExtraWidth(double w)
|
||||
{
|
||||
separator = w;
|
||||
iterator const end = elements_.end();
|
||||
for (iterator it = elements_.begin() ; it != end ; ++it)
|
||||
if (it->type == Row::STRING)
|
||||
it->extra = w;
|
||||
}
|
||||
|
||||
|
||||
bool Row::sameString(Font const & f, Change const & ch) const
|
||||
{
|
||||
if (elements_.empty())
|
||||
@ -278,6 +302,7 @@ void Row::finalizeLast()
|
||||
elt.final = true;
|
||||
|
||||
if (elt.type == STRING) {
|
||||
dim_.wid -= elt.dim.wid;
|
||||
elt.dim.wid = theFontMetrics(elt.font).width(elt.str);
|
||||
dim_.wid += elt.dim.wid;
|
||||
}
|
||||
@ -304,8 +329,16 @@ void Row::add(pos_type const pos, char_type const c,
|
||||
Element e(STRING, pos, f, ch);
|
||||
elements_.push_back(e);
|
||||
}
|
||||
back().str += c;
|
||||
back().endpos = pos + 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -323,18 +356,6 @@ void Row::addVirtual(pos_type const pos, docstring const & s,
|
||||
}
|
||||
|
||||
|
||||
void Row::addSeparator(pos_type const pos, char_type const c,
|
||||
Font const & f, Change const & ch)
|
||||
{
|
||||
finalizeLast();
|
||||
Element e(SEPARATOR, pos, f, ch);
|
||||
e.str += c;
|
||||
e.dim.wid = theFontMetrics(f).width(c);
|
||||
elements_.push_back(e);
|
||||
dim_.wid += e.dim.wid;
|
||||
}
|
||||
|
||||
|
||||
void Row::addSpace(pos_type const pos, int const width,
|
||||
Font const & f, Change const & ch)
|
||||
{
|
||||
@ -357,33 +378,17 @@ void Row::shortenIfNeeded(pos_type const keep, int const w)
|
||||
{
|
||||
if (empty() || width() <= w)
|
||||
return;
|
||||
|
||||
Elements::iterator const beg = elements_.begin();
|
||||
Elements::iterator const end = elements_.end();
|
||||
Elements::iterator last_sep = elements_.end();
|
||||
int last_width = 0;
|
||||
int wid = left_margin;
|
||||
|
||||
Elements::iterator cit = beg;
|
||||
for ( ; cit != end ; ++cit) {
|
||||
if (cit->type == SEPARATOR && cit->pos >= keep) {
|
||||
last_sep = cit;
|
||||
last_width = wid;
|
||||
}
|
||||
if (wid + cit->dim.wid > w)
|
||||
if (cit->endpos >= keep && wid + cit->dim.wid > w)
|
||||
break;
|
||||
wid += cit->dim.wid;
|
||||
}
|
||||
|
||||
if (last_sep != end) {
|
||||
// We have found a suitable separator. This is the
|
||||
// common case.
|
||||
end_ = last_sep->endpos;
|
||||
dim_.wid = last_width;
|
||||
elements_.erase(last_sep, end);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cit == end) {
|
||||
// This should not happen since the row is too long.
|
||||
LYXERR0("Something is wrong cannot shorten row: " << *this);
|
||||
@ -397,23 +402,41 @@ void Row::shortenIfNeeded(pos_type const keep, int const w)
|
||||
wid -= cit->dim.wid;
|
||||
}
|
||||
|
||||
// Try to break this row cleanly (at word boundary)
|
||||
if (cit->breakAt(w - wid, false)) {
|
||||
end_ = cit->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 endo of the row, since the spaces at row break
|
||||
// are invisible.
|
||||
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;
|
||||
}
|
||||
|
||||
if (cit != beg) {
|
||||
// There is no separator, but several elements (probably
|
||||
// insets) have been added. We can cut at this place.
|
||||
// There is no separator, but several elements have been
|
||||
// added. We can cut right here.
|
||||
end_ = cit->pos;
|
||||
dim_.wid = wid;
|
||||
elements_.erase(cit, end);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
/* 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.
|
||||
*/
|
||||
if (cit->breakAt(w - left_margin)) {
|
||||
if (cit->breakAt(w - wid, true)) {
|
||||
end_ = cit->endpos;
|
||||
dim_.wid = left_margin + cit->dim.wid;
|
||||
// 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);
|
||||
}
|
||||
|
41
src/Row.h
41
src/Row.h
@ -29,18 +29,6 @@ namespace lyx {
|
||||
class DocIterator;
|
||||
class Inset;
|
||||
|
||||
/**
|
||||
* FIXME: Change Row object to operate only on integers and not doubles.
|
||||
*
|
||||
* This use of double is only useful to distribute the extra
|
||||
* horizontal space between separators in justified text. If we do
|
||||
* integer arithmetic, then it is possible to have two groups of
|
||||
* separators, with size s or s+1. Then strings can be drawn without
|
||||
* cutting at separators in justfied text, as it is done in
|
||||
* non-justified text. This will improve performance.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* An on-screen row of text. A paragraph is broken into a RowList for
|
||||
* display. Each Row contains a tokenized description of the contents
|
||||
@ -58,8 +46,6 @@ public:
|
||||
* correspond to any paragraph contents
|
||||
*/
|
||||
VIRTUAL,
|
||||
// A stretchable space, basically
|
||||
SEPARATOR,
|
||||
// An inset
|
||||
INSET,
|
||||
// Some spacing described by its width, not a string
|
||||
@ -76,7 +62,10 @@ public:
|
||||
extra(0), font(f), change(ch), final(false) {}
|
||||
|
||||
// Return total width of element, including separator overhead
|
||||
double full_width() const { return dim.wid + extra; };
|
||||
double full_width() const { return dim.wid + extra * countSeparators(); };
|
||||
// Return the number of separator in the element (only STRING type)
|
||||
int countSeparators() const;
|
||||
|
||||
/** Return position in pixels (from the left) of position
|
||||
* \param i in the row element.
|
||||
*/
|
||||
@ -86,10 +75,12 @@ public:
|
||||
* adjusted to the actual pixel position.
|
||||
*/
|
||||
pos_type x2pos(int &x) const;
|
||||
/** Break the element if possible, so that its width is
|
||||
* less then \param w. Returns true on success.
|
||||
/** 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.
|
||||
*/
|
||||
bool breakAt(int w);
|
||||
bool breakAt(int w, bool force);
|
||||
|
||||
// Returns the position on left side of the element.
|
||||
pos_type left_pos() const;
|
||||
@ -109,10 +100,10 @@ public:
|
||||
// Non-zero only if element is an inset
|
||||
Inset const * inset;
|
||||
|
||||
// Only non-null for separator elements
|
||||
// Only non-null for justified rows
|
||||
double extra;
|
||||
|
||||
// Non-empty if element is a string or separator
|
||||
// Non-empty if element is a string or is virtual
|
||||
docstring str;
|
||||
//
|
||||
Font font;
|
||||
@ -172,6 +163,11 @@ public:
|
||||
///
|
||||
int descent() const { return dim_.des; }
|
||||
|
||||
// Return the number of separators in the row
|
||||
int countSeparators() const;
|
||||
// Set the extra spacing for every separator in STRING elements
|
||||
void setSeparatorExtraWidth(double w);
|
||||
|
||||
///
|
||||
void add(pos_type pos, Inset const * ins, Dimension const & dim,
|
||||
Font const & f, Change const & ch);
|
||||
@ -182,9 +178,6 @@ public:
|
||||
void addVirtual(pos_type pos, docstring const & s,
|
||||
Font const & f, Change const & ch);
|
||||
///
|
||||
void addSeparator(pos_type pos, char_type const c,
|
||||
Font const & f, Change const & ch);
|
||||
///
|
||||
void addSpace(pos_type pos, int width, Font const & f, Change const & ch);
|
||||
|
||||
///
|
||||
@ -239,7 +232,7 @@ public:
|
||||
|
||||
friend std::ostream & operator<<(std::ostream & os, Row const & row);
|
||||
|
||||
/// width of a separator (i.e. space)
|
||||
/// additional width for separators in justified rows (i.e. space)
|
||||
double separator;
|
||||
/// width of hfills in the label
|
||||
double label_hfill;
|
||||
|
@ -64,8 +64,6 @@ RowPainter::RowPainter(PainterInfo & pi,
|
||||
solid_line_thickness_(1), solid_line_offset_(1),
|
||||
dotted_line_thickness_(1), dotted_line_offset_(2)
|
||||
{
|
||||
bidi_.computeTables(par_, pi_.base.bv->buffer(), row_);
|
||||
|
||||
if (lyxrc.zoom >= 200) {
|
||||
// derive the line thickness from zoom factor
|
||||
// the zoom is given in percent
|
||||
@ -103,32 +101,33 @@ FontInfo RowPainter::labelFont() const
|
||||
}
|
||||
|
||||
|
||||
int RowPainter::leftMargin() const
|
||||
{
|
||||
return text_metrics_.leftMargin(text_metrics_.width(), pit_,
|
||||
row_.pos());
|
||||
}
|
||||
|
||||
// If you want to debug inset metrics uncomment the following line:
|
||||
//#define DEBUG_METRICS
|
||||
// This draws green lines around each inset.
|
||||
|
||||
|
||||
void RowPainter::paintInset(Inset const * inset, pos_type const pos)
|
||||
void RowPainter::paintInset(Inset const * inset, Font const & font,
|
||||
Change const & change,
|
||||
pos_type const pos)
|
||||
{
|
||||
Font const font = text_metrics_.displayFont(pit_, pos);
|
||||
// Handle selection
|
||||
bool const pi_selected = pi_.selected;
|
||||
Cursor const & cur = pi_.base.bv->cursor();
|
||||
if (cur.selection() && cur.text() == &text_
|
||||
&& cur.normalAnchor().text() == &text_)
|
||||
pi_.selected = row_.sel_beg <= pos && row_.sel_end > pos;
|
||||
|
||||
LASSERT(inset, return);
|
||||
// Backup full_repaint status because some insets (InsetTabular)
|
||||
// requires a full repaint
|
||||
bool const pi_full_repaint = pi_.full_repaint;
|
||||
bool const pi_do_spellcheck = pi_.do_spellcheck;
|
||||
Change const pi_change = pi_.change_;
|
||||
|
||||
pi_.base.font = inset->inheritFont() ? font.fontInfo() :
|
||||
pi_.base.bv->buffer().params().getFont().fontInfo();
|
||||
pi_.ltr_pos = (bidi_.level(pos) % 2 == 0);
|
||||
Change prev_change = change_;
|
||||
pi_.change_ = change_.changed() ? change_ : par_.lookupChange(pos);
|
||||
pi_.ltr_pos = !font.isVisibleRightToLeft();
|
||||
pi_.change_ = change_.changed() ? change_ : change;
|
||||
pi_.do_spellcheck &= inset->allowSpellCheck();
|
||||
|
||||
int const x1 = int(x_);
|
||||
@ -148,8 +147,9 @@ void RowPainter::paintInset(Inset const * inset, pos_type const pos)
|
||||
|
||||
// Restore full_repaint status.
|
||||
pi_.full_repaint = pi_full_repaint;
|
||||
pi_.change_ = prev_change;
|
||||
pi_.change_ = pi_change;
|
||||
pi_.do_spellcheck = pi_do_spellcheck;
|
||||
pi_.selected = pi_selected;
|
||||
|
||||
#ifdef DEBUG_METRICS
|
||||
int const x2 = x1 + dim.wid;
|
||||
@ -163,14 +163,6 @@ void RowPainter::paintInset(Inset const * inset, pos_type const pos)
|
||||
}
|
||||
|
||||
|
||||
void RowPainter::paintSeparator(double orig_x, double width,
|
||||
FontInfo const & font)
|
||||
{
|
||||
pi_.pain.textDecoration(font, int(orig_x), yo_, int(width));
|
||||
x_ += width;
|
||||
}
|
||||
|
||||
|
||||
void RowPainter::paintForeignMark(double orig_x, Language const * lang, int desc) const
|
||||
{
|
||||
if (!lyxrc.mark_foreign_language)
|
||||
@ -187,14 +179,12 @@ void RowPainter::paintForeignMark(double orig_x, Language const * lang, int desc
|
||||
|
||||
|
||||
void RowPainter::paintMisspelledMark(double const orig_x,
|
||||
docstring const & str, Font const & font,
|
||||
pos_type const start_pos,
|
||||
bool const changed) const
|
||||
Row::Element const & e) const
|
||||
{
|
||||
// if changed the misspelled marker gets placed slightly lower than normal
|
||||
// to avoid drawing at the same vertical offset
|
||||
int const y = yo_ + solid_line_offset_ + solid_line_thickness_
|
||||
+ (changed ? solid_line_thickness_ + 1 : 0)
|
||||
+ (e.change.changed() ? solid_line_thickness_ + 1 : 0)
|
||||
+ dotted_line_offset_;
|
||||
|
||||
//FIXME: this could be computed only once, it is probably not costly.
|
||||
@ -211,8 +201,8 @@ void RowPainter::paintMisspelledMark(double const orig_x,
|
||||
--cpos;
|
||||
}
|
||||
|
||||
pos_type pos = start_pos;
|
||||
while (pos < start_pos + pos_type(str.length())) {
|
||||
pos_type pos = e.pos;
|
||||
while (pos < e.pos + pos_type(e.str.length())) {
|
||||
if (!par_.isMisspelled(pos)) {
|
||||
++pos;
|
||||
continue;
|
||||
@ -227,12 +217,12 @@ void RowPainter::paintMisspelledMark(double const orig_x,
|
||||
continue;
|
||||
}
|
||||
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
int x1 = fm.pos2x(str, range.first - start_pos,
|
||||
font.isVisibleRightToLeft());
|
||||
int x2 = fm.pos2x(str, min(range.last - start_pos + 1,
|
||||
pos_type(str.length())),
|
||||
font.isVisibleRightToLeft());
|
||||
FontMetrics const & fm = theFontMetrics(e.font);
|
||||
int x1 = fm.pos2x(e.str, range.first - e.pos,
|
||||
e.font.isVisibleRightToLeft(), e.extra);
|
||||
int x2 = fm.pos2x(e.str, min(range.last - e.pos + 1,
|
||||
pos_type(e.str.length())),
|
||||
e.font.isVisibleRightToLeft(), e.extra);
|
||||
if (x1 > x2)
|
||||
swap(x1, x2);
|
||||
|
||||
@ -244,120 +234,45 @@ void RowPainter::paintMisspelledMark(double const orig_x,
|
||||
}
|
||||
|
||||
|
||||
void RowPainter::paintStringAndSel(docstring const & str, Font const & font,
|
||||
Change const & change,
|
||||
pos_type start_pos, pos_type end_pos)
|
||||
void RowPainter::paintStringAndSel(Row::Element const & e)
|
||||
{
|
||||
// at least part of text selected?
|
||||
bool const some_sel = (end_pos >= row_.sel_beg && start_pos < row_.sel_end)
|
||||
bool const some_sel = (e.endpos >= row_.sel_beg && e.pos < row_.sel_end)
|
||||
|| pi_.selected;
|
||||
// all the text selected?
|
||||
bool const all_sel = (start_pos >= row_.sel_beg && end_pos < row_.sel_end)
|
||||
bool const all_sel = (e.pos >= row_.sel_beg && e.endpos < row_.sel_end)
|
||||
|| pi_.selected;
|
||||
|
||||
if (all_sel) {
|
||||
Font copy = font;
|
||||
Font copy = e.font;
|
||||
copy.fontInfo().setPaintColor(Color_selectiontext);
|
||||
x_ += pi_.pain.text(int(x_), yo_, str, copy);
|
||||
} else if (change.changed()) {
|
||||
Font copy = font;
|
||||
copy.fontInfo().setPaintColor(change.color());
|
||||
x_ += pi_.pain.text(int(x_), yo_, str, copy);
|
||||
pi_.pain.text(int(x_), yo_, e.str, copy, e.extra);
|
||||
} else if (e.change.changed()) {
|
||||
Font copy = e.font;
|
||||
copy.fontInfo().setPaintColor(e.change.color());
|
||||
pi_.pain.text(int(x_), yo_, e.str, copy, e.extra);
|
||||
} else if (!some_sel) {
|
||||
x_ += pi_.pain.text(int(x_), yo_, str, font);
|
||||
pi_.pain.text(int(x_), yo_, e.str, e.font, e.extra);
|
||||
} else {
|
||||
x_ += pi_.pain.text(int(x_), yo_, str, font, Color_selectiontext,
|
||||
max(row_.sel_beg, start_pos) - start_pos,
|
||||
min(row_.sel_end, end_pos) - start_pos);
|
||||
pi_.pain.text(int(x_), yo_, e.str, e.font, Color_selectiontext,
|
||||
max(row_.sel_beg, e.pos) - e.pos,
|
||||
min(row_.sel_end, e.endpos) - e.pos, e.extra);
|
||||
}
|
||||
x_ += e.full_width();
|
||||
}
|
||||
|
||||
|
||||
void RowPainter::paintFromPos(pos_type & vpos, bool changed)
|
||||
void RowPainter::paintChange(double orig_x, Font const & font,
|
||||
Change const & change) const
|
||||
{
|
||||
pos_type pos = bidi_.vis2log(vpos);
|
||||
pos_type start_pos = pos;
|
||||
// first character
|
||||
docstring str;
|
||||
str.reserve(100);
|
||||
char_type const c = par_.getChar(pos);
|
||||
str.push_back(c);
|
||||
|
||||
double const orig_x = x_;
|
||||
|
||||
Font const font = text_metrics_.displayFont(pit_, pos);
|
||||
FontSpan const font_span = par_.fontSpan(pos);
|
||||
// Track-change status.
|
||||
Change const & change_running = par_.lookupChange(pos);
|
||||
|
||||
// collect as much similar chars as we can
|
||||
pos_type const end = row_.endpos();
|
||||
for (++vpos ; vpos < end ; ++vpos) {
|
||||
pos = bidi_.vis2log(vpos);
|
||||
|
||||
if (!font_span.contains(pos))
|
||||
break;
|
||||
|
||||
Change const & change = par_.lookupChange(pos);
|
||||
if (!change_running.isSimilarTo(change))
|
||||
// Track change type or author has changed.
|
||||
break;
|
||||
|
||||
char_type const c = par_.getChar(pos);
|
||||
|
||||
if (c == '\t')
|
||||
break;
|
||||
|
||||
if (!isPrintableNonspace(c))
|
||||
break;
|
||||
|
||||
str.push_back(c);
|
||||
}
|
||||
|
||||
// Make pos point to the last character in the string.
|
||||
// Using "pos = bidi_.vis2log(vpos)" does not work for some reason.
|
||||
if (vpos < end)
|
||||
pos = bidi_.vis2log(vpos - 1);
|
||||
|
||||
// Now make pos point to the position _after_ the string.
|
||||
// Using vis2log for that is not a good idea in general, we
|
||||
// want logical ordering.
|
||||
if (font.isVisibleRightToLeft())
|
||||
--pos;
|
||||
else
|
||||
++pos;
|
||||
|
||||
if (str[0] == '\t')
|
||||
str.replace(0,1,from_ascii(" "));
|
||||
|
||||
/* Because we do our own bidi, at this point the strings are
|
||||
* already in visual order. However, Qt also applies its own
|
||||
* bidi algorithm to strings that it paints to the screen.
|
||||
* Therefore, if we were to paint Hebrew/Arabic words as a
|
||||
* single string, the letters in the words would get reversed
|
||||
* again. In order to avoid that, we reverse our string.
|
||||
* See also http://thread.gmane.org/gmane.editors.lyx.devel/79740
|
||||
* for an earlier thread on the subject
|
||||
*/
|
||||
if (font.isVisibleRightToLeft()) {
|
||||
reverse(str.begin(), str.end());
|
||||
// If the string is reversed, the positions need to be adjusted
|
||||
++pos;
|
||||
++start_pos;
|
||||
swap(start_pos, pos);
|
||||
}
|
||||
|
||||
// Actually paint the text, taking care about the selection
|
||||
paintStringAndSel(str, font, change_running, start_pos, pos);
|
||||
|
||||
// The line that indicates word in a different language
|
||||
paintForeignMark(orig_x, font.language());
|
||||
|
||||
// Paint the spelling mark if needed.
|
||||
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck
|
||||
&& par_.isMisspelled(start_pos)) {
|
||||
paintMisspelledMark(orig_x, str, font, start_pos, changed);
|
||||
}
|
||||
if (!change.changed())
|
||||
return;
|
||||
// Calculate 1/3 height of font
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
int const y_bar = change.deleted() ? yo_ - fm.maxAscent() / 3
|
||||
: yo_ + 2 * solid_line_offset_ + solid_line_thickness_;
|
||||
pi_.pain.line(int(orig_x), y_bar, int(x_), y_bar,
|
||||
change.color(), Painter::line_solid, solid_line_thickness_);
|
||||
}
|
||||
|
||||
|
||||
@ -535,13 +450,10 @@ void RowPainter::paintLabel() const
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
double x = x_;
|
||||
|
||||
if (is_rtl) {
|
||||
x = width_ - leftMargin()
|
||||
+ fm.width(layout.labelsep);
|
||||
} else {
|
||||
x = x_ - fm.width(layout.labelsep)
|
||||
- fm.width(str);
|
||||
}
|
||||
if (is_rtl)
|
||||
x = width_ - row_.right_margin + fm.width(layout.labelsep);
|
||||
else
|
||||
x = x_ - fm.width(layout.labelsep) - fm.width(str);
|
||||
|
||||
pi_.pain.text(int(x), yo_, str, font);
|
||||
}
|
||||
@ -575,12 +487,10 @@ void RowPainter::paintTopLevelLabel() const
|
||||
|
||||
double x = x_;
|
||||
if (layout.labeltype == LABEL_CENTERED) {
|
||||
if (is_rtl)
|
||||
x = leftMargin();
|
||||
x += (width_ - text_metrics_.rightMargin(pm_) - leftMargin()) / 2;
|
||||
x = row_.left_margin + (width_ - row_.left_margin - row_.right_margin) / 2;
|
||||
x -= fm.width(str) / 2;
|
||||
} else if (is_rtl) {
|
||||
x = width_ - leftMargin() - fm.width(str);
|
||||
x = width_ - row_.right_margin - fm.width(str);
|
||||
}
|
||||
pi_.pain.text(int(x), yo_ - maxdesc - labeladdon, str, font);
|
||||
}
|
||||
@ -655,7 +565,7 @@ void RowPainter::paintLast()
|
||||
int const y = yo_ - size;
|
||||
int const max_row_width = width_ - size - Inset::TEXT_TO_INSET_OFFSET;
|
||||
int x = is_rtl ? nestMargin() + changebarMargin()
|
||||
: max_row_width - text_metrics_.rightMargin(pm_);
|
||||
: max_row_width - row_.right_margin;
|
||||
|
||||
// If needed, move the box a bit to avoid overlapping with text.
|
||||
int const rem = max_row_width - row_.width();
|
||||
@ -679,13 +589,6 @@ void RowPainter::paintLast()
|
||||
}
|
||||
|
||||
case END_LABEL_NO_LABEL:
|
||||
if (lyxrc.paragraph_markers && size_type(pit_ + 1) < pars_.size()) {
|
||||
docstring const s = docstring(1, char_type(0x00B6));
|
||||
FontInfo f = FontInfo(text_.layoutFont(pit_));
|
||||
f.setColor(Color_paragraphmarker);
|
||||
pi_.pain.text(int(x_), yo_, s, f);
|
||||
x_ += theFontMetrics(f).width(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -693,194 +596,65 @@ void RowPainter::paintLast()
|
||||
|
||||
void RowPainter::paintOnlyInsets()
|
||||
{
|
||||
CoordCache const & cache = pi_.base.bv->coordCache();
|
||||
pos_type const end = row_.endpos();
|
||||
for (pos_type pos = row_.pos(); pos != end; ++pos) {
|
||||
// If outer row has changed, nested insets are repaint completely.
|
||||
Inset const * inset = par_.getInset(pos);
|
||||
bool const nested_inset = inset &&
|
||||
((inset->asInsetMath() &&
|
||||
!inset->asInsetMath()->asMacroTemplate())
|
||||
|| inset->asInsetText()
|
||||
|| inset->asInsetTabular());
|
||||
if (!nested_inset)
|
||||
continue;
|
||||
if (x_ > pi_.base.bv->workWidth()
|
||||
|| !cache.getInsets().has(inset))
|
||||
continue;
|
||||
x_ = cache.getInsets().x(inset);
|
||||
|
||||
bool const pi_selected = pi_.selected;
|
||||
Cursor const & cur = pi_.base.bv->cursor();
|
||||
if (cur.selection() && cur.text() == &text_
|
||||
&& cur.normalAnchor().text() == &text_)
|
||||
pi_.selected = row_.sel_beg <= pos && row_.sel_end > pos;
|
||||
paintInset(inset, pos);
|
||||
pi_.selected = pi_selected;
|
||||
Row::const_iterator cit = row_.begin();
|
||||
Row::const_iterator const & end = row_.end();
|
||||
for ( ; cit != end ; ++cit) {
|
||||
Row::Element const & e = *cit;
|
||||
if (e.type == Row::INSET) {
|
||||
// If outer row has changed, nested insets are repainted completely.
|
||||
pi_.base.bv->coordCache().insets().add(e.inset, int(x_), yo_);
|
||||
bool const nested_inset =
|
||||
(e.inset->asInsetMath() && !e.inset->asInsetMath()->asMacroTemplate())
|
||||
|| e.inset->asInsetText() || e.inset->asInsetTabular();
|
||||
if (!nested_inset) {
|
||||
x_ += e.full_width();
|
||||
continue;
|
||||
}
|
||||
paintInset(e.inset, e.font, e.change, e.pos);
|
||||
} else
|
||||
x_ += e.full_width();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RowPainter::paintText()
|
||||
{
|
||||
//LYXERR0("-------------------------------------------------------");
|
||||
pos_type const end = row_.endpos();
|
||||
// Spaces at logical line breaks in bidi text must be skipped during
|
||||
// painting. However, they may appear visually in the middle
|
||||
// of a row; they must be skipped, wherever they are...
|
||||
// * logically "abc_[HEBREW_\nHEBREW]"
|
||||
// * visually "abc_[_WERBEH\nWERBEH]"
|
||||
pos_type skipped_sep_vpos = -1;
|
||||
pos_type body_pos = par_.beginOfBody();
|
||||
if (body_pos > 0 &&
|
||||
(body_pos > end || !par_.isLineSeparator(body_pos - 1))) {
|
||||
body_pos = 0;
|
||||
}
|
||||
Row::const_iterator cit = row_.begin();
|
||||
Row::const_iterator const & end = row_.end();
|
||||
for ( ; cit != end ; ++cit) {
|
||||
double const orig_x = x_;
|
||||
Row::Element const & e = *cit;
|
||||
int foreign_descent = 0;
|
||||
|
||||
Layout const & layout = par_.layout();
|
||||
switch (e.type) {
|
||||
case Row::STRING:
|
||||
case Row::VIRTUAL:
|
||||
paintStringAndSel(e);
|
||||
|
||||
Change change_running;
|
||||
int change_last_x = 0;
|
||||
|
||||
// check for possible inline completion
|
||||
DocIterator const & inlineCompletionPos = pi_.base.bv->inlineCompletionPos();
|
||||
pos_type inlineCompletionVPos = -1;
|
||||
if (inlineCompletionPos.inTexted()
|
||||
&& inlineCompletionPos.text() == &text_
|
||||
&& inlineCompletionPos.pit() == pit_
|
||||
&& inlineCompletionPos.pos() - 1 >= row_.pos()
|
||||
&& inlineCompletionPos.pos() - 1 < row_.endpos()) {
|
||||
// draw logically behind the previous character
|
||||
inlineCompletionVPos = bidi_.log2vis(inlineCompletionPos.pos() - 1);
|
||||
}
|
||||
|
||||
// Use font span to speed things up, see below
|
||||
FontSpan font_span;
|
||||
Font font;
|
||||
|
||||
// If the last logical character is a separator, don't paint it, unless
|
||||
// it's in the last row of a paragraph; see skipped_sep_vpos declaration
|
||||
if (end > 0 && end < par_.size() && par_.isSeparator(end - 1))
|
||||
skipped_sep_vpos = bidi_.log2vis(end - 1);
|
||||
|
||||
for (pos_type vpos = row_.pos(); vpos < end; ) {
|
||||
if (x_ > pi_.base.bv->workWidth())
|
||||
// Paint the spelling mark if needed.
|
||||
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck
|
||||
&& par_.isMisspelled(e.pos)) {
|
||||
paintMisspelledMark(orig_x, e);
|
||||
}
|
||||
break;
|
||||
|
||||
// Skip the separator at the logical end of the row
|
||||
if (vpos == skipped_sep_vpos) {
|
||||
++vpos;
|
||||
continue;
|
||||
}
|
||||
|
||||
pos_type const pos = bidi_.vis2log(vpos);
|
||||
|
||||
if (pos >= par_.size()) {
|
||||
++vpos;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use font span to speed things up, see above
|
||||
if (!font_span.contains(pos)) {
|
||||
font_span = par_.fontSpan(pos);
|
||||
font = text_metrics_.displayFont(pit_, pos);
|
||||
|
||||
// split font span if inline completion is inside
|
||||
if (inlineCompletionVPos != -1
|
||||
&& font_span.contains(inlineCompletionPos.pos()))
|
||||
font_span.last = inlineCompletionPos.pos();
|
||||
}
|
||||
|
||||
// Note that this value will only be used in
|
||||
// situations where no ligature of composition of
|
||||
// characters is needed. (see comments in uses of width_pos).
|
||||
const int width_pos = pm_.singleWidth(pos, font);
|
||||
|
||||
Change const & change = par_.lookupChange(pos);
|
||||
if (change.changed() && !change_running.changed()) {
|
||||
change_running = change;
|
||||
change_last_x = int(x_);
|
||||
}
|
||||
|
||||
Inset const * inset = par_.getInset(pos);
|
||||
bool const highly_editable_inset = inset
|
||||
&& inset->editable();
|
||||
|
||||
// If we reach the end of a change or if the author changes, paint it.
|
||||
// We also don't paint across things like tables
|
||||
if (change_running.changed() && (highly_editable_inset
|
||||
|| !change.changed() || !change_running.isSimilarTo(change))) {
|
||||
// Calculate 1/3 height of the buffer's default font
|
||||
FontMetrics const & fm
|
||||
= theFontMetrics(pi_.base.bv->buffer().params().getFont());
|
||||
int const y_bar = change_running.deleted() ?
|
||||
yo_ - fm.maxAscent() / 3 : yo_ + 2 * solid_line_offset_ + solid_line_thickness_;
|
||||
pi_.pain.line(change_last_x, y_bar, int(x_), y_bar,
|
||||
change_running.color(), Painter::line_solid, solid_line_thickness_);
|
||||
|
||||
// Change might continue with a different author or type
|
||||
if (change.changed() && !highly_editable_inset) {
|
||||
change_running = change;
|
||||
change_last_x = int(x_);
|
||||
} else
|
||||
change_running.setUnchanged();
|
||||
}
|
||||
|
||||
if (body_pos > 0 && pos == body_pos - 1) {
|
||||
int const lwidth = theFontMetrics(labelFont())
|
||||
.width(layout.labelsep);
|
||||
|
||||
// width_pos is either the width of a space or an inset
|
||||
x_ += row_.label_hfill + lwidth - width_pos;
|
||||
}
|
||||
|
||||
// Is the inline completion in front of character?
|
||||
if (font.isRightToLeft() && vpos == inlineCompletionVPos)
|
||||
paintInlineCompletion(font);
|
||||
|
||||
if (par_.isSeparator(pos)) {
|
||||
Font const orig_font = text_metrics_.displayFont(pit_, pos);
|
||||
double const orig_x = x_;
|
||||
// width_pos is the width of a space
|
||||
double separator_width = width_pos;
|
||||
if (pos >= body_pos)
|
||||
separator_width += row_.separator;
|
||||
paintSeparator(orig_x, separator_width, orig_font.fontInfo());
|
||||
paintForeignMark(orig_x, orig_font.language());
|
||||
++vpos;
|
||||
|
||||
} else if (inset) {
|
||||
case Row::INSET: {
|
||||
// If outer row has changed, nested insets are repaint completely.
|
||||
pi_.base.bv->coordCache().insets().add(inset, int(x_), yo_);
|
||||
|
||||
bool const pi_selected = pi_.selected;
|
||||
Cursor const & cur = pi_.base.bv->cursor();
|
||||
if (cur.selection() && cur.text() == &text_
|
||||
&& cur.normalAnchor().text() == &text_)
|
||||
pi_.selected = row_.sel_beg <= pos && row_.sel_end > pos;
|
||||
paintInset(inset, pos);
|
||||
pi_.selected = pi_selected;
|
||||
++vpos;
|
||||
|
||||
} else {
|
||||
// paint as many characters as possible.
|
||||
paintFromPos(vpos, change_running.changed());
|
||||
pi_.base.bv->coordCache().insets().add(e.inset, int(x_), yo_);
|
||||
paintInset(e.inset, e.font, e.change, e.pos);
|
||||
foreign_descent = e.dim.descent();
|
||||
}
|
||||
break;
|
||||
case Row::SPACE:
|
||||
pi_.pain.textDecoration(e.font.fontInfo(), int(x_), yo_, int(e.full_width()));
|
||||
x_ += e.full_width();
|
||||
}
|
||||
|
||||
// Is the inline completion after character?
|
||||
if (!font.isRightToLeft() && vpos - 1 == inlineCompletionVPos)
|
||||
paintInlineCompletion(font);
|
||||
}
|
||||
// The line that indicates word in a different language
|
||||
paintForeignMark(orig_x, e.font.language(), foreign_descent);
|
||||
|
||||
// if we reach the end of a struck out range, paint it
|
||||
if (change_running.changed()) {
|
||||
FontMetrics const & fm
|
||||
= theFontMetrics(pi_.base.bv->buffer().params().getFont());
|
||||
int const y_bar = change_running.deleted() ?
|
||||
yo_ - fm.maxAscent() / 3 : yo_ + 2 * solid_line_offset_ + solid_line_thickness_;
|
||||
pi_.pain.line(change_last_x, y_bar, int(x_), y_bar,
|
||||
change_running.color(), Painter::line_solid, solid_line_thickness_);
|
||||
change_running.setUnchanged();
|
||||
// change tracking (not for insets that track their own changes)
|
||||
if (e.type != Row::INSET || ! e.inset->canTrackChanges())
|
||||
paintChange(orig_x, e.font, e.change);
|
||||
}
|
||||
}
|
||||
|
||||
@ -983,38 +757,4 @@ void RowPainter::paintSelection() const
|
||||
}
|
||||
|
||||
|
||||
void RowPainter::paintInlineCompletion(Font const & font)
|
||||
{
|
||||
docstring completion = pi_.base.bv->inlineCompletion();
|
||||
FontInfo f = font.fontInfo();
|
||||
bool rtl = font.isRightToLeft();
|
||||
|
||||
// draw the unique and the non-unique completion part
|
||||
// Note: this is not time-critical as it is
|
||||
// only done once per screen.
|
||||
size_t uniqueTo = pi_.base.bv->inlineCompletionUniqueChars();
|
||||
docstring s1 = completion.substr(0, uniqueTo);
|
||||
docstring s2 = completion.substr(uniqueTo);
|
||||
ColorCode c1 = Color_inlinecompletion;
|
||||
ColorCode c2 = Color_nonunique_inlinecompletion;
|
||||
|
||||
// right to left?
|
||||
if (rtl) {
|
||||
swap(s1, s2);
|
||||
swap(c1, c2);
|
||||
}
|
||||
|
||||
if (!s1.empty()) {
|
||||
f.setColor(c1);
|
||||
pi_.pain.text(int(x_), yo_, s1, f);
|
||||
x_ += theFontMetrics(font).width(s1);
|
||||
}
|
||||
|
||||
if (!s2.empty()) {
|
||||
f.setColor(c2);
|
||||
pi_.pain.text(int(x_), yo_, s2, f);
|
||||
x_ += theFontMetrics(font).width(s2);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -14,8 +14,8 @@
|
||||
#ifndef ROWPAINTER_H
|
||||
#define ROWPAINTER_H
|
||||
|
||||
#include "Bidi.h"
|
||||
#include "Changes.h"
|
||||
#include "Row.h"
|
||||
|
||||
#include "support/types.h"
|
||||
|
||||
@ -30,27 +30,11 @@ class PainterInfo;
|
||||
class Paragraph;
|
||||
class ParagraphList;
|
||||
class ParagraphMetrics;
|
||||
class Row;
|
||||
class Text;
|
||||
class TextMetrics;
|
||||
|
||||
namespace frontend { class Painter; }
|
||||
|
||||
/**
|
||||
* FIXME: Re-implement row painting using row elements.
|
||||
*
|
||||
* This is not difficult in principle, but the code is intricate and
|
||||
* needs some careful analysis. The first thing that needs to be done
|
||||
* is to break row elements with the same criteria. Currently breakRow
|
||||
* does not consider on-the-fly spell-checking, but it is not clear to
|
||||
* me that it is required. Moreover, this thing would only work if we
|
||||
* are sure that the Row object is up-to-date when drawing happens.
|
||||
* This depends on the update machinery.
|
||||
*
|
||||
* This would allow to get rid of the Bidi class.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A class used for painting an individual row of text.
|
||||
* FIXME: get rid of that class.
|
||||
@ -74,21 +58,14 @@ public:
|
||||
void paintSelection() const;
|
||||
|
||||
private:
|
||||
void paintSeparator(double orig_x, double width, FontInfo const & font);
|
||||
void paintSeparator(double width, Font const & font);
|
||||
void paintForeignMark(double orig_x, Language const * lang, int desc = 0) const;
|
||||
void paintStringAndSel(docstring const & str, Font const & font,
|
||||
Change const & change,
|
||||
pos_type start_pos, pos_type end_pos);
|
||||
void paintMisspelledMark(double orig_x,
|
||||
docstring const & str, Font const & font,
|
||||
pos_type pos, bool changed) const;
|
||||
void paintStringAndSel(Row::Element const & e);
|
||||
void paintMisspelledMark(double orig_x, Row::Element const & e) const;
|
||||
void paintChange(double orig_x , Font const & font, Change const & change) const;
|
||||
int paintAppendixStart(int y) const;
|
||||
void paintFromPos(pos_type & vpos, bool changed);
|
||||
void paintInset(Inset const * inset, pos_type const pos);
|
||||
void paintInlineCompletion(Font const & font);
|
||||
|
||||
/// return left margin
|
||||
int leftMargin() const;
|
||||
void paintInset(Inset const * inset, Font const & font,
|
||||
Change const & change, pos_type const pos);
|
||||
|
||||
/// return the label font for this row
|
||||
FontInfo labelFont() const;
|
||||
@ -115,9 +92,6 @@ private:
|
||||
Paragraph const & par_;
|
||||
ParagraphMetrics const & pm_;
|
||||
|
||||
/// bidi cache
|
||||
Bidi bidi_;
|
||||
|
||||
/// row changed? (change tracking)
|
||||
Change const change_;
|
||||
|
||||
|
@ -58,28 +58,6 @@ using frontend::FontMetrics;
|
||||
|
||||
namespace {
|
||||
|
||||
int numberOfSeparators(Row const & row)
|
||||
{
|
||||
int n = 0;
|
||||
Row::const_iterator cit = row.begin();
|
||||
Row::const_iterator const end = row.end();
|
||||
for ( ; cit != end ; ++cit)
|
||||
if (cit->type == Row::SEPARATOR)
|
||||
++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void setSeparatorWidth(Row & row, double w)
|
||||
{
|
||||
row.separator = w;
|
||||
Row::iterator it = row.begin();
|
||||
Row::iterator const end = row.end();
|
||||
for ( ; it != end ; ++it)
|
||||
if (it->type == Row::SEPARATOR)
|
||||
it->extra = w;
|
||||
}
|
||||
|
||||
|
||||
int numberOfLabelHfills(Paragraph const & par, Row const & row)
|
||||
{
|
||||
@ -569,12 +547,6 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
|
||||
// FIXME: put back this assertion when the crash on new doc is solved.
|
||||
//LASSERT(w >= 0, /**/);
|
||||
|
||||
bool const is_rtl = text_->isRTL(par);
|
||||
if (is_rtl)
|
||||
row.left_margin = rightMargin(pit);
|
||||
else
|
||||
row.left_margin = leftMargin(max_width_, pit, row.pos());
|
||||
|
||||
// is there a manual margin with a manual label
|
||||
Layout const & layout = par.layout();
|
||||
|
||||
@ -609,15 +581,15 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
|
||||
// set x how you need it
|
||||
switch (getAlign(par, row.pos())) {
|
||||
case LYX_ALIGN_BLOCK: {
|
||||
int const ns = numberOfSeparators(row);
|
||||
int const ns = row.countSeparators();
|
||||
/** If we have separators, and this row has
|
||||
* not be broken abruptly by a display inset
|
||||
* or newline, then stretch it */
|
||||
if (ns && !row.right_boundary()
|
||||
&& row.endpos() != par.size()) {
|
||||
setSeparatorWidth(row, double(w) / ns);
|
||||
row.setSeparatorExtraWidth(double(w) / ns);
|
||||
row.dimension().wid = width;
|
||||
} else if (is_rtl) {
|
||||
} else if (text_->isRTL(par)) {
|
||||
row.dimension().wid = width;
|
||||
row.left_margin += w;
|
||||
}
|
||||
@ -785,13 +757,19 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
|
||||
Paragraph const & par = text_->getPar(pit);
|
||||
pos_type const end = par.size();
|
||||
pos_type const pos = row.pos();
|
||||
int const width = max_width_ - right_margin;
|
||||
pos_type const body_pos = par.beginOfBody();
|
||||
bool const is_rtl = text_->isRTL(par);
|
||||
|
||||
row.clear();
|
||||
// This make get changed in computeRowMetrics depending on RTL
|
||||
row.left_margin = leftMargin(max_width_, pit, pos);
|
||||
row.dimension().wid = row.left_margin;
|
||||
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.dimension().wid = row.left_margin;
|
||||
// the width available for the row.
|
||||
int const width = max_width_ - row.right_margin;
|
||||
|
||||
if (pos >= end || row.width() > width) {
|
||||
row.endpos(end);
|
||||
@ -819,7 +797,7 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
|
||||
// or the end of the par, then build a representation of the row.
|
||||
pos_type i = pos;
|
||||
FontIterator fi = FontIterator(*this, par, pit, pos);
|
||||
while (i < end && row.width() < width) {
|
||||
while (i < end && row.width() <= width) {
|
||||
char_type c = par.getChar(i);
|
||||
// The most special cases are handled first.
|
||||
if (par.isInset(i)) {
|
||||
@ -837,13 +815,6 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
|
||||
int const add = max(fm.width(par.layout().labelsep),
|
||||
labelEnd(pit) - row.width());
|
||||
row.addSpace(i, add, *fi, par.lookupChange(i));
|
||||
} else if (par.isLineSeparator(i)) {
|
||||
// In theory, no inset has this property. If
|
||||
// this is done, a new addSeparator which
|
||||
// takes an inset as parameter should be
|
||||
// added.
|
||||
LATTEST(!par.isInset(i));
|
||||
row.addSeparator(i, c, *fi, par.lookupChange(i));
|
||||
} else if (c == '\t')
|
||||
row.addSpace(i, theFontMetrics(*fi).width(from_ascii(" ")),
|
||||
*fi, par.lookupChange(i));
|
||||
@ -904,7 +875,8 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
|
||||
row.shortenIfNeeded(body_pos, width);
|
||||
|
||||
// make sure that the RTL elements are in reverse ordering
|
||||
row.reverseRTL(text_->isRTL(par));
|
||||
row.reverseRTL(is_rtl);
|
||||
//LYXERR0("breakrow: row is " << row);
|
||||
}
|
||||
|
||||
|
||||
@ -1090,6 +1062,7 @@ void TextMetrics::setRowHeight(Row & row, pit_type const pit,
|
||||
pos_type TextMetrics::getPosNearX(Row const & row, int & x,
|
||||
bool & boundary) const
|
||||
{
|
||||
//LYXERR0("getPosNearX(" << x << ") row=" << row);
|
||||
/// For the main Text, it is possible that this pit is not
|
||||
/// yet in the CoordCache when moving cursor up.
|
||||
/// x Paragraph coordinate is always 0 for main text anyway.
|
||||
@ -1145,6 +1118,7 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x,
|
||||
boundary = true;
|
||||
|
||||
x += xo;
|
||||
//LYXERR0("getPosNearX ==> pos=" << pos << ", boundary=" << boundary);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
@ -84,15 +84,27 @@ public:
|
||||
* return the x offset of a position in the string. The
|
||||
* direction of the string is forced, and the returned value
|
||||
* is from the left edge of the word, not from the start of the string.
|
||||
* \param rtl is true for right-to-left layout
|
||||
* \param ws is the amount of extra inter-word space applied text justication.
|
||||
*/
|
||||
virtual int pos2x(docstring const & s, int pos, bool rtl) const = 0;
|
||||
virtual int pos2x(docstring const & s, int pos, bool rtl, double ws) const = 0;
|
||||
/**
|
||||
* return the position in the string for a given x offset. The
|
||||
* direction of the string is forced, and the returned value
|
||||
* is from the left edge of the word, not from the start of the string.
|
||||
* the offset x is updated to match the closest position in the string.
|
||||
* \param rtl is true for right-to-left layout
|
||||
* \param ws is the amount of extra inter-word space applied text justication.
|
||||
*/
|
||||
virtual int x2pos(docstring const & s, int & x, bool rtl) const = 0;
|
||||
virtual int x2pos(docstring const & s, int & x, bool rtl, double ws) const = 0;
|
||||
/**
|
||||
* Break string at width at most x.
|
||||
* \return true if successful
|
||||
* \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;
|
||||
/// return char dimension for the font.
|
||||
virtual Dimension const dimension(char_type c) const = 0;
|
||||
/**
|
||||
|
@ -120,13 +120,15 @@ public:
|
||||
* text direction is given by \c rtl.
|
||||
* \return the width of the drawn text.
|
||||
*/
|
||||
virtual int text(int x, int y, docstring const & str, FontInfo const & f, bool rtl = false) = 0;
|
||||
virtual int text(int x, int y, docstring const & str, FontInfo const & f,
|
||||
bool rtl = false, double wordspacing = 0.0) = 0;
|
||||
|
||||
/** draw a string at position x, y (y is the baseline). The
|
||||
* text direction is enforced by the \c Font.
|
||||
* \return the width of the drawn text.
|
||||
*/
|
||||
virtual int text(int x, int y, docstring const & str, Font const & f) = 0;
|
||||
virtual int text(int x, int y, docstring const & str, Font const & f,
|
||||
double wordspacing = 0.0) = 0;
|
||||
|
||||
/** draw a string at position x, y (y is the baseline), but
|
||||
* make sure that the part between \c from and \c to is in
|
||||
@ -134,7 +136,8 @@ public:
|
||||
* \return the width of the drawn text.
|
||||
*/
|
||||
virtual int text(int x, int y, docstring const & str, Font const & f,
|
||||
Color other, size_type from, size_type to) = 0;
|
||||
Color other, size_type from, size_type to,
|
||||
double const wordspacing) = 0;
|
||||
|
||||
void setDrawingEnabled(bool drawing_enabled)
|
||||
{ drawing_enabled_ = drawing_enabled; }
|
||||
|
@ -150,10 +150,11 @@ int GuiFontMetrics::signedWidth(docstring const & s) const
|
||||
}
|
||||
|
||||
namespace {
|
||||
void setTextLayout(QTextLayout & tl, docstring const & s, QFont const & font,
|
||||
bool const rtl)
|
||||
void setTextLayout(QTextLayout & tl, docstring const & s, QFont font,
|
||||
bool const rtl, double const wordspacing)
|
||||
{
|
||||
tl.setText(toqstr(s));
|
||||
font.setWordSpacing(wordspacing);
|
||||
tl.setFont(font);
|
||||
// Note that both setFlags and the enums are undocumented
|
||||
tl.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
|
||||
@ -164,18 +165,22 @@ void setTextLayout(QTextLayout & tl, docstring const & s, QFont const & font,
|
||||
}
|
||||
|
||||
|
||||
int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl) const
|
||||
int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl,
|
||||
double const wordspacing) const
|
||||
{
|
||||
QTextLayout tl;
|
||||
setTextLayout(tl, s, font_, rtl);
|
||||
QFont copy = font_;
|
||||
copy.setWordSpacing(wordspacing);
|
||||
setTextLayout(tl, s, font_, rtl, wordspacing);
|
||||
return static_cast<int>(tl.lineForTextPosition(pos).cursorToX(pos));
|
||||
}
|
||||
|
||||
|
||||
int GuiFontMetrics::x2pos(docstring const & s, int & x, bool const rtl) const
|
||||
int GuiFontMetrics::x2pos(docstring const & s, int & x, bool const rtl,
|
||||
double const wordspacing) const
|
||||
{
|
||||
QTextLayout tl;
|
||||
setTextLayout(tl, s, font_, rtl);
|
||||
setTextLayout(tl, s, font_, rtl, wordspacing);
|
||||
int pos = tl.lineForTextPosition(0).xToCursor(x);
|
||||
// correct x value to the actual cursor position.
|
||||
x = static_cast<int>(tl.lineForTextPosition(0).cursorToX(pos));
|
||||
@ -183,6 +188,31 @@ int GuiFontMetrics::x2pos(docstring const & s, int & x, bool const rtl) const
|
||||
}
|
||||
|
||||
|
||||
bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const force) const
|
||||
{
|
||||
if (s.empty())
|
||||
return false;
|
||||
QTextLayout tl;
|
||||
tl.setText(toqstr(s));
|
||||
tl.setFont(font_);
|
||||
// Note that both setFlags and the enums are undocumented
|
||||
tl.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
|
||||
QTextOption to;
|
||||
to.setWrapMode(force ? QTextOption::WrapAnywhere : QTextOption::WordWrap);
|
||||
tl.setTextOption(to);
|
||||
tl.beginLayout();
|
||||
QTextLine line = tl.createLine();
|
||||
line.setLineWidth(x);
|
||||
tl.createLine();
|
||||
tl.endLayout();
|
||||
if (int(line.naturalTextWidth()) > x)
|
||||
return false;
|
||||
x = int(line.naturalTextWidth());
|
||||
s = s.substr(0, line.textLength());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void GuiFontMetrics::rectText(docstring const & str,
|
||||
int & w, int & ascent, int & descent) const
|
||||
{
|
||||
|
@ -43,8 +43,9 @@ public:
|
||||
virtual int rbearing(char_type c) const;
|
||||
virtual int width(docstring const & s) const;
|
||||
virtual int signedWidth(docstring const & s) const;
|
||||
virtual int pos2x(docstring const & s, int pos, bool rtl) const;
|
||||
virtual int x2pos(docstring const & s, int & x, bool rtl) const;
|
||||
virtual int pos2x(docstring const & s, int pos, bool rtl, double ws) const;
|
||||
virtual int x2pos(docstring const & s, int & x, bool rtl, double ws) const;
|
||||
virtual bool breakAt(docstring & s, int & x, bool rtl, bool force) const;
|
||||
virtual Dimension const dimension(char_type c) const;
|
||||
|
||||
virtual void rectText(docstring const & str,
|
||||
|
@ -290,7 +290,8 @@ int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
|
||||
|
||||
|
||||
int GuiPainter::text(int x, int y, docstring const & s,
|
||||
FontInfo const & f, bool const rtl)
|
||||
FontInfo const & f, bool const rtl,
|
||||
double const wordspacing)
|
||||
{
|
||||
//LYXERR0("text: x=" << x << ", s=" << s);
|
||||
if (s.empty())
|
||||
@ -316,7 +317,8 @@ int GuiPainter::text(int x, int y, docstring const & s,
|
||||
str = ' ' + str;
|
||||
#endif
|
||||
|
||||
QFont const & ff = getFont(f);
|
||||
QFont ff = getFont(f);
|
||||
ff.setWordSpacing(wordspacing);
|
||||
GuiFontMetrics const & fm = getFontMetrics(f);
|
||||
|
||||
// Here we use the font width cache instead of
|
||||
@ -418,14 +420,16 @@ int GuiPainter::text(int x, int y, docstring const & s,
|
||||
}
|
||||
|
||||
|
||||
int GuiPainter::text(int x, int y, docstring const & str, Font const & f)
|
||||
int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
|
||||
double const wordspacing)
|
||||
{
|
||||
return text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft());
|
||||
return text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft(), wordspacing);
|
||||
}
|
||||
|
||||
|
||||
int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
|
||||
Color other, size_type from, size_type to)
|
||||
Color other, size_type const from, size_type const to,
|
||||
double const wordspacing)
|
||||
{
|
||||
GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
|
||||
FontInfo fi = f.fontInfo();
|
||||
@ -434,8 +438,8 @@ int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
|
||||
// dimensions
|
||||
int const ascent = fm.maxAscent();
|
||||
int const height = fm.maxAscent() + fm.maxDescent();
|
||||
int xmin = fm.pos2x(str, from, rtl);
|
||||
int xmax = fm.pos2x(str, to, rtl);
|
||||
int xmin = fm.pos2x(str, from, rtl, wordspacing);
|
||||
int xmax = fm.pos2x(str, to, rtl, wordspacing);
|
||||
if (xmin > xmax)
|
||||
swap(xmin, xmax);
|
||||
|
||||
@ -444,7 +448,7 @@ int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
|
||||
fi.setPaintColor(other);
|
||||
QRegion const clip(x + xmin, y - ascent, xmax - xmin, height);
|
||||
setClipRegion(clip);
|
||||
int const textwidth = text(x, y, str, fi, rtl);
|
||||
int const textwidth = text(x, y, str, fi, rtl, wordspacing);
|
||||
|
||||
// Then the part in normal color
|
||||
// Note that in Qt5, it is not possible to use Qt::UniteClip,
|
||||
@ -452,7 +456,7 @@ int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
|
||||
fi.setPaintColor(orig);
|
||||
QRegion region(viewport());
|
||||
setClipRegion(region - clip);
|
||||
text(x, y, str, fi, rtl);
|
||||
text(x, y, str, fi, rtl, wordspacing);
|
||||
setClipping(false);
|
||||
|
||||
return textwidth;
|
||||
|
@ -91,13 +91,15 @@ public:
|
||||
* text direction is given by \c rtl.
|
||||
* \return the width of the drawn text.
|
||||
*/
|
||||
virtual int text(int x, int y, docstring const & str, FontInfo const & f, bool rtl = false);
|
||||
virtual int text(int x, int y, docstring const & str, FontInfo const & f,
|
||||
bool rtl = false, double wordspacing = 0.0);
|
||||
|
||||
/** draw a string at position x, y (y is the baseline). The
|
||||
* text direction is enforced by the \c Font.
|
||||
* \return the width of the drawn text.
|
||||
*/
|
||||
virtual int text(int x, int y, docstring const & str, Font const & f);
|
||||
virtual int text(int x, int y, docstring const & str, Font const & f,
|
||||
double wordspacing = 0.0);
|
||||
|
||||
/** draw a string at position x, y (y is the baseline), but
|
||||
* make sure that the part between \c from and \c to is in
|
||||
@ -105,7 +107,8 @@ public:
|
||||
* \return the width of the drawn text.
|
||||
*/
|
||||
virtual int text(int x, int y, docstring const & str, Font const & f,
|
||||
Color other, size_type from, size_type to);
|
||||
Color other, size_type from, size_type to,
|
||||
double const wordspacing);
|
||||
|
||||
/// draw a char at position x, y (y is the baseline)
|
||||
virtual int text(int x, int y, char_type c, FontInfo const & f);
|
||||
|
Loading…
Reference in New Issue
Block a user