mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-22 10:00:33 +00:00
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:
commit
41740ea915
@ -1479,7 +1479,7 @@ if __name__ == '__main__':
|
||||
lyx_check_config = True
|
||||
lyx_kpsewhich = True
|
||||
outfile = 'lyxrc.defaults'
|
||||
lyxrc_fileformat = 15
|
||||
lyxrc_fileformat = 17
|
||||
rc_entries = ''
|
||||
lyx_keep_temps = False
|
||||
version_suffix = ''
|
||||
|
@ -63,6 +63,16 @@
|
||||
# New RC default_otf_view_format
|
||||
# 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
|
||||
# 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 = [
|
||||
[ 1, [ # there were several conversions for format 1
|
||||
export_menu,
|
||||
@ -327,5 +353,7 @@ conversions = [
|
||||
[ 12, []],
|
||||
[ 13, [mac_cursor_movement]],
|
||||
[ 14, []],
|
||||
[ 15, []]
|
||||
[ 15, []],
|
||||
[ 16, [remove_force_paint_single_char]],
|
||||
[ 17, [remove_rtl]]
|
||||
]
|
||||
|
@ -57,10 +57,6 @@ void Bidi::computeTables(Paragraph const & par,
|
||||
Buffer const & buf, Row const & row)
|
||||
{
|
||||
same_direction_ = true;
|
||||
if (!lyxrc.rtl_support) {
|
||||
start_ = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (par.inInset().forceLTR()) {
|
||||
start_ = -1;
|
||||
@ -192,7 +188,7 @@ void Bidi::computeTables(Paragraph const & par,
|
||||
bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par,
|
||||
pos_type pos) const
|
||||
{
|
||||
if (!lyxrc.rtl_support || pos == 0)
|
||||
if (pos == 0)
|
||||
return false;
|
||||
|
||||
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,
|
||||
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 rtl2 = inRange(pos)
|
||||
? level(pos) % 2
|
||||
|
@ -803,14 +803,12 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
|
||||
|
||||
void BufferView::translateAndInsert(char_type c, Text * t, Cursor & cur)
|
||||
{
|
||||
if (lyxrc.rtl_support) {
|
||||
if (d->cursor_.real_current_font.isRightToLeft()) {
|
||||
if (d->intl_.keymap == Intl::PRIMARY)
|
||||
d->intl_.keyMapSec();
|
||||
} else {
|
||||
if (d->intl_.keymap == Intl::SECONDARY)
|
||||
d->intl_.keyMapPrim();
|
||||
}
|
||||
if (d->cursor_.real_current_font.isRightToLeft()) {
|
||||
if (d->intl_.keymap == Intl::PRIMARY)
|
||||
d->intl_.keyMapSec();
|
||||
} else {
|
||||
if (d->intl_.keymap == Intl::SECONDARY)
|
||||
d->intl_.keyMapPrim();
|
||||
}
|
||||
|
||||
d->intl_.getTransManager().translateAndInsert(c, t, cur);
|
||||
|
@ -2090,9 +2090,7 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded)
|
||||
|
||||
Row const & real_next_row = tm.parMetrics(pit()).rows()[next_row];
|
||||
bool bound = false;
|
||||
pos_type const col = tm.getColumnNearX(pit(), real_next_row,
|
||||
xo, bound);
|
||||
top().pos() = real_next_row.pos() + col;
|
||||
top().pos() = tm.getPosNearX(real_next_row, xo, bound);
|
||||
boundary(bound);
|
||||
|
||||
updateNeeded |= bv().checkDepm(*this, old);
|
||||
|
221
src/Encoding.cpp
221
src/Encoding.cpp
@ -42,190 +42,6 @@ Encodings::MathSymbolSet Encodings::mathsym;
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
CharInfoMap::const_iterator const it = unicodesymbols.find(c);
|
||||
|
@ -255,29 +255,8 @@ public:
|
||||
///
|
||||
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.
|
||||
static CharInfo const & unicodeCharInfo(char_type c);
|
||||
///
|
||||
static char_type transformChar(char_type c, LetterForm form);
|
||||
/// Is this a combining char?
|
||||
static bool isCombiningChar(char_type c);
|
||||
/// Return the TIPA shortcut
|
||||
|
@ -58,7 +58,7 @@ namespace {
|
||||
|
||||
// The format should also be updated in configure.py, and conversion code
|
||||
// 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!
|
||||
LexerKeyword lyxrcTags[] = {
|
||||
@ -106,7 +106,6 @@ LexerKeyword lyxrcTags[] = {
|
||||
{ "\\example_path", LyXRC::RC_EXAMPLEPATH },
|
||||
{ "\\export_overwrite", LyXRC::RC_EXPORT_OVERWRITE },
|
||||
{ "\\font_encoding", LyXRC::RC_FONT_ENCODING },
|
||||
{ "\\force_paint_single_char", LyXRC::RC_FORCE_PAINT_SINGLE_CHAR },
|
||||
{ "\\format", LyXRC::RC_FILEFORMAT },
|
||||
{ "\\forward_search_dvi", LyXRC::RC_FORWARD_SEARCH_DVI },
|
||||
{ "\\forward_search_pdf", LyXRC::RC_FORWARD_SEARCH_PDF },
|
||||
@ -171,7 +170,6 @@ LexerKeyword lyxrcTags[] = {
|
||||
{ "\\print_to_file", LyXRC::RC_PRINTTOFILE },
|
||||
{ "\\print_to_printer", LyXRC::RC_PRINTTOPRINTER },
|
||||
{ "\\printer", LyXRC::RC_PRINTER },
|
||||
{ "\\rtl", LyXRC::RC_RTL_SUPPORT },
|
||||
{ "\\save_compressed", LyXRC::RC_SAVE_COMPRESSED },
|
||||
{ "\\screen_dpi", LyXRC::RC_SCREEN_DPI },
|
||||
{ "\\screen_font_roman", LyXRC::RC_SCREEN_FONT_ROMAN },
|
||||
@ -314,7 +312,6 @@ void LyXRC::setDefaults()
|
||||
completion_minlength = 6;
|
||||
spellcheck_notes = true;
|
||||
use_kbmap = false;
|
||||
rtl_support = true;
|
||||
visual_cursor = false;
|
||||
auto_number = true;
|
||||
mark_foreign_language = true;
|
||||
@ -447,9 +444,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
|
||||
if (!lexrc.isOK())
|
||||
return ReadError;
|
||||
|
||||
// default for current rowpainter capabilities
|
||||
force_paint_single_char = true;
|
||||
|
||||
// format prior to 2.0 and introduction of format tag
|
||||
unsigned int format = 0;
|
||||
|
||||
@ -555,10 +549,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
|
||||
lexrc >> fontenc;
|
||||
break;
|
||||
|
||||
case RC_FORCE_PAINT_SINGLE_CHAR:
|
||||
lexrc >> force_paint_single_char;
|
||||
break;
|
||||
|
||||
case RC_PRINTER:
|
||||
lexrc >> printer;
|
||||
break;
|
||||
@ -1050,9 +1040,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
|
||||
case RC_LANGUAGE_COMMAND_LOCAL:
|
||||
lexrc >> language_command_local;
|
||||
break;
|
||||
case RC_RTL_SUPPORT:
|
||||
lexrc >> rtl_support;
|
||||
break;
|
||||
case RC_VISUAL_CURSOR:
|
||||
lexrc >> visual_cursor;
|
||||
break;
|
||||
@ -2221,14 +2208,6 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
|
||||
if (tag != RC_LAST)
|
||||
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"
|
||||
<< "# FILE SECTION ######################################\n"
|
||||
<< "#\n\n";
|
||||
@ -2553,13 +2532,6 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
|
||||
if (tag != RC_LAST)
|
||||
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:
|
||||
if (ignore_system_lyxrc ||
|
||||
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_EXAMPLEPATH:
|
||||
case LyXRC::RC_FONT_ENCODING:
|
||||
case LyXRC::RC_FORCE_PAINT_SINGLE_CHAR:
|
||||
case LyXRC::RC_FILEFORMAT:
|
||||
case LyXRC::RC_GROUP_LAYOUTS:
|
||||
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_PRINT_ADAPTOUTPUT:
|
||||
case LyXRC::RC_PRINT_COMMAND:
|
||||
case LyXRC::RC_RTL_SUPPORT:
|
||||
case LyXRC::RC_SAVE_COMPRESSED:
|
||||
case LyXRC::RC_SCREEN_DPI:
|
||||
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.");
|
||||
break;
|
||||
|
||||
case RC_FORCE_PAINT_SINGLE_CHAR:
|
||||
str = _("Disable any kerning and ligatures for text drawing on screen.");
|
||||
break;
|
||||
|
||||
case RC_FILEFORMAT:
|
||||
break;
|
||||
|
||||
@ -3444,10 +3410,6 @@ string const LyXRC::getDescription(LyXRCTags tag)
|
||||
str = _("Your favorite print program, e.g. \"dvips\", \"dvilj4\".");
|
||||
break;
|
||||
|
||||
case RC_RTL_SUPPORT:
|
||||
str = _("Select to enable support of right-to-left languages (e.g. Hebrew, Arabic).");
|
||||
break;
|
||||
|
||||
case RC_VISUAL_CURSOR:
|
||||
str = _("Select to have visual bidi cursor movement, unselect for logical movement.");
|
||||
break;
|
||||
|
@ -80,7 +80,6 @@ public:
|
||||
RC_EXAMPLEPATH,
|
||||
RC_EXPORT_OVERWRITE,
|
||||
RC_FONT_ENCODING,
|
||||
RC_FORCE_PAINT_SINGLE_CHAR,
|
||||
RC_FILEFORMAT,
|
||||
RC_FORWARD_SEARCH_DVI,
|
||||
RC_FORWARD_SEARCH_PDF,
|
||||
@ -147,7 +146,6 @@ public:
|
||||
RC_PRINTTOPRINTER,
|
||||
RC_PRINT_ADAPTOUTPUT,
|
||||
RC_PRINT_COMMAND,
|
||||
RC_RTL_SUPPORT,
|
||||
RC_SAVE_COMPRESSED,
|
||||
RC_SCREEN_DPI,
|
||||
RC_SCREEN_FONT_ROMAN,
|
||||
@ -411,8 +409,6 @@ public:
|
||||
};
|
||||
///
|
||||
LangPackageSelection language_package_selection;
|
||||
///
|
||||
bool rtl_support;
|
||||
/// bidi cursor movement: true = visual, false = logical
|
||||
bool visual_cursor;
|
||||
///
|
||||
@ -552,8 +548,6 @@ public:
|
||||
///
|
||||
ScrollWheelZoom scroll_wheel_zoom;
|
||||
///
|
||||
bool force_paint_single_char;
|
||||
///
|
||||
int cursor_width;
|
||||
/// One of: yes, no, ask
|
||||
std::string close_buffer_with_last_view;
|
||||
|
@ -1928,7 +1928,7 @@ FontSize Paragraph::highestFontInRange
|
||||
char_type Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
|
||||
{
|
||||
char_type c = d->text_[pos];
|
||||
if (!lyxrc.rtl_support || !getFontSettings(bparams, pos).isRightToLeft())
|
||||
if (!getFontSettings(bparams, pos).isRightToLeft())
|
||||
return c;
|
||||
|
||||
// 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
|
||||
{
|
||||
Inset const * inset = getInset(pos);
|
||||
return inset && (inset->lyxCode() == SPACE_CODE &&
|
||||
inset->isStretchableSpace());
|
||||
return inset && inset->isHfill();
|
||||
}
|
||||
|
||||
|
||||
@ -3305,8 +3304,7 @@ Paragraph::getParLanguage(BufferParams const & bparams) const
|
||||
|
||||
bool Paragraph::isRTL(BufferParams const & bparams) const
|
||||
{
|
||||
return lyxrc.rtl_support
|
||||
&& getParLanguage(bparams)->rightToLeft()
|
||||
return getParLanguage(bparams)->rightToLeft()
|
||||
&& !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
|
||||
{
|
||||
// there is a problem if there is no bibitem at position 0 or
|
||||
|
@ -431,8 +431,6 @@ public:
|
||||
/// return true if we allow this par to stay empty
|
||||
bool allowEmpty() const;
|
||||
///
|
||||
char_type transformChar(char_type c, pos_type pos) const;
|
||||
///
|
||||
ParagraphParameters & params();
|
||||
///
|
||||
ParagraphParameters const & params() const;
|
||||
|
@ -190,8 +190,7 @@ void ParagraphMetrics::dump() const
|
||||
{
|
||||
lyxerr << "Paragraph::dump: rows.size(): " << rows_.size() << endl;
|
||||
for (size_t i = 0; i != rows_.size(); ++i) {
|
||||
lyxerr << " row " << i << ": ";
|
||||
rows_[i].dump();
|
||||
lyxerr << " row " << i << ": " << rows_[i];
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,27 +215,15 @@ int ParagraphMetrics::singleWidth(pos_type pos, Font const & font) const
|
||||
if (Inset const * inset = par_->getInset(pos))
|
||||
return insetDimension(inset).wid;
|
||||
|
||||
char_type c = par_->getChar(pos);
|
||||
char_type const c = par_->getChar(pos);
|
||||
|
||||
if (c == '\t')
|
||||
return 4 * theFontMetrics(font).width(' ');
|
||||
|
||||
if (!isPrintable(c))
|
||||
return theFontMetrics(font).width(c);
|
||||
|
||||
Language const * language = font.language();
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Note that this function is only called in
|
||||
// RowPainter::paintText, and only used for characters that do
|
||||
// not require handling of compose chars or ligatures. It can
|
||||
// therefore be kept simple.
|
||||
return theFontMetrics(font).width(c);
|
||||
}
|
||||
|
||||
|
373
src/Row.cpp
373
src/Row.cpp
@ -3,11 +3,11 @@
|
||||
* This file is part of LyX, the document processor.
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author unknown
|
||||
* \author Lars Gullik Bjønnes
|
||||
* \author John Levon
|
||||
* \author André Pönitz
|
||||
* \author Jürgen Vigna
|
||||
* \author Jean-Marc Lasgouttes
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*
|
||||
@ -20,17 +20,124 @@
|
||||
|
||||
#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 {
|
||||
|
||||
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()
|
||||
: separator(0), label_hfill(0), x(0),
|
||||
sel_beg(-1), sel_end(-1),
|
||||
begin_margin_sel(false), end_margin_sel(false),
|
||||
changed_(false), crc_(0), pos_(0), end_(0)
|
||||
: separator(0), label_hfill(0), x(0), right_margin(0),
|
||||
sel_beg(-1), sel_end(-1),
|
||||
begin_margin_sel(false), end_margin_sel(false),
|
||||
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,
|
||||
DocIterator const & end) const
|
||||
{
|
||||
@ -68,7 +157,7 @@ bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
|
||||
// Is the chosen margin selected ?
|
||||
if (sel_pos == margin_pos) {
|
||||
if (beg.pos() == end.pos())
|
||||
// This is a special case in which the space between after
|
||||
// This is a special case in which the space between after
|
||||
// pos i-1 and before pos i is selected, i.e. the margins
|
||||
// (see DocIterator::boundary_).
|
||||
return beg.boundary() && !end.boundary();
|
||||
@ -77,21 +166,21 @@ bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
|
||||
// drawn if the cursor is after the margin.
|
||||
return !end.boundary();
|
||||
else if (beg.pos() == margin_pos)
|
||||
// If the selection begins around the margin, it is
|
||||
// If the selection begins around the margin, it is
|
||||
// only drawn if the cursor is before the margin.
|
||||
return beg.boundary();
|
||||
else
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Row::setSelectionAndMargins(DocIterator const & beg,
|
||||
void Row::setSelectionAndMargins(DocIterator const & beg,
|
||||
DocIterator const & end) const
|
||||
{
|
||||
setSelection(beg.pos(), end.pos());
|
||||
|
||||
|
||||
if (selection()) {
|
||||
end_margin_sel = isMarginSelected(false, beg, end);
|
||||
begin_margin_sel = isMarginSelected(true, beg, end);
|
||||
@ -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_
|
||||
<< " width: " << dim_.wid
|
||||
<< " ascent: " << dim_.asc
|
||||
<< " descent: " << dim_.des);
|
||||
if (e.font.isVisibleRightToLeft())
|
||||
os << e.endpos << "<<" << e.pos << " ";
|
||||
else
|
||||
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
|
||||
|
203
src/Row.h
203
src/Row.h
@ -15,22 +15,116 @@
|
||||
#ifndef ROW_H
|
||||
#define ROW_H
|
||||
|
||||
#include "Changes.h"
|
||||
#include "Dimension.h"
|
||||
#include "Font.h"
|
||||
|
||||
#include "support/docstring.h"
|
||||
#include "support/types.h"
|
||||
|
||||
#include "Dimension.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class DocIterator;
|
||||
class Inset;
|
||||
|
||||
/**
|
||||
* An on-screen row of text. A paragraph is broken into a
|
||||
* RowList for display. Each Row contains position pointers
|
||||
* into the first and last character positions of that row.
|
||||
* 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
|
||||
* of the line.
|
||||
*/
|
||||
class Row {
|
||||
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();
|
||||
///
|
||||
@ -49,22 +143,27 @@ public:
|
||||
bool selection() const;
|
||||
/// Set the selection begin and end and whether the left and/or right
|
||||
/// margins are selected.
|
||||
void setSelectionAndMargins(DocIterator const & beg,
|
||||
void setSelectionAndMargins(DocIterator const & beg,
|
||||
DocIterator const & end) const;
|
||||
|
||||
|
||||
///
|
||||
void pos(pos_type p);
|
||||
void pos(pos_type p) { pos_ = p; }
|
||||
///
|
||||
pos_type pos() const { return pos_; }
|
||||
///
|
||||
void endpos(pos_type p);
|
||||
void endpos(pos_type p) { end_ = p; }
|
||||
///
|
||||
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 & dimension() { return dim_; }
|
||||
///
|
||||
int height() const { return dim_.height(); }
|
||||
///
|
||||
int width() const { return dim_.wid; }
|
||||
@ -73,15 +172,81 @@ public:
|
||||
///
|
||||
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)
|
||||
double separator;
|
||||
/// width of hfills in the label
|
||||
double label_hfill;
|
||||
/// the x position of the row
|
||||
/// the x position of the row (left margin)
|
||||
double x;
|
||||
/// the right margin of the row
|
||||
int right_margin;
|
||||
///
|
||||
mutable pos_type sel_beg;
|
||||
///
|
||||
@ -101,6 +266,16 @@ private:
|
||||
bool isMarginSelected(bool left_margin, DocIterator const & beg,
|
||||
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?
|
||||
mutable bool changed_;
|
||||
/// CRC of row contents.
|
||||
@ -109,6 +284,8 @@ private:
|
||||
pos_type pos_;
|
||||
/// one behind last pos covered by this row
|
||||
pos_type end_;
|
||||
// Is there is a boundary at the end of the row (display inset...)
|
||||
bool right_boundary_;
|
||||
/// Row dimension.
|
||||
Dimension dim_;
|
||||
};
|
||||
|
1016
src/TextMetrics.cpp
1016
src/TextMetrics.cpp
File diff suppressed because it is too large
Load Diff
@ -105,30 +105,21 @@ public:
|
||||
|
||||
///
|
||||
int maxWidth() const { return max_width_; }
|
||||
///
|
||||
int singleWidth(pit_type const pit, pos_type pos) const;
|
||||
|
||||
///
|
||||
int rightMargin(ParagraphMetrics const & pm) 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 drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) const;
|
||||
|
||||
/// Returns the height of the row (width member is set to 0).
|
||||
/// If \c topBottomSpace is true, extra space is added for the
|
||||
/// top and bottom row.
|
||||
Dimension rowHeight(
|
||||
pit_type const pit,
|
||||
pos_type const first,
|
||||
pos_type const end,
|
||||
bool topBottomSpace = true) const;
|
||||
void setRowHeight(Row & row, pit_type const pit,
|
||||
bool topBottomSpace = true) const;
|
||||
|
||||
private:
|
||||
///
|
||||
@ -142,19 +133,13 @@ private:
|
||||
|
||||
/// sets row.end to the pos value *after* which a row should break.
|
||||
/// for example, the pos after which isNewLine(pos) == true
|
||||
pos_type rowBreakPoint(
|
||||
int width,
|
||||
pit_type const pit,
|
||||
pos_type first
|
||||
) const;
|
||||
void breakRow(Row & row, int right_margin, pit_type const pit) const;
|
||||
|
||||
/// returns the minimum space a row needs on the screen in pixel
|
||||
int rowWidth(
|
||||
int right_margin,
|
||||
pit_type const pit,
|
||||
pos_type const first,
|
||||
pos_type const end
|
||||
) const;
|
||||
// Expand the alignment of paragraph \param par at position \param pos
|
||||
int getAlign(Paragraph const & par, pos_type pos) 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;
|
||||
|
||||
// Helper function for the other checkInsetHit method.
|
||||
InsetList::InsetTable * checkInsetHit(pit_type pit, int x, int y);
|
||||
@ -162,11 +147,10 @@ private:
|
||||
|
||||
// Temporary 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
|
||||
/// of this column.
|
||||
pos_type getColumnNearX(pit_type pit, Row const & row, int & x,
|
||||
bool & boundary) const;
|
||||
pos_type getPosNearX(Row const & row, int & x, bool & boundary) const;
|
||||
|
||||
/// returns pos in given par at given x coord.
|
||||
pos_type x2pos(pit_type pit, int row, int x) const;
|
||||
|
@ -77,6 +77,19 @@ public:
|
||||
virtual int width(docstring const & s) const = 0;
|
||||
/// FIXME ??
|
||||
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.
|
||||
virtual Dimension const dimension(char_type c) const = 0;
|
||||
/**
|
||||
|
@ -14,9 +14,11 @@
|
||||
#define PAINTER_H
|
||||
|
||||
#include "support/strfwd.h"
|
||||
#include "support/types.h"
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Font;
|
||||
class FontInfo;
|
||||
|
||||
namespace graphics { class Image; }
|
||||
@ -106,11 +108,25 @@ public:
|
||||
virtual void image(int x, int y, int w, int h,
|
||||
graphics::Image const & image) = 0;
|
||||
|
||||
/// draw a string at position x, y (y is the baseline)
|
||||
/**
|
||||
* \return the width of the drawn text.
|
||||
*/
|
||||
virtual int text(int x, int y, docstring const & str, FontInfo const & f) = 0;
|
||||
/** 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.
|
||||
*/
|
||||
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)
|
||||
{ drawing_enabled_ = drawing_enabled; }
|
||||
|
@ -2417,7 +2417,6 @@ void GuiApplication::restoreGuiSession()
|
||||
QString const GuiApplication::romanFontName()
|
||||
{
|
||||
QFont font;
|
||||
font.setKerning(false);
|
||||
font.setStyleHint(QFont::Serif);
|
||||
font.setFamily("serif");
|
||||
|
||||
@ -2428,7 +2427,6 @@ QString const GuiApplication::romanFontName()
|
||||
QString const GuiApplication::sansFontName()
|
||||
{
|
||||
QFont font;
|
||||
font.setKerning(false);
|
||||
font.setStyleHint(QFont::SansSerif);
|
||||
font.setFamily("sans");
|
||||
|
||||
@ -2439,7 +2437,6 @@ QString const GuiApplication::sansFontName()
|
||||
QString const GuiApplication::typewriterFontName()
|
||||
{
|
||||
QFont font;
|
||||
font.setKerning(false);
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
font.setFamily("monospace");
|
||||
|
||||
|
@ -165,7 +165,6 @@ QFont symbolFont(QString const & family, bool * ok)
|
||||
upper[0] = family[0].toUpper();
|
||||
|
||||
QFont font;
|
||||
font.setKerning(false);
|
||||
font.setFamily(family);
|
||||
|
||||
if (isChosenFont(font, family)) {
|
||||
@ -256,7 +255,6 @@ static QString makeFontName(QString const & family, QString const & foundry)
|
||||
GuiFontInfo::GuiFontInfo(FontInfo const & f)
|
||||
: metrics(QFont())
|
||||
{
|
||||
font.setKerning(false);
|
||||
QString const pat = symbolFamily(f.family());
|
||||
if (!pat.isEmpty()) {
|
||||
bool ok;
|
||||
|
@ -15,14 +15,18 @@
|
||||
|
||||
#include "qt_helpers.h"
|
||||
|
||||
#include "Language.h"
|
||||
#include "Dimension.h"
|
||||
#include "Language.h"
|
||||
#include "LyXRC.h"
|
||||
|
||||
#include "insets/Inset.h"
|
||||
|
||||
#include "support/lassert.h"
|
||||
|
||||
#include <QTextLayout>
|
||||
|
||||
using namespace std;
|
||||
using namespace lyx::support;
|
||||
|
||||
namespace lyx {
|
||||
namespace frontend {
|
||||
@ -39,7 +43,7 @@ namespace {
|
||||
* why this works well for symbol fonts used in mathed too, even though
|
||||
* these are not real ucs4 characters. These are codepoints in the
|
||||
* 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)
|
||||
{
|
||||
@ -49,7 +53,7 @@ inline QChar const ucs4_to_qchar(char_type const ucs4)
|
||||
} // 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
|
||||
{
|
||||
size_t ls = s.size();
|
||||
int w = 0;
|
||||
for (unsigned int i = 0; i < ls; ++i) {
|
||||
//FIXME: we need to detect surrogate pairs and act accordingly
|
||||
/**
|
||||
if isSurrogateBase(s[i]) {
|
||||
docstring c = s[i];
|
||||
w += metrics_.width(toqstr(c + s[i + 1]));
|
||||
++i;
|
||||
}
|
||||
else
|
||||
*/
|
||||
w += width(s[i]);
|
||||
map<docstring, int>::const_iterator it = strwidth_cache_.find(s);
|
||||
if (it != strwidth_cache_.end()) {
|
||||
w = it->second;
|
||||
} else {
|
||||
w = metrics_.width(toqstr(s));
|
||||
strwidth_cache_[s] = w;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
@ -146,6 +143,39 @@ int GuiFontMetrics::signedWidth(docstring const & s) const
|
||||
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,
|
||||
int & w, int & ascent, int & descent) const
|
||||
|
@ -16,6 +16,9 @@
|
||||
|
||||
#include "support/docstring.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QHash>
|
||||
|
||||
@ -39,6 +42,8 @@ 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 Dimension const dimension(char_type c) const;
|
||||
|
||||
virtual void rectText(docstring const & str,
|
||||
@ -53,12 +58,19 @@ public:
|
||||
int width(QString const & str) const;
|
||||
|
||||
private:
|
||||
/// The font
|
||||
QFont font_;
|
||||
|
||||
/// Metrics on the font
|
||||
QFontMetrics metrics_;
|
||||
|
||||
/// Cache of char widths
|
||||
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 {
|
||||
int ascent;
|
||||
int descent;
|
||||
|
@ -132,7 +132,6 @@ GuiLog::GuiLog(GuiView & lv)
|
||||
|
||||
logTB->setReadOnly(true);
|
||||
QFont font(guiApp->typewriterFontName());
|
||||
font.setKerning(false);
|
||||
font.setFixedPitch(true);
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
logTB->setFont(font);
|
||||
|
@ -9,6 +9,10 @@
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
|
||||
#ifdef Q_WS_MAC
|
||||
#define USE_RTL_OVERRIDE 1
|
||||
#endif
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "GuiPainter.h"
|
||||
@ -20,7 +24,7 @@
|
||||
#include "GuiImage.h"
|
||||
#include "qt_helpers.h"
|
||||
|
||||
#include "FontInfo.h"
|
||||
#include "Font.h"
|
||||
#include "Language.h"
|
||||
#include "LyXRC.h"
|
||||
|
||||
@ -41,6 +45,7 @@
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace lyx::support;
|
||||
|
||||
namespace lyx {
|
||||
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)
|
||||
{
|
||||
docstring s(1, c);
|
||||
return text(x, y, s, f);
|
||||
return text(x, y, docstring(1, c), f);
|
||||
}
|
||||
|
||||
|
||||
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())
|
||||
return 0;
|
||||
|
||||
@ -302,27 +307,24 @@ int GuiPainter::text(int x, int y, docstring const & s,
|
||||
str = ' ' + str;
|
||||
#endif
|
||||
|
||||
QFont const & ff = getFont(f);
|
||||
GuiFontMetrics const & fm = getFontMetrics(f);
|
||||
|
||||
int textwidth;
|
||||
QFont const & ff = getFont(f);
|
||||
GuiFontMetrics const & fm = getFontMetrics(f);
|
||||
|
||||
// Here we use the font width cache instead of
|
||||
// textwidth = fontMetrics().width(str);
|
||||
// because the above is awfully expensive on MacOSX
|
||||
textwidth = fm.width(s);
|
||||
textDecoration(f, x, y, textwidth);
|
||||
int const textwidth = fm.width(s);
|
||||
|
||||
if (!isDrawingEnabled())
|
||||
return textwidth;
|
||||
|
||||
textDecoration(f, x, y, textwidth);
|
||||
|
||||
// Qt4 does not display a glyph whose codepoint is the
|
||||
// same as that of a soft-hyphen (0x00ad), unless it
|
||||
// occurs at a line-break. As a kludge, we force Qt to
|
||||
// render this glyph using a one-column line.
|
||||
// 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?
|
||||
// I don't think so (i.e., Qt is correct as far as
|
||||
// texted is concerned). /spitz
|
||||
@ -391,15 +393,78 @@ int GuiPainter::text(int x, int y, docstring const & s,
|
||||
setQPainterPen(computeColor(f.realColor()));
|
||||
if (font() != 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);
|
||||
#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())
|
||||
// << " at " << x << "," << y);
|
||||
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)
|
||||
{
|
||||
if (f.underbar() == FONT_ON)
|
||||
|
@ -86,9 +86,25 @@ public:
|
||||
virtual void image(int x, int y, int w, int h,
|
||||
lyx::graphics::Image const & image);
|
||||
|
||||
/// draw a string at position x, y (y is the baseline)
|
||||
virtual int text(int x, int y,
|
||||
docstring const & str, FontInfo const & f);
|
||||
/** 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.
|
||||
*/
|
||||
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)
|
||||
virtual int text(int x, int y, char_type c, FontInfo const & f);
|
||||
|
@ -314,7 +314,6 @@ static void setComboxFont(QComboBox * cb, string const & family,
|
||||
// for bug 1063.
|
||||
|
||||
QFont font;
|
||||
font.setKerning(false);
|
||||
|
||||
QString const font_family = toqstr(family);
|
||||
if (font_family == guiApp->romanFontName()) {
|
||||
@ -2263,8 +2262,6 @@ PrefLanguage::PrefLanguage(GuiPreferences * form)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
connect(rtlGB, SIGNAL(clicked()),
|
||||
this, SIGNAL(changed()));
|
||||
connect(visualCursorRB, SIGNAL(clicked()),
|
||||
this, SIGNAL(changed()));
|
||||
connect(logicalCursorRB, SIGNAL(clicked()),
|
||||
@ -2348,9 +2345,7 @@ void PrefLanguage::on_languagePackageCO_currentIndexChanged(int i)
|
||||
|
||||
void PrefLanguage::apply(LyXRC & rc) const
|
||||
{
|
||||
// FIXME: remove rtl_support bool
|
||||
rc.rtl_support = rtlGB->isChecked();
|
||||
rc.visual_cursor = rtlGB->isChecked() && visualCursorRB->isChecked();
|
||||
rc.visual_cursor = visualCursorRB->isChecked();
|
||||
rc.mark_foreign_language = markForeignCB->isChecked();
|
||||
rc.language_auto_begin = autoBeginCB->isChecked();
|
||||
rc.language_auto_end = autoEndCB->isChecked();
|
||||
@ -2376,8 +2371,6 @@ void PrefLanguage::apply(LyXRC & rc) const
|
||||
|
||||
void PrefLanguage::update(LyXRC const & rc)
|
||||
{
|
||||
// FIXME: remove rtl_support bool
|
||||
rtlGB->setChecked(rc.rtl_support);
|
||||
if (rc.visual_cursor)
|
||||
visualCursorRB->setChecked(true);
|
||||
else
|
||||
|
@ -61,7 +61,6 @@ GuiProgressView::GuiProgressView(GuiView & parent, Qt::DockWidgetArea area,
|
||||
setWidget(widget_);
|
||||
|
||||
QFont font(guiApp->typewriterFontName());
|
||||
font.setKerning(false);
|
||||
font.setFixedPitch(true);
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
widget_->outTE->setFont(font);
|
||||
|
@ -72,7 +72,6 @@ ViewSourceWidget::ViewSourceWidget()
|
||||
///dialog_->viewSourceTV->setAcceptRichText(false);
|
||||
// this is personal. I think source code should be in fixed-size font
|
||||
QFont font(guiApp->typewriterFontName());
|
||||
font.setKerning(false);
|
||||
font.setFixedPitch(true);
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
viewSourceTV->setFont(font);
|
||||
|
@ -998,11 +998,11 @@ void GuiWorkArea::generateSyntheticMouseEvent()
|
||||
break;
|
||||
yy += h;
|
||||
}
|
||||
|
||||
|
||||
// Find the position of the cursor
|
||||
bool bound;
|
||||
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
|
||||
cur.pit() = pit;
|
||||
|
@ -224,16 +224,6 @@
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<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 &RTL support</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
|
@ -431,8 +431,8 @@ public:
|
||||
/// is this equivalent to a space (which is BTW different from
|
||||
/// a line separator)?
|
||||
virtual bool isSpace() const { return false; }
|
||||
/// is this an expandible space (rubber length)?
|
||||
virtual bool isStretchableSpace() const { return false; }
|
||||
/// does this inset try to use all available space (like \\hfill does)?
|
||||
virtual bool isHfill() const { return false; }
|
||||
|
||||
enum DisplayType {
|
||||
Inline = 0,
|
||||
|
@ -620,16 +620,4 @@ void InsetIPAChar::validate(LaTeXFeatures & features) const
|
||||
}
|
||||
|
||||
|
||||
bool InsetIPAChar::isLetter() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool InsetIPAChar::isLineSeparator() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -167,9 +167,7 @@ public:
|
||||
/// should this inset be handled like a normal character?
|
||||
bool isChar() const { return true; }
|
||||
/// is this equivalent to a letter?
|
||||
bool isLetter() const;
|
||||
/// should we break lines after this inset?
|
||||
bool isLineSeparator() const;
|
||||
bool isLetter() const { return true; }
|
||||
private:
|
||||
Inset * clone() const { return new InsetIPAChar(*this); }
|
||||
|
||||
|
@ -199,7 +199,7 @@ int const arrow_size = 8;
|
||||
|
||||
void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
{
|
||||
if (isStretchableSpace()) {
|
||||
if (isHfill()) {
|
||||
// The metrics for this kinds are calculated externally in
|
||||
// \c TextMetrics::computeRowMetrics. Those are dummy value:
|
||||
dim = Dimension(10, 10, 10);
|
||||
@ -240,7 +240,7 @@ void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
break;
|
||||
case InsetSpaceParams::CUSTOM:
|
||||
case InsetSpaceParams::CUSTOM_PROTECTED: {
|
||||
int const w =
|
||||
int const w =
|
||||
params_.length.len().inPixels(mi.base.textwidth,
|
||||
fm.width(char_type('M')));
|
||||
int const minw = (w < 0) ? 3 * arrow_size : 4;
|
||||
@ -267,7 +267,7 @@ void InsetSpace::draw(PainterInfo & pi, int x, int y) const
|
||||
{
|
||||
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 desc = theFontMetrics(pi.base.font).descent('M');
|
||||
// Pixel height divisible by 2 for prettier fill graphics:
|
||||
@ -459,7 +459,7 @@ void InsetSpaceParams::write(ostream & os) const
|
||||
os << "\\hspace*{}";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!length.len().empty())
|
||||
os << "\n\\length " << length.asString();
|
||||
}
|
||||
@ -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
|
||||
|| params_.kind == InsetSpaceParams::HFILL_PROTECTED
|
||||
|
@ -142,9 +142,8 @@ public:
|
||||
bool clickable(int, int) const { return true; }
|
||||
///
|
||||
InsetCode lyxCode() const { return SPACE_CODE; }
|
||||
/// is this an expandible space (rubber length)?
|
||||
bool isStretchableSpace() const;
|
||||
|
||||
/// does this inset try to use all available space (like \\hfill does)?
|
||||
bool isHfill() const;
|
||||
/// should this inset be handled like a normal character?
|
||||
bool isChar() const { return true; }
|
||||
/// is this equivalent to a letter?
|
||||
|
@ -14,14 +14,12 @@
|
||||
|
||||
#include "rowpainter.h"
|
||||
|
||||
#include "Bidi.h"
|
||||
#include "Buffer.h"
|
||||
#include "CoordCache.h"
|
||||
#include "Cursor.h"
|
||||
#include "BufferParams.h"
|
||||
#include "BufferView.h"
|
||||
#include "Changes.h"
|
||||
#include "Encoding.h"
|
||||
#include "Language.h"
|
||||
#include "Layout.h"
|
||||
#include "LyXRC.h"
|
||||
@ -56,13 +54,12 @@ using frontend::FontMetrics;
|
||||
|
||||
|
||||
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),
|
||||
text_metrics_(pi_.base.bv->textMetrics(&text)),
|
||||
pars_(text.paragraphs()),
|
||||
row_(row), pit_(pit), par_(text.paragraphs()[pit]),
|
||||
pm_(text_metrics_.parMetrics(pit)),
|
||||
bidi_(bidi), change_(pi_.change_),
|
||||
pm_(text_metrics_.parMetrics(pit)), change_(pi_.change_),
|
||||
xo_(x), yo_(y), width_(text_metrics_.width()),
|
||||
solid_line_thickness_(1.0), solid_line_offset_(1),
|
||||
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)
|
||||
{
|
||||
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)
|
||||
void RowPainter::paintChars(pos_type & vpos, Font const & font)
|
||||
{
|
||||
// This method takes up 70% of time when typing
|
||||
pos_type pos = bidi_.vis2log(vpos);
|
||||
pos_type start_pos = pos;
|
||||
// first character
|
||||
char_type prev_char = par_.getChar(pos);
|
||||
vector<char_type> str;
|
||||
docstring str;
|
||||
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);
|
||||
// Track-change status.
|
||||
Change const & change_running = par_.lookupChange(pos);
|
||||
|
||||
// selected text?
|
||||
bool const selection = (pos >= row_.sel_beg && pos < row_.sel_end)
|
||||
|| pi_.selected;
|
||||
|
||||
// spelling correct?
|
||||
bool const spell_state =
|
||||
lyxrc.spellcheck_continuously && par_.isMisspelled(pos);
|
||||
|
||||
// collect as much similar chars as we can
|
||||
pos_type const end = row_.endpos();
|
||||
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);
|
||||
if (pos < font_span.first || pos > font_span.last)
|
||||
break;
|
||||
|
||||
bool const new_selection = pos >= row_.sel_beg && pos < row_.sel_end;
|
||||
if (new_selection != selection)
|
||||
// Selection ends or starts here.
|
||||
if (!font_span.inside(pos))
|
||||
break;
|
||||
|
||||
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.
|
||||
break;
|
||||
|
||||
char_type c = par_.getChar(pos);
|
||||
char_type const c = par_.getChar(pos);
|
||||
|
||||
if (c == '\t')
|
||||
break;
|
||||
@ -303,65 +205,64 @@ void RowPainter::paintChars(pos_type & vpos, FontInfo const & font,
|
||||
if (!isPrintableNonspace(c))
|
||||
break;
|
||||
|
||||
/* 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 don't collect Hebrew/
|
||||
* Arabic characters, but rather paint them one at a time.
|
||||
* See also http://thread.gmane.org/gmane.editors.lyx.devel/79740
|
||||
*/
|
||||
if (hebrew)
|
||||
break;
|
||||
|
||||
/* FIXME: these checks are irrelevant, since 'arabic' and
|
||||
* 'hebrew' alone are already going to trigger a break.
|
||||
* However, this should not be removed completely, because
|
||||
* 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);
|
||||
prev_char = c;
|
||||
}
|
||||
|
||||
docstring s(&str[0], str.size());
|
||||
// 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);
|
||||
|
||||
if (s[0] == '\t')
|
||||
s.replace(0,1,from_ascii(" "));
|
||||
// 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 (!selection && !change_running.changed()) {
|
||||
x_ += pi_.pain.text(int(x_), yo_, s, font);
|
||||
return;
|
||||
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 force LTR drawing.
|
||||
* 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);
|
||||
}
|
||||
|
||||
// at least part of text selected?
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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_;
|
||||
|
||||
// usual characters, no insets
|
||||
char_type const c = par_.getChar(pos);
|
||||
paintChars(vpos, font);
|
||||
paintForeignMark(orig_x, font.language());
|
||||
|
||||
// special case languages
|
||||
string const & lang = orig_font.language()->lang();
|
||||
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) {
|
||||
// Paint the spelling mark if needed.
|
||||
if (lyxrc.spellcheck_continuously && par_.isMisspelled(pos)) {
|
||||
// check for cursor position
|
||||
// don't draw misspelled marker for words at cursor position
|
||||
// we don't want to disturb the process of text editing
|
||||
@ -757,11 +636,10 @@ 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();
|
||||
FontMetrics const & fm = theFontMetrics(f);
|
||||
FontInfo f = FontInfo(text_.layoutFont(pit_));
|
||||
f.setColor(Color_paragraphmarker);
|
||||
pi_.pain.text(int(x_), yo_, s, f);
|
||||
x_ += fm.width(s);
|
||||
x_ += theFontMetrics(f).width(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -800,6 +678,7 @@ void RowPainter::paintOnlyInsets()
|
||||
|
||||
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
|
||||
@ -857,23 +736,21 @@ void RowPainter::paintText()
|
||||
}
|
||||
|
||||
// Use font span to speed things up, see above
|
||||
if (vpos < font_span.first || vpos > font_span.last) {
|
||||
font_span = par_.fontSpan(vpos);
|
||||
font = text_metrics_.displayFont(pit_, vpos);
|
||||
if (!font_span.inside(pos)) {
|
||||
font_span = par_.fontSpan(pos);
|
||||
font = text_metrics_.displayFont(pit_, pos);
|
||||
|
||||
// split font span if inline completion is inside
|
||||
if (font_span.first <= inlineCompletionVPos
|
||||
&& font_span.last > inlineCompletionVPos)
|
||||
font_span.last = inlineCompletionVPos;
|
||||
if (inlineCompletionVPos != -1
|
||||
&& font_span.inside(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);
|
||||
|
||||
if (x_ + width_pos < 0) {
|
||||
x_ += width_pos;
|
||||
++vpos;
|
||||
continue;
|
||||
}
|
||||
Change const & change = par_.lookupChange(pos);
|
||||
if (change.changed() && !change_running.changed()) {
|
||||
change_running = change;
|
||||
@ -908,6 +785,7 @@ void RowPainter::paintText()
|
||||
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;
|
||||
}
|
||||
|
||||
@ -918,6 +796,7 @@ void RowPainter::paintText()
|
||||
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;
|
||||
|
@ -14,13 +14,13 @@
|
||||
#ifndef ROWPAINTER_H
|
||||
#define ROWPAINTER_H
|
||||
|
||||
#include "Bidi.h"
|
||||
#include "Changes.h"
|
||||
|
||||
#include "support/types.h"
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Bidi;
|
||||
class BufferView;
|
||||
class Font;
|
||||
class FontInfo;
|
||||
@ -36,6 +36,21 @@ 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.
|
||||
@ -44,7 +59,7 @@ class RowPainter {
|
||||
public:
|
||||
/// initialise and run painter
|
||||
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
|
||||
/// FIXME: transfer to TextMetrics
|
||||
@ -61,10 +76,7 @@ private:
|
||||
void paintSeparator(double orig_x, double width, FontInfo const & font);
|
||||
void paintForeignMark(double orig_x, Language const * lang, int desc = 0);
|
||||
void paintMisspelledMark(double orig_x, bool changed);
|
||||
void paintHebrewComposeChar(pos_type & vpos, FontInfo const & font);
|
||||
void paintArabicComposeChar(pos_type & vpos, FontInfo const & font);
|
||||
void paintChars(pos_type & vpos, FontInfo const & font,
|
||||
bool hebrew, bool arabic);
|
||||
void paintChars(pos_type & vpos, Font const & font);
|
||||
int paintAppendixStart(int y);
|
||||
void paintFromPos(pos_type & vpos, bool changed);
|
||||
void paintInset(Inset const * inset, pos_type const pos);
|
||||
@ -98,10 +110,8 @@ private:
|
||||
Paragraph const & par_;
|
||||
ParagraphMetrics const & pm_;
|
||||
|
||||
/// bidi cache, comes from outside the rowpainter because
|
||||
/// rowpainters are normally created in a for loop and there only
|
||||
/// one of them is active at a time.
|
||||
Bidi & bidi_;
|
||||
/// bidi cache
|
||||
Bidi bidi_;
|
||||
|
||||
/// row changed? (change tracking)
|
||||
Change const change_;
|
||||
|
Loading…
Reference in New Issue
Block a user