Merge remote-tracking branch 'features/str-metrics'

This branch implements string-wise metrics computation. The goal is to
have both good metrics computation (and font with proper kerning and
ligatures) and better performance than what we have with
force_paint_single_char. Moreover there has been some code
factorization in TextMetrics, where the same row-breaking algorithm
was basically implemented 3 times.

Globally, the new code is a bit shorter than the existing one, and it
is much cleaner.  There is still a lot of potential for code removal,
especially in the RowPainter, which should be rewritten to use the new
Row information.

The bugs fixed and caused by this branch are tracked at ticket #9003:
http://www.lyx.org/trac/ticket/9003

What is done:

* Make TextMetrics methods operate on Row objects: breakRow and
  setRowHeight instead of rowBreakPoint and rowHeight.

* Change breakRow operation to operate at strings level to compute
  metrics The list of elements is stored in the row object in visual
  ordering, not logical. This will eventually allow to get rid of the
  Bidi class.

* rename getColumnNearX to getPosNearX (and change code accordingly).
  It does not make sense to return a position relative to the start of
  row, since nobody needs this.

* Re-implement cursorX and getPosNearX using row elements.

* Get rid of lyxrc.force_paint_single_char. This was a workaround that
  is not necessary anymore.

* Implement proper string metrics computation (with cache). Remove
  useless workarounds which disable kerning and ligatures.

* Draw also RtL text string-wise. This speeds-up drawing.

* Do not cut strings at selection boundary in RowPainter. This avoids
  ligature/kerning breaking in latin text, and bad rendering problems
  in Arabic.

* Remove homebrew Arabic and Hebrew support from Encoding.cpp. We now
  rely on Qt to do handle complex scripts.

* Get rid of LyXRC::rtl_support, which does not have a real use case.

* Fix display of [] and {} delimiters in Arabic scripts.
This commit is contained in:
Jean-Marc 2014-07-25 20:10:55 +02:00
commit 41740ea915
37 changed files with 1256 additions and 1403 deletions

View File

@ -1479,7 +1479,7 @@ if __name__ == '__main__':
lyx_check_config = True lyx_check_config = True
lyx_kpsewhich = True lyx_kpsewhich = True
outfile = 'lyxrc.defaults' outfile = 'lyxrc.defaults'
lyxrc_fileformat = 15 lyxrc_fileformat = 17
rc_entries = '' rc_entries = ''
lyx_keep_temps = False lyx_keep_temps = False
version_suffix = '' version_suffix = ''

View File

@ -63,6 +63,16 @@
# New RC default_otf_view_format # New RC default_otf_view_format
# No conversion necessary. # No conversion necessary.
# Incremented to format 15, by prannoy
# Add fullscreen_statusbar
# No conversion necessary.
# Incremented to format 16, by lasgouttes
# Remove force_paint_single_char rc.
# Incremented to format 17, by lasgouttes
# Remove rtl_support rc.
# NOTE: The format should also be updated in LYXRC.cpp and # NOTE: The format should also be updated in LYXRC.cpp and
# in configure.py. # in configure.py.
@ -306,6 +316,22 @@ def mac_cursor_movement(line):
#################################### ####################################
#################################
# Conversions from LyX 2.1 to 2.2
def remove_force_paint_single_char(line):
if not line.lower().startswith("\\force_paint_single_char"):
return no_match
return (True, "")
def remove_rtl(line):
if not line.lower().startswith("\\rtl "):
return no_match
return (True, "")
# End conversions for LyX 2.1 to 2.2
####################################
conversions = [ conversions = [
[ 1, [ # there were several conversions for format 1 [ 1, [ # there were several conversions for format 1
export_menu, export_menu,
@ -327,5 +353,7 @@ conversions = [
[ 12, []], [ 12, []],
[ 13, [mac_cursor_movement]], [ 13, [mac_cursor_movement]],
[ 14, []], [ 14, []],
[ 15, []] [ 15, []],
[ 16, [remove_force_paint_single_char]],
[ 17, [remove_rtl]]
] ]

View File

@ -57,10 +57,6 @@ void Bidi::computeTables(Paragraph const & par,
Buffer const & buf, Row const & row) Buffer const & buf, Row const & row)
{ {
same_direction_ = true; same_direction_ = true;
if (!lyxrc.rtl_support) {
start_ = -1;
return;
}
if (par.inInset().forceLTR()) { if (par.inInset().forceLTR()) {
start_ = -1; start_ = -1;
@ -192,7 +188,7 @@ void Bidi::computeTables(Paragraph const & par,
bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par, bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par,
pos_type pos) const pos_type pos) const
{ {
if (!lyxrc.rtl_support || pos == 0) if (pos == 0)
return false; return false;
if (!inRange(pos - 1)) { if (!inRange(pos - 1)) {
@ -212,9 +208,6 @@ bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par,
bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par, bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par,
pos_type pos, Font const & font) const pos_type pos, Font const & font) const
{ {
if (!lyxrc.rtl_support)
return false; // This is just for speedup
bool const rtl = font.isVisibleRightToLeft(); bool const rtl = font.isVisibleRightToLeft();
bool const rtl2 = inRange(pos) bool const rtl2 = inRange(pos)
? level(pos) % 2 ? level(pos) % 2

View File

@ -803,7 +803,6 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
void BufferView::translateAndInsert(char_type c, Text * t, Cursor & cur) void BufferView::translateAndInsert(char_type c, Text * t, Cursor & cur)
{ {
if (lyxrc.rtl_support) {
if (d->cursor_.real_current_font.isRightToLeft()) { if (d->cursor_.real_current_font.isRightToLeft()) {
if (d->intl_.keymap == Intl::PRIMARY) if (d->intl_.keymap == Intl::PRIMARY)
d->intl_.keyMapSec(); d->intl_.keyMapSec();
@ -811,7 +810,6 @@ void BufferView::translateAndInsert(char_type c, Text * t, Cursor & cur)
if (d->intl_.keymap == Intl::SECONDARY) if (d->intl_.keymap == Intl::SECONDARY)
d->intl_.keyMapPrim(); d->intl_.keyMapPrim();
} }
}
d->intl_.getTransManager().translateAndInsert(c, t, cur); d->intl_.getTransManager().translateAndInsert(c, t, cur);
} }

View File

@ -2090,9 +2090,7 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded)
Row const & real_next_row = tm.parMetrics(pit()).rows()[next_row]; Row const & real_next_row = tm.parMetrics(pit()).rows()[next_row];
bool bound = false; bool bound = false;
pos_type const col = tm.getColumnNearX(pit(), real_next_row, top().pos() = tm.getPosNearX(real_next_row, xo, bound);
xo, bound);
top().pos() = real_next_row.pos() + col;
boundary(bound); boundary(bound);
updateNeeded |= bv().checkDepm(*this, old); updateNeeded |= bv().checkDepm(*this, old);

View File

@ -42,190 +42,6 @@ Encodings::MathSymbolSet Encodings::mathsym;
namespace { namespace {
char_type arabic_table[172][4] = {
{0xfe80, 0xfe80, 0xfe80, 0xfe80}, // 0x0621 = hamza
{0xfe81, 0xfe82, 0xfe81, 0xfe82}, // 0x0622 = ligature madda on alef
{0xfe83, 0xfe84, 0xfe83, 0xfe84}, // 0x0623 = ligature hamza on alef
{0xfe85, 0xfe86, 0xfe85, 0xfe86}, // 0x0624 = ligature hamza on waw
{0xfe87, 0xfe88, 0xfe87, 0xfe88}, // 0x0625 = ligature hamza under alef
{0xfe89, 0xfe8a, 0xfe8b, 0xfe8c}, // 0x0626 = ligature hamza on ya
{0xfe8d, 0xfe8e, 0xfe8d, 0xfe8e}, // 0x0627 = alef
{0xfe8f, 0xfe90, 0xfe91, 0xfe92}, // 0x0628 = baa
{0xfe93, 0xfe94, 0xfe93, 0xfe94}, // 0x0629 = taa marbuta
{0xfe95, 0xfe96, 0xfe97, 0xfe98}, // 0x062a = taa
{0xfe99, 0xfe9a, 0xfe9b, 0xfe9c}, // 0x062b = thaa
{0xfe9d, 0xfe9e, 0xfe9f, 0xfea0}, // 0x062c = jeem
{0xfea1, 0xfea2, 0xfea3, 0xfea4}, // 0x062d = haa
{0xfea5, 0xfea6, 0xfea7, 0xfea8}, // 0x062e = khaa
{0xfea9, 0xfeaa, 0xfea9, 0xfeaa}, // 0x062f = dal
{0xfeab, 0xfeac, 0xfeab, 0xfeac}, // 0x0630 = thal
{0xfead, 0xfeae, 0xfead, 0xfeae}, // 0x0631 = ra
{0xfeaf, 0xfeb0, 0xfeaf, 0xfeb0}, // 0x0632 = zain
{0xfeb1, 0xfeb2, 0xfeb3, 0xfeb4}, // 0x0633 = seen
{0xfeb5, 0xfeb6, 0xfeb7, 0xfeb8}, // 0x0634 = sheen
{0xfeb9, 0xfeba, 0xfebb, 0xfebc}, // 0x0635 = sad
{0xfebd, 0xfebe, 0xfebf, 0xfec0}, // 0x0636 = dad
{0xfec1, 0xfec2, 0xfec3, 0xfec4}, // 0x0637 = tah
{0xfec5, 0xfec6, 0xfec7, 0xfec8}, // 0x0638 = zah
{0xfec9, 0xfeca, 0xfecb, 0xfecc}, // 0x0639 = ain
{0xfecd, 0xfece, 0xfecf, 0xfed0}, // 0x063a = ghain
{0, 0, 0, 0}, // 0x063b
{0, 0, 0, 0}, // 0x063c
{0, 0, 0, 0}, // 0x063d
{0, 0, 0, 0}, // 0x063e
{0, 0, 0, 0}, // 0x063f
{0, 0, 0, 0}, // 0x0640
{0xfed1, 0xfed2, 0xfed3, 0xfed4}, // 0x0641 = fa
{0xfed5, 0xfed6, 0xfed7, 0xfed8}, // 0x0642 = qaf
{0xfed9, 0xfeda, 0xfedb, 0xfedc}, // 0x0643 = kaf
{0xfedd, 0xfede, 0xfedf, 0xfee0}, // 0x0644 = lam
{0xfee1, 0xfee2, 0xfee3, 0xfee4}, // 0x0645 = meem
{0xfee5, 0xfee6, 0xfee7, 0xfee8}, // 0x0646 = noon
{0xfee9, 0xfeea, 0xfeeb, 0xfeec}, // 0x0647 = ha
{0xfeed, 0xfeee, 0xfeed, 0xfeee}, // 0x0648 = waw
{0xfeef, 0xfef0, 0xfeef, 0xfef0}, // 0x0649 = alef maksura
{0xfef1, 0xfef2, 0xfef3, 0xfef4}, // 0x064a = ya
{0x065b, 0x065b, 0x065b, 0x065b}, // 0x064b = fathatan
{0x065c, 0x065c, 0x065c, 0x065c}, // 0x064c = dammatan
{0x064d, 0x064d, 0x064d, 0x064d}, // 0x064d = kasratan
{0x064e, 0x064e, 0x064e, 0x064e}, // 0x064e = fatha
{0x064f, 0x064f, 0x064f, 0x064f}, // 0x064f = damma
{0x0650, 0x0650, 0x0650, 0x0650}, // 0x0650 = kasra
{0x0651, 0x0651, 0x0651, 0x0651}, // 0x0651 = shadda
{0x0652, 0x0652, 0x0652, 0x0652}, // 0x0652 = sukun
{0, 0, 0, 0}, // 0x0653
{0, 0, 0, 0}, // 0x0654
{0, 0, 0, 0}, // 0x0655
{0, 0, 0, 0}, // 0x0656
{0, 0, 0, 0}, // 0x0657
{0, 0, 0, 0}, // 0x0658
{0, 0, 0, 0}, // 0x0659
{0, 0, 0, 0}, // 0x065a
{0, 0, 0, 0}, // 0x065b
{0, 0, 0, 0}, // 0x065c
{0, 0, 0, 0}, // 0x065d
{0, 0, 0, 0}, // 0x065e
{0, 0, 0, 0}, // 0x065f
{0, 0, 0, 0}, // 0x0660
{0, 0, 0, 0}, // 0x0661
{0, 0, 0, 0}, // 0x0662
{0, 0, 0, 0}, // 0x0663
{0, 0, 0, 0}, // 0x0664
{0, 0, 0, 0}, // 0x0665
{0, 0, 0, 0}, // 0x0666
{0, 0, 0, 0}, // 0x0667
{0, 0, 0, 0}, // 0x0668
{0, 0, 0, 0}, // 0x0669
{0, 0, 0, 0}, // 0x066a
{0, 0, 0, 0}, // 0x066b
{0, 0, 0, 0}, // 0x066c
{0, 0, 0, 0}, // 0x066d
{0, 0, 0, 0}, // 0x066e
{0, 0, 0, 0}, // 0x066f
{0, 0, 0, 0}, // 0x0670
{0, 0, 0, 0}, // 0x0671
{0, 0, 0, 0}, // 0x0672
{0, 0, 0, 0}, // 0x0673
{0, 0, 0, 0}, // 0x0674
{0, 0, 0, 0}, // 0x0675
{0, 0, 0, 0}, // 0x0676
{0, 0, 0, 0}, // 0x0677
{0, 0, 0, 0}, // 0x0678
{0, 0, 0, 0}, // 0x0679
{0, 0, 0, 0}, // 0x067a
{0, 0, 0, 0}, // 0x067b
{0, 0, 0, 0}, // 0x067c
{0, 0, 0, 0}, // 0x067d
{0xfb56, 0xfb57, 0xfb58, 0xfb59}, // 0x067e = peh
{0, 0, 0, 0}, // 0x067f
{0, 0, 0, 0}, // 0x0680
{0, 0, 0, 0}, // 0x0681
{0, 0, 0, 0}, // 0x0682
{0, 0, 0, 0}, // 0x0683
{0, 0, 0, 0}, // 0x0684
{0, 0, 0, 0}, // 0x0685
{0xfb7a, 0xfb7b, 0xfb7c, 0xfb7d}, // 0x0686 = tcheh
{0, 0, 0, 0}, // 0x0687
{0, 0, 0, 0}, // 0x0688
{0, 0, 0, 0}, // 0x0689
{0, 0, 0, 0}, // 0x068a
{0, 0, 0, 0}, // 0x068b
{0, 0, 0, 0}, // 0x068c
{0, 0, 0, 0}, // 0x068d
{0, 0, 0, 0}, // 0x068e
{0, 0, 0, 0}, // 0x068f
{0, 0, 0, 0}, // 0x0690
{0, 0, 0, 0}, // 0x0691
{0, 0, 0, 0}, // 0x0692
{0, 0, 0, 0}, // 0x0693
{0, 0, 0, 0}, // 0x0694
{0, 0, 0, 0}, // 0x0695
{0, 0, 0, 0}, // 0x0696
{0, 0, 0, 0}, // 0x0697
{0xfb8a, 0xfb8b, 0xfb8a, 0xfb8b}, // 0x0698 = jeh
{0, 0, 0, 0}, // 0x0699
{0, 0, 0, 0}, // 0x069a
{0, 0, 0, 0}, // 0x069b
{0, 0, 0, 0}, // 0x069c
{0, 0, 0, 0}, // 0x069d
{0, 0, 0, 0}, // 0x069e
{0, 0, 0, 0}, // 0x069f
{0, 0, 0, 0}, // 0x06a0
{0, 0, 0, 0}, // 0x06a1
{0, 0, 0, 0}, // 0x06a2
{0, 0, 0, 0}, // 0x06a3
{0, 0, 0, 0}, // 0x06a4
{0, 0, 0, 0}, // 0x06a5
{0, 0, 0, 0}, // 0x06a6
{0, 0, 0, 0}, // 0x06a7
{0, 0, 0, 0}, // 0x06a8
{0xfb8e, 0xfb8f, 0xfb90, 0xfb91}, // 0x06a9 = farsi kaf
{0, 0, 0, 0}, // 0x06aa
{0, 0, 0, 0}, // 0x06ab
{0, 0, 0, 0}, // 0x06ac
{0, 0, 0, 0}, // 0x06ad
{0, 0, 0, 0}, // 0x06ae
{0xfb92, 0xfb93, 0xfb94, 0xfb95}, // 0x06af = gaf
{0, 0, 0, 0}, // 0x06b0
{0, 0, 0, 0}, // 0x06b1
{0, 0, 0, 0}, // 0x06b2
{0, 0, 0, 0}, // 0x06b3
{0, 0, 0, 0}, // 0x06b4
{0, 0, 0, 0}, // 0x06b5
{0, 0, 0, 0}, // 0x06b6
{0, 0, 0, 0}, // 0x06b7
{0, 0, 0, 0}, // 0x06b8
{0, 0, 0, 0}, // 0x06b9
{0, 0, 0, 0}, // 0x06ba
{0, 0, 0, 0}, // 0x06bb
{0, 0, 0, 0}, // 0x06bc
{0, 0, 0, 0}, // 0x06bd
{0, 0, 0, 0}, // 0x06be
{0, 0, 0, 0}, // 0x06bf
{0, 0, 0, 0}, // 0x06c0
{0, 0, 0, 0}, // 0x06c1
{0, 0, 0, 0}, // 0x06c2
{0, 0, 0, 0}, // 0x06c3
{0, 0, 0, 0}, // 0x06c4
{0, 0, 0, 0}, // 0x06c5
{0, 0, 0, 0}, // 0x06c6
{0, 0, 0, 0}, // 0x06c7
{0, 0, 0, 0}, // 0x06c8
{0, 0, 0, 0}, // 0x06c9
{0, 0, 0, 0}, // 0x06ca
{0, 0, 0, 0}, // 0x06cb
{0xfbfc, 0xfbfd, 0xfbfe, 0xfbff} // 0x06cc = farsi yeh
};
char_type const arabic_start = 0x0621;
char_type const arabic_end = 0x06cc;
typedef map<char_type, CharInfo> CharInfoMap; typedef map<char_type, CharInfo> CharInfoMap;
CharInfoMap unicodesymbols; CharInfoMap unicodesymbols;
@ -711,37 +527,6 @@ docstring Encodings::fromLaTeXCommand(docstring const & cmd, int cmdtype,
} }
bool Encodings::isHebrewComposeChar(char_type c)
{
return c <= 0x05c2 && c >= 0x05b0 && c != 0x05be && c != 0x05c0;
}
// Special Arabic letters are ones that do not get connected from left
// they are hamza, alef_madda, alef_hamza, waw_hamza, alef_hamza_under,
// alef, tah_marbota, dal, thal, rah, zai, wow, alef_maksoura
bool Encodings::isArabicSpecialChar(char_type c)
{
return (c >= 0x0621 && c <= 0x0625) || (c >= 0x0630 && c <= 0x0632)
|| c == 0x0627 || c == 0x0629 || c == 0x062f || c == 0x0648
|| c == 0x0649 || c == 0x0698;
}
bool Encodings::isArabicComposeChar(char_type c)
{
return c >= 0x064b && c <= 0x0652;
}
bool Encodings::isArabicChar(char_type c)
{
return c >= arabic_start && c <= arabic_end
&& arabic_table[c-arabic_start][0];
}
CharInfo const & Encodings::unicodeCharInfo(char_type c) CharInfo const & Encodings::unicodeCharInfo(char_type c)
{ {
static CharInfo empty; static CharInfo empty;
@ -750,12 +535,6 @@ CharInfo const & Encodings::unicodeCharInfo(char_type c)
} }
char_type Encodings::transformChar(char_type c, Encodings::LetterForm form)
{
return isArabicChar(c) ? arabic_table[c-arabic_start][form] : c;
}
bool Encodings::isCombiningChar(char_type c) bool Encodings::isCombiningChar(char_type c)
{ {
CharInfoMap::const_iterator const it = unicodesymbols.find(c); CharInfoMap::const_iterator const it = unicodesymbols.find(c);

View File

@ -255,29 +255,8 @@ public:
/// ///
const_iterator end() const { return encodinglist.end(); } const_iterator end() const { return encodinglist.end(); }
///
enum LetterForm {
///
FORM_ISOLATED,
///
FORM_FINAL,
///
FORM_INITIAL,
///
FORM_MEDIAL
};
///
static bool isHebrewComposeChar(char_type c);
///
static bool isArabicComposeChar(char_type c);
///
static bool isArabicSpecialChar(char_type c);
///
static bool isArabicChar(char_type c);
/// Accessor for the unicode information table. /// Accessor for the unicode information table.
static CharInfo const & unicodeCharInfo(char_type c); static CharInfo const & unicodeCharInfo(char_type c);
///
static char_type transformChar(char_type c, LetterForm form);
/// Is this a combining char? /// Is this a combining char?
static bool isCombiningChar(char_type c); static bool isCombiningChar(char_type c);
/// Return the TIPA shortcut /// Return the TIPA shortcut

View File

@ -58,7 +58,7 @@ namespace {
// The format should also be updated in configure.py, and conversion code // The format should also be updated in configure.py, and conversion code
// should be added to prefs2prefs_prefs.py. // should be added to prefs2prefs_prefs.py.
static unsigned int const LYXRC_FILEFORMAT = 15; // prannoy: statusbar on/off in full screen static unsigned int const LYXRC_FILEFORMAT = 17; // lasgouttes: remove \\rtl
// when adding something to this array keep it sorted! // when adding something to this array keep it sorted!
LexerKeyword lyxrcTags[] = { LexerKeyword lyxrcTags[] = {
@ -106,7 +106,6 @@ LexerKeyword lyxrcTags[] = {
{ "\\example_path", LyXRC::RC_EXAMPLEPATH }, { "\\example_path", LyXRC::RC_EXAMPLEPATH },
{ "\\export_overwrite", LyXRC::RC_EXPORT_OVERWRITE }, { "\\export_overwrite", LyXRC::RC_EXPORT_OVERWRITE },
{ "\\font_encoding", LyXRC::RC_FONT_ENCODING }, { "\\font_encoding", LyXRC::RC_FONT_ENCODING },
{ "\\force_paint_single_char", LyXRC::RC_FORCE_PAINT_SINGLE_CHAR },
{ "\\format", LyXRC::RC_FILEFORMAT }, { "\\format", LyXRC::RC_FILEFORMAT },
{ "\\forward_search_dvi", LyXRC::RC_FORWARD_SEARCH_DVI }, { "\\forward_search_dvi", LyXRC::RC_FORWARD_SEARCH_DVI },
{ "\\forward_search_pdf", LyXRC::RC_FORWARD_SEARCH_PDF }, { "\\forward_search_pdf", LyXRC::RC_FORWARD_SEARCH_PDF },
@ -171,7 +170,6 @@ LexerKeyword lyxrcTags[] = {
{ "\\print_to_file", LyXRC::RC_PRINTTOFILE }, { "\\print_to_file", LyXRC::RC_PRINTTOFILE },
{ "\\print_to_printer", LyXRC::RC_PRINTTOPRINTER }, { "\\print_to_printer", LyXRC::RC_PRINTTOPRINTER },
{ "\\printer", LyXRC::RC_PRINTER }, { "\\printer", LyXRC::RC_PRINTER },
{ "\\rtl", LyXRC::RC_RTL_SUPPORT },
{ "\\save_compressed", LyXRC::RC_SAVE_COMPRESSED }, { "\\save_compressed", LyXRC::RC_SAVE_COMPRESSED },
{ "\\screen_dpi", LyXRC::RC_SCREEN_DPI }, { "\\screen_dpi", LyXRC::RC_SCREEN_DPI },
{ "\\screen_font_roman", LyXRC::RC_SCREEN_FONT_ROMAN }, { "\\screen_font_roman", LyXRC::RC_SCREEN_FONT_ROMAN },
@ -314,7 +312,6 @@ void LyXRC::setDefaults()
completion_minlength = 6; completion_minlength = 6;
spellcheck_notes = true; spellcheck_notes = true;
use_kbmap = false; use_kbmap = false;
rtl_support = true;
visual_cursor = false; visual_cursor = false;
auto_number = true; auto_number = true;
mark_foreign_language = true; mark_foreign_language = true;
@ -447,9 +444,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
if (!lexrc.isOK()) if (!lexrc.isOK())
return ReadError; return ReadError;
// default for current rowpainter capabilities
force_paint_single_char = true;
// format prior to 2.0 and introduction of format tag // format prior to 2.0 and introduction of format tag
unsigned int format = 0; unsigned int format = 0;
@ -555,10 +549,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
lexrc >> fontenc; lexrc >> fontenc;
break; break;
case RC_FORCE_PAINT_SINGLE_CHAR:
lexrc >> force_paint_single_char;
break;
case RC_PRINTER: case RC_PRINTER:
lexrc >> printer; lexrc >> printer;
break; break;
@ -1050,9 +1040,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
case RC_LANGUAGE_COMMAND_LOCAL: case RC_LANGUAGE_COMMAND_LOCAL:
lexrc >> language_command_local; lexrc >> language_command_local;
break; break;
case RC_RTL_SUPPORT:
lexrc >> rtl_support;
break;
case RC_VISUAL_CURSOR: case RC_VISUAL_CURSOR:
lexrc >> visual_cursor; lexrc >> visual_cursor;
break; break;
@ -2221,14 +2208,6 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
if (tag != RC_LAST) if (tag != RC_LAST)
break; break;
case RC_FORCE_PAINT_SINGLE_CHAR:
if (ignore_system_lyxrc ||
force_paint_single_char != system_lyxrc.force_paint_single_char) {
os << "\\force_paint_single_char \"" << force_paint_single_char << "\"\n";
}
if (tag != RC_LAST)
break;
os << "\n#\n" os << "\n#\n"
<< "# FILE SECTION ######################################\n" << "# FILE SECTION ######################################\n"
<< "#\n\n"; << "#\n\n";
@ -2553,13 +2532,6 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
if (tag != RC_LAST) if (tag != RC_LAST)
break; break;
case RC_RTL_SUPPORT:
if (ignore_system_lyxrc ||
rtl_support != system_lyxrc.rtl_support) {
os << "\\rtl " << convert<string>(rtl_support) << '\n';
}
if (tag != RC_LAST)
break;
case RC_VISUAL_CURSOR: case RC_VISUAL_CURSOR:
if (ignore_system_lyxrc || if (ignore_system_lyxrc ||
visual_cursor != system_lyxrc.visual_cursor) { visual_cursor != system_lyxrc.visual_cursor) {
@ -2974,7 +2946,6 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
case LyXRC::RC_ESC_CHARS: case LyXRC::RC_ESC_CHARS:
case LyXRC::RC_EXAMPLEPATH: case LyXRC::RC_EXAMPLEPATH:
case LyXRC::RC_FONT_ENCODING: case LyXRC::RC_FONT_ENCODING:
case LyXRC::RC_FORCE_PAINT_SINGLE_CHAR:
case LyXRC::RC_FILEFORMAT: case LyXRC::RC_FILEFORMAT:
case LyXRC::RC_GROUP_LAYOUTS: case LyXRC::RC_GROUP_LAYOUTS:
case LyXRC::RC_HUNSPELLDIR_PATH: case LyXRC::RC_HUNSPELLDIR_PATH:
@ -3032,7 +3003,6 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
case LyXRC::RC_PRINTTOPRINTER: case LyXRC::RC_PRINTTOPRINTER:
case LyXRC::RC_PRINT_ADAPTOUTPUT: case LyXRC::RC_PRINT_ADAPTOUTPUT:
case LyXRC::RC_PRINT_COMMAND: case LyXRC::RC_PRINT_COMMAND:
case LyXRC::RC_RTL_SUPPORT:
case LyXRC::RC_SAVE_COMPRESSED: case LyXRC::RC_SAVE_COMPRESSED:
case LyXRC::RC_SCREEN_DPI: case LyXRC::RC_SCREEN_DPI:
case LyXRC::RC_SCREEN_FONT_ROMAN: case LyXRC::RC_SCREEN_FONT_ROMAN:
@ -3228,10 +3198,6 @@ string const LyXRC::getDescription(LyXRCTags tag)
str = _("The font encoding used for the LaTeX2e fontenc package. T1 is highly recommended for non-English languages."); str = _("The font encoding used for the LaTeX2e fontenc package. T1 is highly recommended for non-English languages.");
break; break;
case RC_FORCE_PAINT_SINGLE_CHAR:
str = _("Disable any kerning and ligatures for text drawing on screen.");
break;
case RC_FILEFORMAT: case RC_FILEFORMAT:
break; break;
@ -3444,10 +3410,6 @@ string const LyXRC::getDescription(LyXRCTags tag)
str = _("Your favorite print program, e.g. \"dvips\", \"dvilj4\"."); str = _("Your favorite print program, e.g. \"dvips\", \"dvilj4\".");
break; break;
case RC_RTL_SUPPORT:
str = _("Select to enable support of right-to-left languages (e.g. Hebrew, Arabic).");
break;
case RC_VISUAL_CURSOR: case RC_VISUAL_CURSOR:
str = _("Select to have visual bidi cursor movement, unselect for logical movement."); str = _("Select to have visual bidi cursor movement, unselect for logical movement.");
break; break;

View File

@ -80,7 +80,6 @@ public:
RC_EXAMPLEPATH, RC_EXAMPLEPATH,
RC_EXPORT_OVERWRITE, RC_EXPORT_OVERWRITE,
RC_FONT_ENCODING, RC_FONT_ENCODING,
RC_FORCE_PAINT_SINGLE_CHAR,
RC_FILEFORMAT, RC_FILEFORMAT,
RC_FORWARD_SEARCH_DVI, RC_FORWARD_SEARCH_DVI,
RC_FORWARD_SEARCH_PDF, RC_FORWARD_SEARCH_PDF,
@ -147,7 +146,6 @@ public:
RC_PRINTTOPRINTER, RC_PRINTTOPRINTER,
RC_PRINT_ADAPTOUTPUT, RC_PRINT_ADAPTOUTPUT,
RC_PRINT_COMMAND, RC_PRINT_COMMAND,
RC_RTL_SUPPORT,
RC_SAVE_COMPRESSED, RC_SAVE_COMPRESSED,
RC_SCREEN_DPI, RC_SCREEN_DPI,
RC_SCREEN_FONT_ROMAN, RC_SCREEN_FONT_ROMAN,
@ -411,8 +409,6 @@ public:
}; };
/// ///
LangPackageSelection language_package_selection; LangPackageSelection language_package_selection;
///
bool rtl_support;
/// bidi cursor movement: true = visual, false = logical /// bidi cursor movement: true = visual, false = logical
bool visual_cursor; bool visual_cursor;
/// ///
@ -552,8 +548,6 @@ public:
/// ///
ScrollWheelZoom scroll_wheel_zoom; ScrollWheelZoom scroll_wheel_zoom;
/// ///
bool force_paint_single_char;
///
int cursor_width; int cursor_width;
/// One of: yes, no, ask /// One of: yes, no, ask
std::string close_buffer_with_last_view; std::string close_buffer_with_last_view;

View File

@ -1928,7 +1928,7 @@ FontSize Paragraph::highestFontInRange
char_type Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const char_type Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
{ {
char_type c = d->text_[pos]; char_type c = d->text_[pos];
if (!lyxrc.rtl_support || !getFontSettings(bparams, pos).isRightToLeft()) if (!getFontSettings(bparams, pos).isRightToLeft())
return c; return c;
// FIXME: The arabic special casing is due to the difference of arabic // FIXME: The arabic special casing is due to the difference of arabic
@ -3200,8 +3200,7 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
bool Paragraph::isHfill(pos_type pos) const bool Paragraph::isHfill(pos_type pos) const
{ {
Inset const * inset = getInset(pos); Inset const * inset = getInset(pos);
return inset && (inset->lyxCode() == SPACE_CODE && return inset && inset->isHfill();
inset->isStretchableSpace());
} }
@ -3305,8 +3304,7 @@ Paragraph::getParLanguage(BufferParams const & bparams) const
bool Paragraph::isRTL(BufferParams const & bparams) const bool Paragraph::isRTL(BufferParams const & bparams) const
{ {
return lyxrc.rtl_support return getParLanguage(bparams)->rightToLeft()
&& getParLanguage(bparams)->rightToLeft()
&& !inInset().getLayout().forceLTR(); && !inInset().getLayout().forceLTR();
} }
@ -3497,46 +3495,6 @@ bool Paragraph::allowEmpty() const
} }
char_type Paragraph::transformChar(char_type c, pos_type pos) const
{
if (!Encodings::isArabicChar(c))
return c;
char_type prev_char = ' ';
char_type next_char = ' ';
for (pos_type i = pos - 1; i >= 0; --i) {
char_type const par_char = d->text_[i];
if (!Encodings::isArabicComposeChar(par_char)) {
prev_char = par_char;
break;
}
}
for (pos_type i = pos + 1, end = size(); i < end; ++i) {
char_type const par_char = d->text_[i];
if (!Encodings::isArabicComposeChar(par_char)) {
next_char = par_char;
break;
}
}
if (Encodings::isArabicChar(next_char)) {
if (Encodings::isArabicChar(prev_char) &&
!Encodings::isArabicSpecialChar(prev_char))
return Encodings::transformChar(c, Encodings::FORM_MEDIAL);
else
return Encodings::transformChar(c, Encodings::FORM_INITIAL);
} else {
if (Encodings::isArabicChar(prev_char) &&
!Encodings::isArabicSpecialChar(prev_char))
return Encodings::transformChar(c, Encodings::FORM_FINAL);
else
return Encodings::transformChar(c, Encodings::FORM_ISOLATED);
}
}
bool Paragraph::brokenBiblio() const bool Paragraph::brokenBiblio() const
{ {
// there is a problem if there is no bibitem at position 0 or // there is a problem if there is no bibitem at position 0 or

View File

@ -431,8 +431,6 @@ public:
/// return true if we allow this par to stay empty /// return true if we allow this par to stay empty
bool allowEmpty() const; bool allowEmpty() const;
/// ///
char_type transformChar(char_type c, pos_type pos) const;
///
ParagraphParameters & params(); ParagraphParameters & params();
/// ///
ParagraphParameters const & params() const; ParagraphParameters const & params() const;

View File

@ -190,8 +190,7 @@ void ParagraphMetrics::dump() const
{ {
lyxerr << "Paragraph::dump: rows.size(): " << rows_.size() << endl; lyxerr << "Paragraph::dump: rows.size(): " << rows_.size() << endl;
for (size_t i = 0; i != rows_.size(); ++i) { for (size_t i = 0; i != rows_.size(); ++i) {
lyxerr << " row " << i << ": "; lyxerr << " row " << i << ": " << rows_[i];
rows_[i].dump();
} }
} }
@ -216,27 +215,15 @@ int ParagraphMetrics::singleWidth(pos_type pos, Font const & font) const
if (Inset const * inset = par_->getInset(pos)) if (Inset const * inset = par_->getInset(pos))
return insetDimension(inset).wid; return insetDimension(inset).wid;
char_type c = par_->getChar(pos); char_type const c = par_->getChar(pos);
if (c == '\t') if (c == '\t')
return 4 * theFontMetrics(font).width(' '); return 4 * theFontMetrics(font).width(' ');
if (!isPrintable(c)) // Note that this function is only called in
return theFontMetrics(font).width(c); // RowPainter::paintText, and only used for characters that do
// not require handling of compose chars or ligatures. It can
Language const * language = font.language(); // therefore be kept simple.
if (language->rightToLeft()) {
if (language->lang() == "arabic_arabtex" ||
language->lang() == "arabic_arabi" ||
language->lang() == "farsi") {
if (Encodings::isArabicComposeChar(c))
return 0;
c = par_->transformChar(c, pos);
} else if (language->lang() == "hebrew" &&
Encodings::isHebrewComposeChar(c)) {
return 0;
}
}
return theFontMetrics(font).width(c); return theFontMetrics(font).width(c);
} }

View File

@ -3,11 +3,11 @@
* This file is part of LyX, the document processor. * This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING. * Licence details can be found in the file COPYING.
* *
* \author unknown
* \author Lars Gullik Bjønnes * \author Lars Gullik Bjønnes
* \author John Levon * \author John Levon
* \author André Pönitz * \author André Pönitz
* \author Jürgen Vigna * \author Jürgen Vigna
* \author Jean-Marc Lasgouttes
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
* *
@ -20,17 +20,124 @@
#include "DocIterator.h" #include "DocIterator.h"
#include "support/debug.h" #include "frontends/FontMetrics.h"
#include "support/debug.h"
#include "support/lassert.h"
#include <algorithm>
#include <ostream>
#include <boost/next_prior.hpp>
using namespace std;
namespace lyx { namespace lyx {
using frontend::FontMetrics;
double Row::Element::pos2x(pos_type const i) const
{
// This can happen with inline completion when clicking on the
// row after the completion.
if (i < pos || i > endpos)
return 0;
bool const rtl = font.isVisibleRightToLeft();
int w = 0;
//handle first the two bounds of the element
if (i == pos || type != STRING)
w = rtl ? width() : 0;
else if (i == endpos)
w = rtl ? 0 : width();
else {
FontMetrics const & fm = theFontMetrics(font);
w = fm.pos2x(str, i - pos, font.isVisibleRightToLeft());
}
return w;
}
pos_type Row::Element::x2pos(double &x) const
{
//lyxerr << "x2pos: x=" << x << " w=" << width() << " " << *this;
bool const rtl = font.isVisibleRightToLeft();
size_t i = 0;
switch (type) {
case STRING: {
FontMetrics const & fm = theFontMetrics(font);
// FIXME: is it really necessary for x to be a double?
int xx = x;
i = fm.x2pos(str, xx, rtl);
x = xx;
break;
}
case VIRTUAL:
// those elements are actually empty (but they have a width)
i = 0;
x = rtl ? width() : 0;
break;
case SEPARATOR:
case INSET:
case SPACE:
// those elements contain only one position. Round to
// the closest side.
if (x > width()) {
x = width();
i = !rtl;
} else {
x = 0;
i = rtl;
}
}
//lyxerr << "=> p=" << pos + i << " x=" << x << endl;
return pos + i;
}
bool Row::Element::breakAt(double w)
{
if (type != STRING || width() <= w)
return false;
bool const rtl = font.isVisibleRightToLeft();
if (rtl)
w = width() - w;
pos_type new_pos = x2pos(w);
if (new_pos == pos)
return false;
str = str.substr(0, new_pos - pos);
if (rtl)
dim.wid -= w;
else
dim.wid = w;
endpos = new_pos;
return true;
}
pos_type Row::Element::left_pos() const
{
return font.isVisibleRightToLeft() ? endpos : pos;
}
pos_type Row::Element::right_pos() const
{
return font.isVisibleRightToLeft() ? pos : endpos;
}
Row::Row() Row::Row()
: separator(0), label_hfill(0), x(0), : separator(0), label_hfill(0), x(0), right_margin(0),
sel_beg(-1), sel_end(-1), sel_beg(-1), sel_end(-1),
begin_margin_sel(false), end_margin_sel(false), begin_margin_sel(false), end_margin_sel(false),
changed_(false), crc_(0), pos_(0), end_(0) changed_(false), crc_(0), pos_(0), end_(0), right_boundary_(false)
{} {}
@ -41,24 +148,6 @@ void Row::setCrc(size_type crc) const
} }
void Row::setDimension(Dimension const & dim)
{
dim_ = dim;
}
void Row::pos(pos_type p)
{
pos_ = p;
}
void Row::endpos(pos_type p)
{
end_ = p;
}
bool Row::isMarginSelected(bool left_margin, DocIterator const & beg, bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
DocIterator const & end) const DocIterator const & end) const
{ {
@ -123,13 +212,229 @@ bool Row::selection() const
} }
void Row::dump(char const * s) const ostream & operator<<(ostream & os, Row::Element const & e)
{ {
LYXERR0(s << " pos: " << pos_ << " end: " << end_ if (e.font.isVisibleRightToLeft())
<< " width: " << dim_.wid os << e.endpos << "<<" << e.pos << " ";
<< " ascent: " << dim_.asc else
<< " descent: " << dim_.des); os << e.pos << ">>" << e.endpos << " ";
switch (e.type) {
case Row::STRING:
os << "STRING: `" << to_utf8(e.str) << "', ";
break;
case Row::VIRTUAL:
os << "VIRTUAL: `" << to_utf8(e.str) << "', ";
break;
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;
}
os << "width=" << e.width();
return os;
} }
ostream & operator<<(ostream & os, Row const & row)
{
os << " pos: " << row.pos_ << " end: " << row.end_
<< " x: " << row.x
<< " width: " << row.dim_.wid
<< " right_margin: " << row.right_margin
<< " ascent: " << row.dim_.asc
<< " descent: " << row.dim_.des
<< " separator: " << row.separator
<< " label_hfill : " << row.label_hfill << "\n";
double x = row.x;
Row::Elements::const_iterator it = row.elements_.begin();
for ( ; it != row.elements_.end() ; ++it) {
os << "x=" << x << " => " << *it << endl;
x += it->width();
}
return os;
}
bool Row::sameString(Font const & f, Change const & ch) const
{
if (elements_.empty())
return false;
Element const & elt = elements_.back();
return elt.type == STRING && !elt.final
&& elt.font == f && elt.change == ch;
}
void Row::finalizeLast()
{
if (elements_.empty())
return;
Element & elt = elements_.back();
if (elt.final)
return;
elt.final = true;
if (elt.type == STRING) {
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)
{
finalizeLast();
Element e(INSET, pos, f, ch);
e.inset = ins;
e.dim = dim;
elements_.push_back(e);
dim_.wid += dim.wid;
}
void Row::add(pos_type const pos, char_type const c,
Font const & f, Change const & ch)
{
if (!sameString(f, ch)) {
finalizeLast();
Element e(STRING, pos, f, ch);
elements_.push_back(e);
}
back().str += c;
back().endpos = pos + 1;
}
void Row::addVirtual(pos_type const pos, docstring const & s,
Font const & f, Change const & ch)
{
finalizeLast();
Element e(VIRTUAL, pos, f, ch);
e.str = s;
e.dim.wid = theFontMetrics(f).width(s);
dim_.wid += e.dim.wid;
e.endpos = pos;
elements_.push_back(e);
finalizeLast();
}
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)
{
finalizeLast();
Element e(SPACE, pos, f, ch);
e.dim.wid = width;
elements_.push_back(e);
dim_.wid += e.dim.wid;
}
void Row::pop_back()
{
dim_.wid -= elements_.back().dim.wid;
elements_.pop_back();
}
void Row::shortenIfNeeded(pos_type const keep, int const w)
{
if (empty() || width() <= w)
return;
/** First, we try to remove elements one by one from the end
* until a separator is found. cit points to the first element
* we want to remove from the row.
*/
Elements::iterator const beg = elements_.begin();
Elements::iterator const end = elements_.end();
Elements::iterator cit = end;
Elements::iterator first_below = end;
int new_end = end_;
int new_wid = dim_.wid;
// if the row ends with a separator, skip it.
if (cit != beg && boost::prior(cit)->type == SEPARATOR && new_end > keep) {
--cit;
new_end = cit->pos;
new_wid -= cit->dim.wid;
}
// Search for a separator where the row can be broken.
while (cit != beg && boost::prior(cit)->type != SEPARATOR && new_end > keep) {
--cit;
new_end = cit->pos;
new_wid -= cit->dim.wid;
if (new_wid < w && first_below == end)
first_below = cit;
}
if (cit != beg) {
// We have found a suitable separator. This is the
// common case.
end_ = new_end;
dim_.wid = new_wid;
elements_.erase(cit, end);
return;
}
/* If we are here, it means that we have not found a separator
* to shorten the row. There is one case where we can do
* something: when we have one big string, maybe with some
* other things after it.
*/
double max_w = w - x;
if (first_below->breakAt(max_w)) {
end_ = first_below->endpos;
dim_.wid = x + first_below->width();
// If there are other elements, they should be removed.
elements_.erase(boost::next(first_below), end);
} else if (first_below->pos > pos_) {
end_ = first_below->pos;
dim_.wid = new_wid;
// Remove all elements from first_below.
elements_.erase(first_below, end);
}
}
void Row::reverseRTL(bool const rtl_par)
{
pos_type i = 0;
pos_type const end = elements_.size();
while (i < end) {
// gather a sequence of elements with the same direction
bool const rtl = elements_[i].font.isVisibleRightToLeft();
pos_type j = i;
while (j < end && elements_[j].font.isVisibleRightToLeft() == rtl)
++j;
// if the direction is not the same as the paragraph
// direction, the sequence has to be reverted.
if (rtl != rtl_par)
reverse(elements_.begin() + i, elements_.begin() + j);
i = j;
}
// If the paragraph itself is RTL, reverse everything
if (rtl_par)
reverse(elements_.begin(), elements_.end());
}
} // namespace lyx } // namespace lyx

199
src/Row.h
View File

@ -15,22 +15,116 @@
#ifndef ROW_H #ifndef ROW_H
#define ROW_H #define ROW_H
#include "Changes.h"
#include "Dimension.h"
#include "Font.h"
#include "support/docstring.h"
#include "support/types.h" #include "support/types.h"
#include "Dimension.h" #include <vector>
namespace lyx { namespace lyx {
class DocIterator; class DocIterator;
class Inset;
/** /**
* An on-screen row of text. A paragraph is broken into a * FIXME: Change Row object to operate only on integers and not doubles.
* RowList for display. Each Row contains position pointers *
* into the first and last character positions of that row. * 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
* of the line.
*/ */
class Row { class Row {
public: public:
// Possible types of row elements
enum Type {
// a string of character
STRING,
/**
* Something (completion, end-of-par marker)
* that occupies space one screen but does not
* correspond to any paragraph contents
*/
VIRTUAL,
// A stretchable space, basically
SEPARATOR,
// An inset
INSET,
// Some spacing described by its width, not a string
SPACE
};
/**
* One element of a Row. It has a set of attributes that can be used
* 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) {}
// Return total width of element, including separator overhead
double width() const { return dim.wid + extra; };
/** Return position in pixels (from the left) of position
* \param i in the row element.
*/
double pos2x(pos_type const i) const;
/** Return character position that is the closest to
* pixel position \param x. The value \param x is
* adjusted to the actual pixel position.
*/
pos_type x2pos(double &x) const;
/** Break the element if possible, so that its width is
* less then \param w. Returns true on success.
*/
bool breakAt(double w);
// Returns the position on left side of the element.
pos_type left_pos() const;
// Returns the position on right side of the element.
pos_type right_pos() const;
// The kind of row element
Type type;
// position of the element in the paragraph
pos_type pos;
// first position after the element in the paragraph
pos_type endpos;
// The dimension of the chunk (does not contains the
// separator correction)
Dimension dim;
// Non-zero only if element is an inset
Inset const * inset;
// Only non-null for separator elements
double extra;
// Non-empty if element is a string or separator
docstring str;
//
Font font;
//
Change change;
// is it possible to add contents to this element?
bool final;
friend std::ostream & operator<<(std::ostream & os, Element const & row);
};
/// ///
Row(); Row();
/// ///
@ -53,18 +147,23 @@ public:
DocIterator const & end) const; DocIterator const & end) const;
/// ///
void pos(pos_type p); void pos(pos_type p) { pos_ = p; }
/// ///
pos_type pos() const { return pos_; } pos_type pos() const { return pos_; }
/// ///
void endpos(pos_type p); void endpos(pos_type p) { end_ = p; }
/// ///
pos_type endpos() const { return end_; } pos_type endpos() const { return end_; }
/// ///
void setDimension(Dimension const & dim); void right_boundary(bool b) { right_boundary_ = b; }
///
bool right_boundary() const { return right_boundary_; }
/// ///
Dimension const & dimension() const { return dim_; } Dimension const & dimension() const { return dim_; }
/// ///
Dimension & dimension() { return dim_; }
///
int height() const { return dim_.height(); } int height() const { return dim_.height(); }
/// ///
int width() const { return dim_.wid; } int width() const { return dim_.wid; }
@ -73,15 +172,81 @@ public:
/// ///
int descent() const { return dim_.des; } int descent() const { return dim_.des; }
/// current debugging only ///
void dump(char const * = "") const; void add(pos_type pos, Inset const * ins, Dimension const & dim,
Font const & f, Change const & ch);
///
void add(pos_type pos, char_type const c,
Font const & f, Change const & ch);
///
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);
///
typedef std::vector<Element> Elements;
///
typedef Elements::iterator iterator;
///
typedef Elements::const_iterator const_iterator;
///
iterator begin() { return elements_.begin(); }
///
iterator end() { return elements_.end(); }
///
const_iterator begin() const { return elements_.begin(); }
///
const_iterator end() const { return elements_.end(); }
///
bool empty() const { return elements_.empty(); }
///
Element & front() { return elements_.front(); }
///
Element const & front() const { return elements_.front(); }
///
Element & back() { return elements_.back(); }
///
Element const & back() const { return elements_.back(); }
/// remove last element
void pop_back();
/// remove all row elements
void clear() { elements_.clear(); }
/**
* 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
*/
void shortenIfNeeded(pos_type const body_pos, int const width);
/**
* If last element of the row is a string, compute its width
* and mark it final.
*/
void finalizeLast();
/**
* 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);
friend std::ostream & operator<<(std::ostream & os, Row const & row);
/// width of a separator (i.e. space) /// width of a separator (i.e. space)
double separator; double separator;
/// width of hfills in the label /// width of hfills in the label
double label_hfill; double label_hfill;
/// the x position of the row /// the x position of the row (left margin)
double x; double x;
/// the right margin of the row
int right_margin;
/// ///
mutable pos_type sel_beg; mutable pos_type sel_beg;
/// ///
@ -101,6 +266,16 @@ private:
bool isMarginSelected(bool left_margin, DocIterator const & beg, bool isMarginSelected(bool left_margin, DocIterator const & beg,
DocIterator const & end) const; DocIterator const & end) const;
/**
* Returns true if a char or string with font \c f and change
* type \c ch can be added to the current last element of the
* row.
*/
bool sameString(Font const & f, Change const & ch) const;
///
Elements elements_;
/// has the Row appearance changed since last drawing? /// has the Row appearance changed since last drawing?
mutable bool changed_; mutable bool changed_;
/// CRC of row contents. /// CRC of row contents.
@ -109,6 +284,8 @@ private:
pos_type pos_; pos_type pos_;
/// one behind last pos covered by this row /// one behind last pos covered by this row
pos_type end_; pos_type end_;
// Is there is a boundary at the end of the row (display inset...)
bool right_boundary_;
/// Row dimension. /// Row dimension.
Dimension dim_; Dimension dim_;
}; };

File diff suppressed because it is too large Load Diff

View File

@ -105,17 +105,11 @@ public:
/// ///
int maxWidth() const { return max_width_; } int maxWidth() const { return max_width_; }
///
int singleWidth(pit_type const pit, pos_type pos) const;
/// ///
int rightMargin(ParagraphMetrics const & pm) const; int rightMargin(ParagraphMetrics const & pm) const;
int rightMargin(pit_type const pit) const; int rightMargin(pit_type const pit) const;
/** this calculates the specified parameters. needed when setting
* the cursor and when creating a visible row */
void computeRowMetrics(pit_type pit, Row & row, int width) const;
/// ///
void draw(PainterInfo & pi, int x, int y) const; void draw(PainterInfo & pi, int x, int y) const;
@ -124,10 +118,7 @@ public:
/// Returns the height of the row (width member is set to 0). /// Returns the height of the row (width member is set to 0).
/// If \c topBottomSpace is true, extra space is added for the /// If \c topBottomSpace is true, extra space is added for the
/// top and bottom row. /// top and bottom row.
Dimension rowHeight( void setRowHeight(Row & row, pit_type const pit,
pit_type const pit,
pos_type const first,
pos_type const end,
bool topBottomSpace = true) const; bool topBottomSpace = true) const;
private: private:
@ -142,19 +133,13 @@ private:
/// sets row.end to the pos value *after* which a row should break. /// sets row.end to the pos value *after* which a row should break.
/// for example, the pos after which isNewLine(pos) == true /// for example, the pos after which isNewLine(pos) == true
pos_type rowBreakPoint( void breakRow(Row & row, int right_margin, pit_type const pit) const;
int width,
pit_type const pit,
pos_type first
) const;
/// returns the minimum space a row needs on the screen in pixel // Expand the alignment of paragraph \param par at position \param pos
int rowWidth( int getAlign(Paragraph const & par, pos_type pos) const;
int right_margin, /** this calculates the specified parameters. needed when setting
pit_type const pit, * the cursor and when creating a visible row */
pos_type const first, void computeRowMetrics(pit_type pit, Row & row, int width) const;
pos_type const end
) const;
// Helper function for the other checkInsetHit method. // Helper function for the other checkInsetHit method.
InsetList::InsetTable * checkInsetHit(pit_type pit, int x, int y); InsetList::InsetTable * checkInsetHit(pit_type pit, int x, int y);
@ -162,11 +147,10 @@ private:
// Temporary public: // Temporary public:
public: public:
/// returns the column near the specified x-coordinate of the row. /// returns the position near the specified x-coordinate of the row.
/// x is an absolute screen coord, it is set to the real beginning /// x is an absolute screen coord, it is set to the real beginning
/// of this column. /// of this column.
pos_type getColumnNearX(pit_type pit, Row const & row, int & x, pos_type getPosNearX(Row const & row, int & x, bool & boundary) const;
bool & boundary) const;
/// returns pos in given par at given x coord. /// returns pos in given par at given x coord.
pos_type x2pos(pit_type pit, int row, int x) const; pos_type x2pos(pit_type pit, int row, int x) const;

View File

@ -77,6 +77,19 @@ public:
virtual int width(docstring const & s) const = 0; virtual int width(docstring const & s) const = 0;
/// FIXME ?? /// FIXME ??
virtual int signedWidth(docstring const & s) const = 0; virtual int signedWidth(docstring const & s) const = 0;
/**
* 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.
*/
virtual int pos2x(docstring const & s, int pos, bool rtl) 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.
*/
virtual int x2pos(docstring const & s, int & x, bool rtl) const = 0;
/// return char dimension for the font. /// return char dimension for the font.
virtual Dimension const dimension(char_type c) const = 0; virtual Dimension const dimension(char_type c) const = 0;
/** /**

View File

@ -14,9 +14,11 @@
#define PAINTER_H #define PAINTER_H
#include "support/strfwd.h" #include "support/strfwd.h"
#include "support/types.h"
namespace lyx { namespace lyx {
class Font;
class FontInfo; class FontInfo;
namespace graphics { class Image; } namespace graphics { class Image; }
@ -106,11 +108,25 @@ public:
virtual void image(int x, int y, int w, int h, virtual void image(int x, int y, int w, int h,
graphics::Image const & image) = 0; graphics::Image const & image) = 0;
/// draw a string at position x, y (y is the baseline) /** draw a string at position x, y (y is the baseline). The
/** * text direction is given by \c rtl.
* \return the width of the drawn text. * \return the width of the drawn text.
*/ */
virtual int text(int x, int y, docstring const & str, FontInfo const & f) = 0; virtual int text(int x, int y, docstring const & str, FontInfo const & f, bool rtl = false) = 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;
/** 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
* \c other color. 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,
Color other, size_type from, size_type to) = 0;
void setDrawingEnabled(bool drawing_enabled) void setDrawingEnabled(bool drawing_enabled)
{ drawing_enabled_ = drawing_enabled; } { drawing_enabled_ = drawing_enabled; }

View File

@ -2417,7 +2417,6 @@ void GuiApplication::restoreGuiSession()
QString const GuiApplication::romanFontName() QString const GuiApplication::romanFontName()
{ {
QFont font; QFont font;
font.setKerning(false);
font.setStyleHint(QFont::Serif); font.setStyleHint(QFont::Serif);
font.setFamily("serif"); font.setFamily("serif");
@ -2428,7 +2427,6 @@ QString const GuiApplication::romanFontName()
QString const GuiApplication::sansFontName() QString const GuiApplication::sansFontName()
{ {
QFont font; QFont font;
font.setKerning(false);
font.setStyleHint(QFont::SansSerif); font.setStyleHint(QFont::SansSerif);
font.setFamily("sans"); font.setFamily("sans");
@ -2439,7 +2437,6 @@ QString const GuiApplication::sansFontName()
QString const GuiApplication::typewriterFontName() QString const GuiApplication::typewriterFontName()
{ {
QFont font; QFont font;
font.setKerning(false);
font.setStyleHint(QFont::TypeWriter); font.setStyleHint(QFont::TypeWriter);
font.setFamily("monospace"); font.setFamily("monospace");

View File

@ -165,7 +165,6 @@ QFont symbolFont(QString const & family, bool * ok)
upper[0] = family[0].toUpper(); upper[0] = family[0].toUpper();
QFont font; QFont font;
font.setKerning(false);
font.setFamily(family); font.setFamily(family);
if (isChosenFont(font, family)) { if (isChosenFont(font, family)) {
@ -256,7 +255,6 @@ static QString makeFontName(QString const & family, QString const & foundry)
GuiFontInfo::GuiFontInfo(FontInfo const & f) GuiFontInfo::GuiFontInfo(FontInfo const & f)
: metrics(QFont()) : metrics(QFont())
{ {
font.setKerning(false);
QString const pat = symbolFamily(f.family()); QString const pat = symbolFamily(f.family());
if (!pat.isEmpty()) { if (!pat.isEmpty()) {
bool ok; bool ok;

View File

@ -15,14 +15,18 @@
#include "qt_helpers.h" #include "qt_helpers.h"
#include "Language.h"
#include "Dimension.h" #include "Dimension.h"
#include "Language.h"
#include "LyXRC.h"
#include "insets/Inset.h" #include "insets/Inset.h"
#include "support/lassert.h" #include "support/lassert.h"
#include <QTextLayout>
using namespace std; using namespace std;
using namespace lyx::support;
namespace lyx { namespace lyx {
namespace frontend { namespace frontend {
@ -39,7 +43,7 @@ namespace {
* why this works well for symbol fonts used in mathed too, even though * why this works well for symbol fonts used in mathed too, even though
* these are not real ucs4 characters. These are codepoints in the * these are not real ucs4 characters. These are codepoints in the
* computer modern fonts used, nothing unicode related. * computer modern fonts used, nothing unicode related.
* See comment in QLPainter::text() for more explanation. * See comment in GuiPainter::text() for more explanation.
**/ **/
inline QChar const ucs4_to_qchar(char_type const ucs4) inline QChar const ucs4_to_qchar(char_type const ucs4)
{ {
@ -49,7 +53,7 @@ inline QChar const ucs4_to_qchar(char_type const ucs4)
} // anon namespace } // anon namespace
GuiFontMetrics::GuiFontMetrics(QFont const & font) : metrics_(font, 0) GuiFontMetrics::GuiFontMetrics(QFont const & font) : font_(font), metrics_(font, 0)
{ {
} }
@ -110,21 +114,14 @@ int GuiFontMetrics::rbearing(char_type c) const
int GuiFontMetrics::width(docstring const & s) const int GuiFontMetrics::width(docstring const & s) const
{ {
size_t ls = s.size();
int w = 0; int w = 0;
for (unsigned int i = 0; i < ls; ++i) { map<docstring, int>::const_iterator it = strwidth_cache_.find(s);
//FIXME: we need to detect surrogate pairs and act accordingly if (it != strwidth_cache_.end()) {
/** w = it->second;
if isSurrogateBase(s[i]) { } else {
docstring c = s[i]; w = metrics_.width(toqstr(s));
w += metrics_.width(toqstr(c + s[i + 1])); strwidth_cache_[s] = w;
++i;
} }
else
*/
w += width(s[i]);
}
return w; return w;
} }
@ -146,6 +143,39 @@ int GuiFontMetrics::signedWidth(docstring const & s) const
return width(s); return width(s);
} }
namespace {
void setTextLayout(QTextLayout & tl, docstring const & s, QFont const & font,
bool const rtl)
{
tl.setText(toqstr(s));
tl.setFont(font);
// Note that both setFlags and the enums are undocumented
tl.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
tl.beginLayout();
tl.createLine();
tl.endLayout();
}
}
int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl) const
{
QTextLayout tl;
setTextLayout(tl, s, font_, rtl);
return tl.lineForTextPosition(pos).cursorToX(pos);
}
int GuiFontMetrics::x2pos(docstring const & s, int & x, bool const rtl) const
{
QTextLayout tl;
setTextLayout(tl, s, font_, rtl);
int pos = tl.lineForTextPosition(0).xToCursor(x);
// correct x value to the actual cursor position.
x = tl.lineForTextPosition(0).cursorToX(pos);
return pos;
}
void GuiFontMetrics::rectText(docstring const & str, void GuiFontMetrics::rectText(docstring const & str,
int & w, int & ascent, int & descent) const int & w, int & ascent, int & descent) const

View File

@ -16,6 +16,9 @@
#include "support/docstring.h" #include "support/docstring.h"
#include <map>
#include <QFont>
#include <QFontMetrics> #include <QFontMetrics>
#include <QHash> #include <QHash>
@ -39,6 +42,8 @@ public:
virtual int rbearing(char_type c) const; virtual int rbearing(char_type c) const;
virtual int width(docstring const & s) const; virtual int width(docstring const & s) const;
virtual int signedWidth(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 Dimension const dimension(char_type c) const; virtual Dimension const dimension(char_type c) const;
virtual void rectText(docstring const & str, virtual void rectText(docstring const & str,
@ -53,12 +58,19 @@ public:
int width(QString const & str) const; int width(QString const & str) const;
private: private:
/// The font
QFont font_;
/// Metrics on the font /// Metrics on the font
QFontMetrics metrics_; QFontMetrics metrics_;
/// Cache of char widths /// Cache of char widths
mutable QHash<char_type, int> width_cache_; mutable QHash<char_type, int> width_cache_;
/// Cache of string widths
/// FIXME Try to use a QHash (this requires to define qHash(docstring))
mutable std::map<docstring, int> strwidth_cache_;
struct AscendDescend { struct AscendDescend {
int ascent; int ascent;
int descent; int descent;

View File

@ -132,7 +132,6 @@ GuiLog::GuiLog(GuiView & lv)
logTB->setReadOnly(true); logTB->setReadOnly(true);
QFont font(guiApp->typewriterFontName()); QFont font(guiApp->typewriterFontName());
font.setKerning(false);
font.setFixedPitch(true); font.setFixedPitch(true);
font.setStyleHint(QFont::TypeWriter); font.setStyleHint(QFont::TypeWriter);
logTB->setFont(font); logTB->setFont(font);

View File

@ -9,6 +9,10 @@
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*/ */
#ifdef Q_WS_MAC
#define USE_RTL_OVERRIDE 1
#endif
#include <config.h> #include <config.h>
#include "GuiPainter.h" #include "GuiPainter.h"
@ -20,7 +24,7 @@
#include "GuiImage.h" #include "GuiImage.h"
#include "qt_helpers.h" #include "qt_helpers.h"
#include "FontInfo.h" #include "Font.h"
#include "Language.h" #include "Language.h"
#include "LyXRC.h" #include "LyXRC.h"
@ -41,6 +45,7 @@
#endif #endif
using namespace std; using namespace std;
using namespace lyx::support;
namespace lyx { namespace lyx {
namespace frontend { namespace frontend {
@ -271,14 +276,14 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
int GuiPainter::text(int x, int y, char_type c, FontInfo const & f) int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
{ {
docstring s(1, c); return text(x, y, docstring(1, c), f);
return text(x, y, s, f);
} }
int GuiPainter::text(int x, int y, docstring const & s, int GuiPainter::text(int x, int y, docstring const & s,
FontInfo const & f) FontInfo const & f, bool const rtl)
{ {
//LYXERR0("text: x=" << x << ", s=" << s);
if (s.empty()) if (s.empty())
return 0; return 0;
@ -305,24 +310,21 @@ int GuiPainter::text(int x, int y, docstring const & s,
QFont const & ff = getFont(f); QFont const & ff = getFont(f);
GuiFontMetrics const & fm = getFontMetrics(f); GuiFontMetrics const & fm = getFontMetrics(f);
int textwidth;
// Here we use the font width cache instead of // Here we use the font width cache instead of
// textwidth = fontMetrics().width(str); // textwidth = fontMetrics().width(str);
// because the above is awfully expensive on MacOSX // because the above is awfully expensive on MacOSX
textwidth = fm.width(s); int const textwidth = fm.width(s);
textDecoration(f, x, y, textwidth);
if (!isDrawingEnabled()) if (!isDrawingEnabled())
return textwidth; return textwidth;
textDecoration(f, x, y, textwidth);
// Qt4 does not display a glyph whose codepoint is the // Qt4 does not display a glyph whose codepoint is the
// same as that of a soft-hyphen (0x00ad), unless it // same as that of a soft-hyphen (0x00ad), unless it
// occurs at a line-break. As a kludge, we force Qt to // occurs at a line-break. As a kludge, we force Qt to
// render this glyph using a one-column line. // render this glyph using a one-column line.
// This is needed for some math glyphs. // This is needed for some math glyphs.
// FIXME In texted, this behaves differently depending
// on lyxrc.force_paint_single_char status.
// Should the soft hyphen char be displayed at all? // Should the soft hyphen char be displayed at all?
// I don't think so (i.e., Qt is correct as far as // I don't think so (i.e., Qt is correct as far as
// texted is concerned). /spitz // texted is concerned). /spitz
@ -391,15 +393,78 @@ int GuiPainter::text(int x, int y, docstring const & s,
setQPainterPen(computeColor(f.realColor())); setQPainterPen(computeColor(f.realColor()));
if (font() != ff) if (font() != ff)
setFont(ff); setFont(ff);
// We need to draw the text as LTR as we use our own bidi code.
QPainter::setLayoutDirection(Qt::LeftToRight); /* In LyX, the character direction is forced by the language.
* Therefore, we have to signal that fact to Qt.
*/
#ifdef USE_RTL_OVERRIDE
/* Use unicode override characters to enforce drawing direction
* Source: http://www.iamcal.com/understanding-bidirectional-text/
*/
if (rtl)
// Right-to-left override: forces to draw text right-to-left
str = QChar(0x202E) + str;
else
// Left-to-right override: forces to draw text left-to-right
str = QChar(0x202D) + str;
drawText(x, y, str); drawText(x, y, str);
#else
/* This is a cleanr solution, but it has two drawbacks
* - it seems that it does not work under Mac OS X
* - it is not really documented
*/
//This is much stronger than setLayoutDirection.
int flag = rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
drawText(x + (rtl ? textwidth : 0), y - fm.maxAscent(), 0, 0,
flag | Qt::TextDontClip,
str);
#endif
//LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8()) //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
// << " at " << x << "," << y); // << " at " << x << "," << y);
return textwidth; return textwidth;
} }
int GuiPainter::text(int x, int y, docstring const & str, Font const & f)
{
return text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft());
}
int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
Color other, size_type from, size_type to)
{
GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
FontInfo fi = f.fontInfo();
bool const rtl = f.isVisibleRightToLeft();
// 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);
if (xmin > xmax)
swap(xmin, xmax);
// First the part in other color
Color const orig = fi.realColor();
fi.setPaintColor(other);
setClipRect(QRect(x + xmin, y - ascent, xmax - xmin, height));
int const textwidth = text(x, y, str, fi, rtl);
// Then the part in normal color
// Note that in Qt5, it is not possible to use Qt::UniteClip
fi.setPaintColor(orig);
setClipRect(QRect(x, y - ascent, xmin, height));
text(x, y, str, fi, rtl);
setClipRect(QRect(x + xmax, y - ascent, textwidth - xmax, height));
text(x, y, str, fi, rtl);
setClipping(false);
return textwidth;
}
void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width) void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
{ {
if (f.underbar() == FONT_ON) if (f.underbar() == FONT_ON)

View File

@ -86,9 +86,25 @@ public:
virtual void image(int x, int y, int w, int h, virtual void image(int x, int y, int w, int h,
lyx::graphics::Image const & image); lyx::graphics::Image const & image);
/// draw a string at position x, y (y is the baseline) /** draw a string at position x, y (y is the baseline). The
virtual int text(int x, int y, * text direction is given by \c rtl.
docstring const & str, FontInfo const & f); * \return the width of the drawn text.
*/
virtual int text(int x, int y, docstring const & str, FontInfo const & f, bool rtl = false);
/** 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);
/** 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
* \c other color. 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,
Color other, size_type from, size_type to);
/// draw a char at position x, y (y is the baseline) /// draw a char at position x, y (y is the baseline)
virtual int text(int x, int y, char_type c, FontInfo const & f); virtual int text(int x, int y, char_type c, FontInfo const & f);

View File

@ -314,7 +314,6 @@ static void setComboxFont(QComboBox * cb, string const & family,
// for bug 1063. // for bug 1063.
QFont font; QFont font;
font.setKerning(false);
QString const font_family = toqstr(family); QString const font_family = toqstr(family);
if (font_family == guiApp->romanFontName()) { if (font_family == guiApp->romanFontName()) {
@ -2263,8 +2262,6 @@ PrefLanguage::PrefLanguage(GuiPreferences * form)
{ {
setupUi(this); setupUi(this);
connect(rtlGB, SIGNAL(clicked()),
this, SIGNAL(changed()));
connect(visualCursorRB, SIGNAL(clicked()), connect(visualCursorRB, SIGNAL(clicked()),
this, SIGNAL(changed())); this, SIGNAL(changed()));
connect(logicalCursorRB, SIGNAL(clicked()), connect(logicalCursorRB, SIGNAL(clicked()),
@ -2348,9 +2345,7 @@ void PrefLanguage::on_languagePackageCO_currentIndexChanged(int i)
void PrefLanguage::apply(LyXRC & rc) const void PrefLanguage::apply(LyXRC & rc) const
{ {
// FIXME: remove rtl_support bool rc.visual_cursor = visualCursorRB->isChecked();
rc.rtl_support = rtlGB->isChecked();
rc.visual_cursor = rtlGB->isChecked() && visualCursorRB->isChecked();
rc.mark_foreign_language = markForeignCB->isChecked(); rc.mark_foreign_language = markForeignCB->isChecked();
rc.language_auto_begin = autoBeginCB->isChecked(); rc.language_auto_begin = autoBeginCB->isChecked();
rc.language_auto_end = autoEndCB->isChecked(); rc.language_auto_end = autoEndCB->isChecked();
@ -2376,8 +2371,6 @@ void PrefLanguage::apply(LyXRC & rc) const
void PrefLanguage::update(LyXRC const & rc) void PrefLanguage::update(LyXRC const & rc)
{ {
// FIXME: remove rtl_support bool
rtlGB->setChecked(rc.rtl_support);
if (rc.visual_cursor) if (rc.visual_cursor)
visualCursorRB->setChecked(true); visualCursorRB->setChecked(true);
else else

View File

@ -61,7 +61,6 @@ GuiProgressView::GuiProgressView(GuiView & parent, Qt::DockWidgetArea area,
setWidget(widget_); setWidget(widget_);
QFont font(guiApp->typewriterFontName()); QFont font(guiApp->typewriterFontName());
font.setKerning(false);
font.setFixedPitch(true); font.setFixedPitch(true);
font.setStyleHint(QFont::TypeWriter); font.setStyleHint(QFont::TypeWriter);
widget_->outTE->setFont(font); widget_->outTE->setFont(font);

View File

@ -72,7 +72,6 @@ ViewSourceWidget::ViewSourceWidget()
///dialog_->viewSourceTV->setAcceptRichText(false); ///dialog_->viewSourceTV->setAcceptRichText(false);
// this is personal. I think source code should be in fixed-size font // this is personal. I think source code should be in fixed-size font
QFont font(guiApp->typewriterFontName()); QFont font(guiApp->typewriterFontName());
font.setKerning(false);
font.setFixedPitch(true); font.setFixedPitch(true);
font.setStyleHint(QFont::TypeWriter); font.setStyleHint(QFont::TypeWriter);
viewSourceTV->setFont(font); viewSourceTV->setFont(font);

View File

@ -1002,7 +1002,7 @@ void GuiWorkArea::generateSyntheticMouseEvent()
// Find the position of the cursor // Find the position of the cursor
bool bound; bool bound;
int x = d->synthetic_mouse_event_.cmd.x(); int x = d->synthetic_mouse_event_.cmd.x();
pos_type const pos = rit->pos() + tm.getColumnNearX(pit, *rit, x, bound); pos_type const pos = tm.getPosNearX(*rit, x, bound);
// Set the cursor // Set the cursor
cur.pit() = pit; cur.pit() = pit;

View File

@ -224,16 +224,6 @@
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QCheckBox" name="rtlGB">
<property name="toolTip">
<string>Select to enable support of right-to-left languages (e.g. Hebrew, Arabic).</string>
</property>
<property name="text">
<string>Enable &amp;RTL support</string>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<property name="spacing"> <property name="spacing">
<number>6</number> <number>6</number>

View File

@ -431,8 +431,8 @@ public:
/// is this equivalent to a space (which is BTW different from /// is this equivalent to a space (which is BTW different from
/// a line separator)? /// a line separator)?
virtual bool isSpace() const { return false; } virtual bool isSpace() const { return false; }
/// is this an expandible space (rubber length)? /// does this inset try to use all available space (like \\hfill does)?
virtual bool isStretchableSpace() const { return false; } virtual bool isHfill() const { return false; }
enum DisplayType { enum DisplayType {
Inline = 0, Inline = 0,

View File

@ -620,16 +620,4 @@ void InsetIPAChar::validate(LaTeXFeatures & features) const
} }
bool InsetIPAChar::isLetter() const
{
return true;
}
bool InsetIPAChar::isLineSeparator() const
{
return false;
}
} // namespace lyx } // namespace lyx

View File

@ -167,9 +167,7 @@ public:
/// should this inset be handled like a normal character? /// should this inset be handled like a normal character?
bool isChar() const { return true; } bool isChar() const { return true; }
/// is this equivalent to a letter? /// is this equivalent to a letter?
bool isLetter() const; bool isLetter() const { return true; }
/// should we break lines after this inset?
bool isLineSeparator() const;
private: private:
Inset * clone() const { return new InsetIPAChar(*this); } Inset * clone() const { return new InsetIPAChar(*this); }

View File

@ -199,7 +199,7 @@ int const arrow_size = 8;
void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
{ {
if (isStretchableSpace()) { if (isHfill()) {
// The metrics for this kinds are calculated externally in // The metrics for this kinds are calculated externally in
// \c TextMetrics::computeRowMetrics. Those are dummy value: // \c TextMetrics::computeRowMetrics. Those are dummy value:
dim = Dimension(10, 10, 10); dim = Dimension(10, 10, 10);
@ -267,7 +267,7 @@ void InsetSpace::draw(PainterInfo & pi, int x, int y) const
{ {
Dimension const dim = dimension(*pi.base.bv); Dimension const dim = dimension(*pi.base.bv);
if (isStretchableSpace() || params_.length.len().value() < 0) { if (isHfill() || params_.length.len().value() < 0) {
int const asc = theFontMetrics(pi.base.font).ascent('M'); int const asc = theFontMetrics(pi.base.font).ascent('M');
int const desc = theFontMetrics(pi.base.font).descent('M'); int const desc = theFontMetrics(pi.base.font).descent('M');
// Pixel height divisible by 2 for prettier fill graphics: // Pixel height divisible by 2 for prettier fill graphics:
@ -837,7 +837,7 @@ void InsetSpace::forOutliner(docstring & os, size_t) const
} }
bool InsetSpace::isStretchableSpace() const bool InsetSpace::isHfill() const
{ {
return params_.kind == InsetSpaceParams::HFILL return params_.kind == InsetSpaceParams::HFILL
|| params_.kind == InsetSpaceParams::HFILL_PROTECTED || params_.kind == InsetSpaceParams::HFILL_PROTECTED

View File

@ -142,9 +142,8 @@ public:
bool clickable(int, int) const { return true; } bool clickable(int, int) const { return true; }
/// ///
InsetCode lyxCode() const { return SPACE_CODE; } InsetCode lyxCode() const { return SPACE_CODE; }
/// is this an expandible space (rubber length)? /// does this inset try to use all available space (like \\hfill does)?
bool isStretchableSpace() const; bool isHfill() const;
/// should this inset be handled like a normal character? /// should this inset be handled like a normal character?
bool isChar() const { return true; } bool isChar() const { return true; }
/// is this equivalent to a letter? /// is this equivalent to a letter?

View File

@ -14,14 +14,12 @@
#include "rowpainter.h" #include "rowpainter.h"
#include "Bidi.h"
#include "Buffer.h" #include "Buffer.h"
#include "CoordCache.h" #include "CoordCache.h"
#include "Cursor.h" #include "Cursor.h"
#include "BufferParams.h" #include "BufferParams.h"
#include "BufferView.h" #include "BufferView.h"
#include "Changes.h" #include "Changes.h"
#include "Encoding.h"
#include "Language.h" #include "Language.h"
#include "Layout.h" #include "Layout.h"
#include "LyXRC.h" #include "LyXRC.h"
@ -56,13 +54,12 @@ using frontend::FontMetrics;
RowPainter::RowPainter(PainterInfo & pi, RowPainter::RowPainter(PainterInfo & pi,
Text const & text, pit_type pit, Row const & row, Bidi & bidi, int x, int y) Text const & text, pit_type pit, Row const & row, int x, int y)
: pi_(pi), text_(text), : pi_(pi), text_(text),
text_metrics_(pi_.base.bv->textMetrics(&text)), text_metrics_(pi_.base.bv->textMetrics(&text)),
pars_(text.paragraphs()), pars_(text.paragraphs()),
row_(row), pit_(pit), par_(text.paragraphs()[pit]), row_(row), pit_(pit), par_(text.paragraphs()[pit]),
pm_(text_metrics_.parMetrics(pit)), pm_(text_metrics_.parMetrics(pit)), change_(pi_.change_),
bidi_(bidi), change_(pi_.change_),
xo_(x), yo_(y), width_(text_metrics_.width()), xo_(x), yo_(y), width_(text_metrics_.width()),
solid_line_thickness_(1.0), solid_line_offset_(1), solid_line_thickness_(1.0), solid_line_offset_(1),
dotted_line_thickness_(1.0), dotted_line_offset_(2) dotted_line_thickness_(1.0), dotted_line_offset_(2)
@ -163,125 +160,30 @@ void RowPainter::paintInset(Inset const * inset, pos_type const pos)
} }
void RowPainter::paintHebrewComposeChar(pos_type & vpos, FontInfo const & font) void RowPainter::paintChars(pos_type & vpos, Font const & font)
{
pos_type pos = bidi_.vis2log(vpos);
docstring str;
// first char
char_type c = par_.getChar(pos);
str += c;
++vpos;
int const width = theFontMetrics(font).width(c);
int dx = 0;
for (pos_type i = pos - 1; i >= 0; --i) {
c = par_.getChar(i);
if (!Encodings::isHebrewComposeChar(c)) {
if (isPrintableNonspace(c)) {
int const width2 = pm_.singleWidth(i,
text_metrics_.displayFont(pit_, i));
dx = (c == 0x05e8 || // resh
c == 0x05d3) // dalet
? width2 - width
: (width2 - width) / 2;
}
break;
}
}
// Draw nikud
pi_.pain.text(int(x_) + dx, yo_, str, font);
}
void RowPainter::paintArabicComposeChar(pos_type & vpos, FontInfo const & font)
{
pos_type pos = bidi_.vis2log(vpos);
docstring str;
// first char
char_type c = par_.getChar(pos);
c = par_.transformChar(c, pos);
str += c;
++vpos;
int const width = theFontMetrics(font).width(c);
int dx = 0;
for (pos_type i = pos - 1; i >= 0; --i) {
c = par_.getChar(i);
if (!Encodings::isArabicComposeChar(c)) {
if (isPrintableNonspace(c)) {
int const width2 = pm_.singleWidth(i,
text_metrics_.displayFont(pit_, i));
dx = (width2 - width) / 2;
}
break;
}
}
// Draw nikud
pi_.pain.text(int(x_) + dx, yo_, str, font);
}
void RowPainter::paintChars(pos_type & vpos, FontInfo const & font,
bool hebrew, bool arabic)
{ {
// This method takes up 70% of time when typing // This method takes up 70% of time when typing
pos_type pos = bidi_.vis2log(vpos); pos_type pos = bidi_.vis2log(vpos);
pos_type start_pos = pos;
// first character // first character
char_type prev_char = par_.getChar(pos); docstring str;
vector<char_type> str;
str.reserve(100); str.reserve(100);
str.push_back(prev_char); char_type const c = par_.getChar(pos);
str.push_back(c);
// FIXME: Why only round brackets and why the difference to
// Hebrew? See also Paragraph::getUChar
if (arabic) {
char_type c = str[0];
if (c == '(')
c = ')';
else if (c == ')')
c = '(';
str[0] = par_.transformChar(c, pos);
}
pos_type const end = row_.endpos();
FontSpan const font_span = par_.fontSpan(pos); FontSpan const font_span = par_.fontSpan(pos);
// Track-change status. // Track-change status.
Change const & change_running = par_.lookupChange(pos); Change const & change_running = par_.lookupChange(pos);
// selected text?
bool const selection = (pos >= row_.sel_beg && pos < row_.sel_end)
|| pi_.selected;
// spelling correct? // spelling correct?
bool const spell_state = bool const spell_state =
lyxrc.spellcheck_continuously && par_.isMisspelled(pos); lyxrc.spellcheck_continuously && par_.isMisspelled(pos);
// collect as much similar chars as we can // collect as much similar chars as we can
pos_type const end = row_.endpos();
for (++vpos ; vpos < end ; ++vpos) { for (++vpos ; vpos < end ; ++vpos) {
// Work-around bug #6920
// The bug can be reproduced with DejaVu font under Linux.
// The issue is that we compute the metrics character by character
// in ParagraphMetrics::singleWidth(); but we paint word by word
// for performance reason.
// Maybe a more general fix would be draw character by character
// for some predefined fonts on some platform. In arabic and
// Hebrew we already do paint this way.
if (prev_char == 'f' || lyxrc.force_paint_single_char)
break;
pos = bidi_.vis2log(vpos); pos = bidi_.vis2log(vpos);
if (pos < font_span.first || pos > font_span.last)
break;
bool const new_selection = pos >= row_.sel_beg && pos < row_.sel_end; if (!font_span.inside(pos))
if (new_selection != selection)
// Selection ends or starts here.
break; break;
bool const new_spell_state = bool const new_spell_state =
@ -295,7 +197,7 @@ void RowPainter::paintChars(pos_type & vpos, FontInfo const & font,
// Track change type or author has changed. // Track change type or author has changed.
break; break;
char_type c = par_.getChar(pos); char_type const c = par_.getChar(pos);
if (c == '\t') if (c == '\t')
break; break;
@ -303,65 +205,64 @@ void RowPainter::paintChars(pos_type & vpos, FontInfo const & font,
if (!isPrintableNonspace(c)) if (!isPrintableNonspace(c))
break; 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 /* Because we do our own bidi, at this point the strings are
* already in visual order. However, Qt also applies its own * already in visual order. However, Qt also applies its own
* bidi algorithm to strings that it paints to the screen. * bidi algorithm to strings that it paints to the screen.
* Therefore, if we were to paint Hebrew/Arabic words as a * Therefore, if we were to paint Hebrew/Arabic words as a
* single string, the letters in the words would get reversed * single string, the letters in the words would get reversed
* again. In order to avoid that, we don't collect Hebrew/ * again. In order to avoid that, we force LTR drawing.
* Arabic characters, but rather paint them one at a time.
* See also http://thread.gmane.org/gmane.editors.lyx.devel/79740 * See also http://thread.gmane.org/gmane.editors.lyx.devel/79740
* for an earlier thread on the subject
*/ */
if (hebrew) if (font.isVisibleRightToLeft()) {
break; reverse(str.begin(), str.end());
// If the string is reversed, the positions need to be adjusted
/* FIXME: these checks are irrelevant, since 'arabic' and ++pos;
* 'hebrew' alone are already going to trigger a break. ++start_pos;
* However, this should not be removed completely, because swap(start_pos, pos);
* if an alternative solution is found which allows grouping
* of arabic and hebrew characters, then these breaks may have
* to be re-applied.
if (arabic && Encodings::isArabicComposeChar(c))
break;
if (hebrew && Encodings::isHebrewComposeChar(c))
break;
*/
// FIXME: Why only round brackets and why the difference to
// Hebrew? See also Paragraph::getUChar
if (arabic) {
if (c == '(')
c = ')';
else if (c == ')')
c = '(';
c = par_.transformChar(c, pos);
/* see comment in hebrew, explaining why we break */
break;
} }
str.push_back(c); // at least part of text selected?
prev_char = c; bool const some_sel = (pos >= row_.sel_beg && start_pos < row_.sel_end)
|| pi_.selected;
// all the text selected?
bool const all_sel = (start_pos >= row_.sel_beg && pos < row_.sel_end)
|| pi_.selected;
if (all_sel) {
Font copy = font;
copy.fontInfo().setPaintColor(Color_selectiontext);
x_ += pi_.pain.text(int(x_), yo_, str, copy);
} else if (change_running.changed()) {
Font copy = font;
copy.fontInfo().setPaintColor(change_running.color());
x_ += pi_.pain.text(int(x_), yo_, str, copy);
} else if (!some_sel) {
x_ += pi_.pain.text(int(x_), yo_, str, font);
} else {
x_ += pi_.pain.text(int(x_), yo_, str, font, Color_selectiontext,
max(row_.sel_beg, start_pos) - start_pos,
min(row_.sel_end, pos) - start_pos);
} }
docstring s(&str[0], str.size());
if (s[0] == '\t')
s.replace(0,1,from_ascii(" "));
if (!selection && !change_running.changed()) {
x_ += pi_.pain.text(int(x_), yo_, s, font);
return;
}
FontInfo copy = font;
if (change_running.changed())
copy.setPaintColor(change_running.color());
else if (selection)
copy.setPaintColor(Color_selectiontext);
x_ += pi_.pain.text(int(x_), yo_, s, copy);
} }
@ -404,36 +305,14 @@ void RowPainter::paintMisspelledMark(double orig_x, bool changed)
void RowPainter::paintFromPos(pos_type & vpos, bool changed) void RowPainter::paintFromPos(pos_type & vpos, bool changed)
{ {
pos_type const pos = bidi_.vis2log(vpos); pos_type const pos = bidi_.vis2log(vpos);
Font const orig_font = text_metrics_.displayFont(pit_, pos); Font const font = text_metrics_.displayFont(pit_, pos);
double const orig_x = x_; double const orig_x = x_;
// usual characters, no insets paintChars(vpos, font);
char_type const c = par_.getChar(pos); paintForeignMark(orig_x, font.language());
// special case languages // Paint the spelling mark if needed.
string const & lang = orig_font.language()->lang(); if (lyxrc.spellcheck_continuously && par_.isMisspelled(pos)) {
bool const hebrew = lang == "hebrew";
bool const arabic = lang == "arabic_arabtex" || lang == "arabic_arabi" ||
lang == "farsi";
// spelling correct?
bool const misspelled =
lyxrc.spellcheck_continuously && par_.isMisspelled(pos);
// draw as many chars as we can
if ((!hebrew && !arabic)
|| (hebrew && !Encodings::isHebrewComposeChar(c))
|| (arabic && !Encodings::isArabicComposeChar(c))) {
paintChars(vpos, orig_font.fontInfo(), hebrew, arabic);
} else if (hebrew) {
paintHebrewComposeChar(vpos, orig_font.fontInfo());
} else if (arabic) {
paintArabicComposeChar(vpos, orig_font.fontInfo());
}
paintForeignMark(orig_x, orig_font.language());
if (lyxrc.spellcheck_continuously && misspelled) {
// check for cursor position // check for cursor position
// don't draw misspelled marker for words at cursor position // don't draw misspelled marker for words at cursor position
// we don't want to disturb the process of text editing // we don't want to disturb the process of text editing
@ -757,11 +636,10 @@ void RowPainter::paintLast()
case END_LABEL_NO_LABEL: case END_LABEL_NO_LABEL:
if (lyxrc.paragraph_markers && size_type(pit_ + 1) < pars_.size()) { if (lyxrc.paragraph_markers && size_type(pit_ + 1) < pars_.size()) {
docstring const s = docstring(1, char_type(0x00B6)); docstring const s = docstring(1, char_type(0x00B6));
FontInfo f = FontInfo(); FontInfo f = FontInfo(text_.layoutFont(pit_));
FontMetrics const & fm = theFontMetrics(f);
f.setColor(Color_paragraphmarker); f.setColor(Color_paragraphmarker);
pi_.pain.text(int(x_), yo_, s, f); pi_.pain.text(int(x_), yo_, s, f);
x_ += fm.width(s); x_ += theFontMetrics(f).width(s);
} }
break; break;
} }
@ -800,6 +678,7 @@ void RowPainter::paintOnlyInsets()
void RowPainter::paintText() void RowPainter::paintText()
{ {
//LYXERR0("-------------------------------------------------------");
pos_type const end = row_.endpos(); pos_type const end = row_.endpos();
// Spaces at logical line breaks in bidi text must be skipped during // Spaces at logical line breaks in bidi text must be skipped during
// painting. However, they may appear visually in the middle // painting. However, they may appear visually in the middle
@ -857,23 +736,21 @@ void RowPainter::paintText()
} }
// Use font span to speed things up, see above // Use font span to speed things up, see above
if (vpos < font_span.first || vpos > font_span.last) { if (!font_span.inside(pos)) {
font_span = par_.fontSpan(vpos); font_span = par_.fontSpan(pos);
font = text_metrics_.displayFont(pit_, vpos); font = text_metrics_.displayFont(pit_, pos);
// split font span if inline completion is inside // split font span if inline completion is inside
if (font_span.first <= inlineCompletionVPos if (inlineCompletionVPos != -1
&& font_span.last > inlineCompletionVPos) && font_span.inside(inlineCompletionPos.pos()))
font_span.last = inlineCompletionVPos; 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); const int width_pos = pm_.singleWidth(pos, font);
if (x_ + width_pos < 0) {
x_ += width_pos;
++vpos;
continue;
}
Change const & change = par_.lookupChange(pos); Change const & change = par_.lookupChange(pos);
if (change.changed() && !change_running.changed()) { if (change.changed() && !change_running.changed()) {
change_running = change; change_running = change;
@ -908,6 +785,7 @@ void RowPainter::paintText()
int const lwidth = theFontMetrics(labelFont()) int const lwidth = theFontMetrics(labelFont())
.width(layout.labelsep); .width(layout.labelsep);
// width_pos is either the width of a space or an inset
x_ += row_.label_hfill + lwidth - width_pos; x_ += row_.label_hfill + lwidth - width_pos;
} }
@ -918,6 +796,7 @@ void RowPainter::paintText()
if (par_.isSeparator(pos)) { if (par_.isSeparator(pos)) {
Font const orig_font = text_metrics_.displayFont(pit_, pos); Font const orig_font = text_metrics_.displayFont(pit_, pos);
double const orig_x = x_; double const orig_x = x_;
// width_pos is the width of a space
double separator_width = width_pos; double separator_width = width_pos;
if (pos >= body_pos) if (pos >= body_pos)
separator_width += row_.separator; separator_width += row_.separator;

View File

@ -14,13 +14,13 @@
#ifndef ROWPAINTER_H #ifndef ROWPAINTER_H
#define ROWPAINTER_H #define ROWPAINTER_H
#include "Bidi.h"
#include "Changes.h" #include "Changes.h"
#include "support/types.h" #include "support/types.h"
namespace lyx { namespace lyx {
class Bidi;
class BufferView; class BufferView;
class Font; class Font;
class FontInfo; class FontInfo;
@ -36,6 +36,21 @@ class TextMetrics;
namespace frontend { class Painter; } 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. * A class used for painting an individual row of text.
* FIXME: get rid of that class. * FIXME: get rid of that class.
@ -44,7 +59,7 @@ class RowPainter {
public: public:
/// initialise and run painter /// initialise and run painter
RowPainter(PainterInfo & pi, Text const & text, RowPainter(PainterInfo & pi, Text const & text,
pit_type pit, Row const & row, Bidi & bidi, int x, int y); pit_type pit, Row const & row, int x, int y);
/// paint various parts /// paint various parts
/// FIXME: transfer to TextMetrics /// FIXME: transfer to TextMetrics
@ -61,10 +76,7 @@ private:
void paintSeparator(double orig_x, double width, FontInfo const & font); void paintSeparator(double orig_x, double width, FontInfo const & font);
void paintForeignMark(double orig_x, Language const * lang, int desc = 0); void paintForeignMark(double orig_x, Language const * lang, int desc = 0);
void paintMisspelledMark(double orig_x, bool changed); void paintMisspelledMark(double orig_x, bool changed);
void paintHebrewComposeChar(pos_type & vpos, FontInfo const & font); void paintChars(pos_type & vpos, Font const & font);
void paintArabicComposeChar(pos_type & vpos, FontInfo const & font);
void paintChars(pos_type & vpos, FontInfo const & font,
bool hebrew, bool arabic);
int paintAppendixStart(int y); int paintAppendixStart(int y);
void paintFromPos(pos_type & vpos, bool changed); void paintFromPos(pos_type & vpos, bool changed);
void paintInset(Inset const * inset, pos_type const pos); void paintInset(Inset const * inset, pos_type const pos);
@ -98,10 +110,8 @@ private:
Paragraph const & par_; Paragraph const & par_;
ParagraphMetrics const & pm_; ParagraphMetrics const & pm_;
/// bidi cache, comes from outside the rowpainter because /// bidi cache
/// rowpainters are normally created in a for loop and there only Bidi bidi_;
/// one of them is active at a time.
Bidi & bidi_;
/// row changed? (change tracking) /// row changed? (change tracking)
Change const change_; Change const change_;