X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetSpecialChar.cpp;h=2614f9a2ebe3aa6788f0767daf7af30d5bd152a0;hb=71b8019d56142397994e0ef2d4244ada51f199d5;hp=77792b973cd02e228b927d2f46e29d9d886fa4c4;hpb=3e9e6e344ace9160f93fd882bf2243d24a069fa5;p=lyx.git diff --git a/src/insets/InsetSpecialChar.cpp b/src/insets/InsetSpecialChar.cpp index 77792b973c..2614f9a2eb 100644 --- a/src/insets/InsetSpecialChar.cpp +++ b/src/insets/InsetSpecialChar.cpp @@ -16,10 +16,12 @@ #include "Dimension.h" #include "Font.h" +#include "Language.h" #include "LaTeXFeatures.h" #include "Lexer.h" #include "MetricsInfo.h" #include "output_xhtml.h" +#include "texstream.h" #include "frontends/FontMetrics.h" #include "frontends/Painter.h" @@ -43,15 +45,98 @@ InsetSpecialChar::Kind InsetSpecialChar::kind() const } +namespace { + +int logoWidth(FontInfo const & font, InsetSpecialChar::Kind kind) { + frontend::FontMetrics const & fm = theFontMetrics(font); + int const em = fm.em(); + int width = 0; + // See drawlogo() below to understand what this does. + switch (kind) { + case InsetSpecialChar::PHRASE_LYX: + width = fm.width(from_ascii("L")) - em / 6 + + fm.width(from_ascii("Y")) - em / 8 + + fm.width(from_ascii("X")); + break; + + case InsetSpecialChar::PHRASE_TEX: + width = fm.width(from_ascii("T")) - em / 6 + + fm.width(from_ascii("E")) - em / 8 + + fm.width(from_ascii("X")); + break; + + case InsetSpecialChar::PHRASE_LATEX2E: + width = logoWidth(font, InsetSpecialChar::PHRASE_LATEX) + + 3 * em / 20 + + fm.width(from_ascii("2") + char_type(0x03b5)); + break; + case InsetSpecialChar::PHRASE_LATEX: { + FontInfo smaller = font; + smaller.decSize().decSize(); + width = fm.width(from_ascii("L")) - 9 * em / 25 + + theFontMetrics(smaller).width(from_ascii("A")) - 3 * em / 20 + + logoWidth(font, InsetSpecialChar::PHRASE_TEX); + break; + } + default: + LYXERR0("No information for computing width of logo " << kind); + } + + return width; +} + +} // namespace + +docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const +{ + docstring message; + switch (kind_) { + case ALLOWBREAK: + message = from_ascii("Optional Line Break (ZWSP)"); + break; + case LIGATURE_BREAK: + message = from_ascii("Ligature Break (ZWNJ)"); + break; + case END_OF_SENTENCE: + message = from_ascii("End of Sentence"); + break; + case HYPHENATION: + message = from_ascii("Hyphenation Point"); + break; + case SLASH: + message = from_ascii("Breakable Slash"); + break; + case NOBREAKDASH: + message = from_ascii("Protected Hyphen (SHY)"); + break; + case LDOTS: + case MENU_SEPARATOR: + case PHRASE_LYX: + case PHRASE_TEX: + case PHRASE_LATEX2E: + case PHRASE_LATEX: + // no tooltip for these ones. + break; + } + return message; +} + + void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const { frontend::FontMetrics const & fm = theFontMetrics(mi.base.font); dim.asc = fm.maxAscent(); - dim.des = fm.maxDescent(); + dim.des = 0; + dim.wid = 0; docstring s; switch (kind_) { + case ALLOWBREAK: + dim.asc = fm.ascent('x'); + dim.des = fm.descent('g'); + dim.wid = fm.em() / 8; + break; case LIGATURE_BREAK: s = from_ascii("|"); break; @@ -62,38 +147,117 @@ void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const s = from_ascii(". . ."); break; case MENU_SEPARATOR: - s = from_ascii(" x "); + // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE + // There is a \thinspace on each side of the triangle + dim.wid = 2 * fm.em() / 6 + fm.width(char_type(0x25B9)); break; case HYPHENATION: - s = from_ascii("-"); + dim.wid = fm.width(from_ascii("-")); + if (dim.wid > 5) + dim.wid -= 2; // to make it look shorter break; case SLASH: s = from_ascii("/"); + dim.des = fm.descent(s[0]); break; case NOBREAKDASH: s = from_ascii("-"); break; case PHRASE_LYX: - s = from_ascii("LyX"); - break; case PHRASE_TEX: - s = from_ascii("TeX"); - break; case PHRASE_LATEX2E: - s = from_ascii("LaTeX2") + char_type(0x03b5); - break; case PHRASE_LATEX: - s = from_ascii("LaTeX"); + dim.asc = fm.maxAscent(); + dim.des = fm.maxDescent(); + dim.wid = logoWidth(mi.base.font, kind_); break; } - dim.wid = fm.width(s); - if (kind_ == HYPHENATION && dim.wid > 5) - dim.wid -= 2; // to make it look shorter - - setDimCache(mi, dim); + if (dim.wid == 0) + dim.wid = fm.width(s); } +namespace { + +// helper function: draw text and update x. +void drawChar(PainterInfo & pi, int & x, int const y, char_type ch) +{ + FontInfo font = pi.base.font; + font.setPaintColor(pi.textColor(font.realColor())); + pi.pain.text(x, y, ch, font); + x += theFontMetrics(font).width(ch); +} + + +void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind) +{ + FontInfo const & font = pi.base.font; + int const em = theFontMetrics(font).em(); + switch (kind) { + case InsetSpecialChar::PHRASE_LYX: + /** Reference macro: + * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@}; + */ + drawChar(pi, x, y, 'L'); + x -= em / 6; + drawChar(pi, x, y + em / 4, 'Y'); + x -= em / 8; + drawChar(pi, x, y, 'X'); + break; + + case InsetSpecialChar::PHRASE_TEX: { + /** Reference macro: + * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@} + */ + int const ex = theFontMetrics(font).ascent('x'); + drawChar(pi, x, y, 'T'); + x -= em / 6; + drawChar(pi, x, y + ex / 2, 'E'); + x -= em / 8; + drawChar(pi, x, y, 'X'); + break; + } + case InsetSpecialChar::PHRASE_LATEX2E: + /** Reference macro: + * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th + * \if b\expandafter\@car\f@series\@nil\boldmath\fi + * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}} + */ + drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX); + x += 3 * em / 20; + drawChar(pi, x, y, '2'); + drawChar(pi, x, y + em / 4, char_type(0x03b5)); + break; + + case InsetSpecialChar::PHRASE_LATEX: { + /** Reference macro: + * \DeclareRobustCommand{\LaTeX}{L\kern-.36em% + * {\sbox\z@ T% + * \vbox to\ht\z@{\hbox{\check@mathfonts + * \fontsize\sf@size\z@ + * \math@fontsfalse\selectfont + * A}% + * \vss}% + * }% + * \kern-.15em% + * \TeX} + */ + drawChar(pi, x, y, 'L'); + x -= 9 * em / 25; + PainterInfo pi2 = pi; + pi2.base.font.decSize().decSize(); + drawChar(pi2, x, y - em / 5, 'A'); + x -= 3 * em / 20; + drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX); + break; + } + default: + LYXERR0("No information for drawing logo " << kind); + } +} + +} // namespace + void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const { FontInfo font = pi.base.font; @@ -105,6 +269,18 @@ void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const pi.pain.text(x, y, char_type('-'), font); break; } + case ALLOWBREAK: + { + // A small vertical line + int const asc = theFontMetrics(pi.base.font).ascent('x'); + int const desc = theFontMetrics(pi.base.font).descent('g'); + int const x0 = x; // x + 1; // FIXME: incline, + int const x1 = x; // x - 1; // similar to LibreOffice? + int const y0 = y + desc; + int const y1 = y - asc / 3; + pi.pain.line(x0, y1, x1, y0, Color_special); + break; + } case LIGATURE_BREAK: { font.setColor(Color_special); @@ -130,18 +306,13 @@ void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const frontend::FontMetrics const & fm = theFontMetrics(font); - // A triangle the width and height of an 'x' - int w = fm.width(char_type('x')); - int ox = fm.width(char_type(' ')) + x; - int h = fm.ascent(char_type('x')); - int xp[4], yp[4]; - - xp[0] = ox; yp[0] = y; - xp[1] = ox; yp[1] = y - h; - xp[2] = ox + w; yp[2] = y - h/2; - xp[3] = ox; yp[3] = y; - - pi.pain.lines(xp, yp, 4, Color_special); + // There is a \thinspace on each side of the triangle + x += fm.em() / 6; + // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE + // ◃ U+25C3 WHITE LEFT-POINTING SMALL TRIANGLE + char_type const c = pi.ltr_pos ? 0x25B9 : 0x25C3; + font.setColor(Color_special); + pi.pain.text(x, y, c, font); break; } case SLASH: @@ -157,95 +328,88 @@ void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const break; } case PHRASE_LYX: - font.setColor(Color_special); - pi.pain.text(x, y, from_ascii("LyX"), font); - break; case PHRASE_TEX: - font.setColor(Color_special); - pi.pain.text(x, y, from_ascii("TeX"), font); - break; case PHRASE_LATEX2E: - font.setColor(Color_special); - pi.pain.text(x, y, from_ascii("LaTeX2") + char_type(0x03b5), font); - break; case PHRASE_LATEX: - font.setColor(Color_special); - pi.pain.text(x, y, from_ascii("LaTeX"), font); + drawLogo(pi, x, y, kind_); break; } } -// In lyxf3 this will be just LaTeX void InsetSpecialChar::write(ostream & os) const { string command; switch (kind_) { case HYPHENATION: - command = "\\-"; + command = "softhyphen"; + break; + case ALLOWBREAK: + command = "allowbreak"; break; case LIGATURE_BREAK: - command = "\\textcompwordmark{}"; + command = "ligaturebreak"; break; case END_OF_SENTENCE: - command = "\\@."; + command = "endofsentence"; break; case LDOTS: - command = "\\ldots{}"; + command = "ldots"; break; case MENU_SEPARATOR: - command = "\\menuseparator"; + command = "menuseparator"; break; case SLASH: - command = "\\slash{}"; + command = "breakableslash"; break; case NOBREAKDASH: - command = "\\nobreakdash-"; + command = "nobreakdash"; break; case PHRASE_LYX: - command = "\\LyX"; + command = "LyX"; break; case PHRASE_TEX: - command = "\\TeX"; + command = "TeX"; break; case PHRASE_LATEX2E: - command = "\\LaTeX2e"; + command = "LaTeX2e"; break; case PHRASE_LATEX: - command = "\\LaTeX"; + command = "LaTeX"; break; } os << "\\SpecialChar " << command << "\n"; } -// This function will not be necessary when lyx3 void InsetSpecialChar::read(Lexer & lex) { lex.next(); string const command = lex.getString(); - if (command == "\\-") + if (command == "softhyphen") kind_ = HYPHENATION; - else if (command == "\\textcompwordmark{}") + else if (command == "allowbreak") + kind_ = ALLOWBREAK; + else if (command == "ligaturebreak") kind_ = LIGATURE_BREAK; - else if (command == "\\@.") + else if (command == "endofsentence") kind_ = END_OF_SENTENCE; - else if (command == "\\ldots{}") + else if (command == "ldots") kind_ = LDOTS; - else if (command == "\\menuseparator") + else if (command == "menuseparator") kind_ = MENU_SEPARATOR; - else if (command == "\\slash{}") + else if (command == "breakableslash") kind_ = SLASH; - else if (command == "\\nobreakdash-") + else if (command == "nobreakdash") kind_ = NOBREAKDASH; - else if (command == "\\LyX") + else if (command == "LyX") kind_ = PHRASE_LYX; - else if (command == "\\TeX") + else if (command == "TeX") kind_ = PHRASE_TEX; - else if (command == "\\LaTeX2e") + else if (command == "LaTeX2e") kind_ = PHRASE_LATEX2E; - else if (command == "\\LaTeX") + else if (command == "LaTeX") kind_ = PHRASE_LATEX; else lex.printError("InsetSpecialChar: Unknown kind: `$$Token'"); @@ -255,27 +419,42 @@ void InsetSpecialChar::read(Lexer & lex) void InsetSpecialChar::latex(otexstream & os, OutputParams const & rp) const { + bool const rtl = rp.local_font->isRightToLeft(); + string lswitch = ""; + string lswitche = ""; + if (rtl && !rp.use_polyglossia) { + lswitch = "\\L{"; + lswitche = "}"; + if (rp.local_font->language()->lang() == "arabic_arabi" + || rp.local_font->language()->lang() == "farsi") + lswitch = "\\textLR{"; + } + switch (kind_) { case HYPHENATION: os << "\\-"; break; + case ALLOWBREAK: + os << "\\LyXZeroWidthSpace" << termcmd; + break; case LIGATURE_BREAK: - os << "\\textcompwordmark{}"; + os << "\\textcompwordmark" << termcmd; break; case END_OF_SENTENCE: os << "\\@."; break; case LDOTS: - os << "\\ldots{}"; + os << "\\ldots" << termcmd; break; case MENU_SEPARATOR: - if (rp.local_font->isRightToLeft()) - os << "\\lyxarrow*{}"; + if (rtl) + os << "\\lyxarrow*"; else - os << "\\lyxarrow{}"; + os << "\\lyxarrow"; + os << termcmd; break; case SLASH: - os << "\\slash{}"; + os << "\\slash" << termcmd; break; case NOBREAKDASH: if (rp.moving_arg) @@ -285,22 +464,22 @@ void InsetSpecialChar::latex(otexstream & os, case PHRASE_LYX: if (rp.moving_arg) os << "\\protect"; - os << "\\LyX{}"; + os << lswitch << "\\LyX" << termcmd << lswitche; break; case PHRASE_TEX: if (rp.moving_arg) os << "\\protect"; - os << "\\TeX{}"; + os << lswitch << "\\TeX" << termcmd << lswitche; break; case PHRASE_LATEX2E: if (rp.moving_arg) os << "\\protect"; - os << "\\LaTeX2e{}"; + os << lswitch << "\\LaTeXe" << termcmd << lswitche; break; case PHRASE_LATEX: if (rp.moving_arg) os << "\\protect"; - os << "\\LaTeX{}"; + os << lswitch << "\\LaTeX" << termcmd << lswitche; break; } } @@ -312,6 +491,9 @@ int InsetSpecialChar::plaintext(odocstringstream & os, switch (kind_) { case HYPHENATION: return 0; + case ALLOWBREAK: + os.put(0x200b); + return 1; case LIGATURE_BREAK: os.put(0x200c); return 1; @@ -352,6 +534,10 @@ int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const { switch (kind_) { case HYPHENATION: + break; + case ALLOWBREAK: + os.put(0x200b); + break; case LIGATURE_BREAK: break; case END_OF_SENTENCE: @@ -392,6 +578,9 @@ docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const switch (kind_) { case HYPHENATION: break; + case ALLOWBREAK: + xs << XHTMLStream::ESCAPE_NONE << "​"; + break; case LIGATURE_BREAK: xs << XHTMLStream::ESCAPE_NONE << "‌"; break; @@ -430,8 +619,9 @@ docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const void InsetSpecialChar::toString(odocstream & os) const { switch (kind_) { + case ALLOWBREAK: case LIGATURE_BREAK: - // Do not output ZERO WIDTH NON JOINER here + // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here // Spell checker would choke on it. return; default: @@ -443,7 +633,8 @@ void InsetSpecialChar::toString(odocstream & os) const } -void InsetSpecialChar::forOutliner(docstring & os, size_t) const +void InsetSpecialChar::forOutliner(docstring & os, size_t const, + bool const) const { odocstringstream ods; plaintext(ods, OutputParams(0)); @@ -453,6 +644,8 @@ void InsetSpecialChar::forOutliner(docstring & os, size_t) const void InsetSpecialChar::validate(LaTeXFeatures & features) const { + if (kind_ == ALLOWBREAK) + features.require("lyxzerowidthspace"); if (kind_ == MENU_SEPARATOR) features.require("lyxarrow"); if (kind_ == NOBREAKDASH) @@ -462,6 +655,12 @@ void InsetSpecialChar::validate(LaTeXFeatures & features) const } +bool InsetSpecialChar::isChar() const +{ + return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK; +} + + bool InsetSpecialChar::isLetter() const { return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK @@ -476,8 +675,8 @@ bool InsetSpecialChar::isLineSeparator() const // Paragraph::stripLeadingSpaces nukes the characters which // have this property. I leave the code here, since it should // eventually be made to work. (JMarc 20020327) - return kind_ == HYPHENATION || kind_ == MENU_SEPARATOR - || kind_ == SLASH; + return kind_ == HYPHENATION || kind_ == ALLOWBREAK + || kind_ == MENU_SEPARATOR || kind_ == SLASH; #else return false; #endif