X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetQuotes.cpp;h=1882769cc0e18ece5a62faccbdb7ea2042207ed6;hb=c2229d388b622a65029d0566f5eca53b43d92528;hp=1191eb1263264de58d5f6eed86c98c62424cf634;hpb=421853d1bf021b7180a61d8c2908ede2dce48061;p=lyx.git diff --git a/src/insets/InsetQuotes.cpp b/src/insets/InsetQuotes.cpp index 1191eb1263..1882769cc0 100644 --- a/src/insets/InsetQuotes.cpp +++ b/src/insets/InsetQuotes.cpp @@ -27,11 +27,10 @@ #include "Lexer.h" #include "LyXRC.h" #include "MetricsInfo.h" -#include "OutputParams.h" -#include "output_xhtml.h" #include "Paragraph.h" #include "ParIterator.h" #include "texstream.h" +#include "xml.h" #include "frontends/FontMetrics.h" #include "frontends/Painter.h" @@ -41,6 +40,7 @@ #include "support/docstream.h" #include "support/gettext.h" #include "support/lstrings.h" +#include "support/textutils.h" #include @@ -67,14 +67,15 @@ namespace { * r <> (,,inner quotation``) * j [U+300C]cjk[U+300D] ([U+300E]inner quotation[U+300F]) [CORNER BRACKETS] * k [U+300A]cjkangle[U+300B] ([U+3008]inner quotation[U+3009]) [ANGLE BRACKETS] + * h ,,hungarian'' (>>inner quotation<<) * x dynamic style (inherits document settings) */ -char const * const style_char = "esgpcaqbwfirjkx"; +char const * const style_char = "esgpcaqbwfirjkhx"; char const * const side_char = "lr" ; char const * const level_char = "sd"; -} // namespace anon +} // namespace ///////////////////////////////////////////////////////////////////// @@ -92,8 +93,110 @@ int InsetQuotesParams::stylescount() const } +char InsetQuotesParams::getStyleChar(QuoteStyle const & style) const +{ + return style_char[static_cast(style)]; +} + + +QuoteStyle InsetQuotesParams::getQuoteStyle(string const & s, + bool const allow_wildcards, QuoteStyle fb) const +{ + QuoteStyle res = fb; + + string str = s; + if (str.length() != 3) { + LYXERR0("ERROR (InsetQuotes::InsetQuotes):" + " bad string length."); + str = "eld"; + } + + // '.' wildcard means: keep current style + if (!allow_wildcards || str[0] != '.') { + int i; + for (i = 0; i < stylescount(); ++i) { + if (str[0] == style_char[i]) { + res = QuoteStyle(i); + break; + } + } + if (i >= stylescount()) { + LYXERR0("ERROR (InsetQuotes::InsetQuotes):" + " bad style specification."); + res = QuoteStyle::English; + } + } + + return res; +} + + +QuoteSide InsetQuotesParams::getQuoteSide(string const & s, + bool const allow_wildcards, QuoteSide fb) const +{ + QuoteSide res = fb; + + string str = s; + if (str.length() != 3) { + LYXERR0("ERROR (InsetQuotes::InsetQuotes):" + " bad string length."); + str = "eld"; + } + + // '.' wildcard means: keep current side + if (!allow_wildcards || str[1] != '.') { + int i; + for (i = 0; i < 2; ++i) { + if (str[1] == side_char[i]) { + res = QuoteSide(i); + break; + } + } + if (i >= 2) { + LYXERR0("ERROR (InsetQuotes::InsetQuotes):" + " bad side specification."); + res = QuoteSide::Opening; + } + } + + return res; +} + + +QuoteLevel InsetQuotesParams::getQuoteLevel(string const & s, + bool const allow_wildcards, QuoteLevel fb) const +{ + QuoteLevel res = fb; + + string str = s; + if (str.length() != 3) { + LYXERR0("ERROR (InsetQuotes::InsetQuotes):" + " bad string length."); + str = "eld"; + } + + // '.' wildcard means: keep current level + if (!allow_wildcards || str[2] != '.') { + int i; + for (i = 0; i < 2; ++i) { + if (str[2] == level_char[i]) { + res = QuoteLevel(i); + break; + } + } + if (i >= 2) { + LYXERR0("ERROR (InsetQuotes::InsetQuotes):" + " bad level specification."); + res = QuoteLevel::Primary; + } + } + + return res; +} + + char_type InsetQuotesParams::getQuoteChar(QuoteStyle const & style, QuoteLevel const & level, - QuoteSide const & side) const + QuoteSide const & side, bool const rtl) const { // main opening quotation mark char_type left_primary; @@ -105,105 +208,112 @@ char_type InsetQuotesParams::getQuoteChar(QuoteStyle const & style, QuoteLevel c char_type right_secondary; switch (style) { - case EnglishQuotes: { + case QuoteStyle::English: { left_primary = 0x201c; // `` right_primary = 0x201d; // '' left_secondary = 0x2018; // ` right_secondary = 0x2019; // ' break; } - case SwedishQuotes: { + case QuoteStyle::Swedish: { left_primary = 0x201d; // '' right_primary = 0x201d; // '' left_secondary = 0x2019; // ' right_secondary = 0x2019; // ' break; } - case GermanQuotes: { + case QuoteStyle::German: { left_primary = 0x201e; // ,, right_primary = 0x201c; // `` left_secondary = 0x201a; // , right_secondary = 0x2018; // ` break; } - case PolishQuotes: { + case QuoteStyle::Polish: { left_primary = 0x201e; // ,, right_primary = 0x201d; // '' left_secondary = 0x201a; // , right_secondary = 0x2019; // ' break; } - case SwissQuotes: { + case QuoteStyle::Swiss: { left_primary = 0x00ab; // << right_primary = 0x00bb; // >> left_secondary = 0x2039; // < right_secondary = 0x203a; // > break; } - case DanishQuotes: { + case QuoteStyle::Danish: { left_primary = 0x00bb; // >> right_primary = 0x00ab; // << left_secondary = 0x203a; // > right_secondary = 0x2039; // < break; } - case PlainQuotes: { + case QuoteStyle::Plain: { left_primary = 0x0022; // " right_primary = 0x0022; // " left_secondary = 0x0027; // ' right_secondary = 0x0027; // ' break; } - case BritishQuotes: { + case QuoteStyle::British: { left_primary = 0x2018; // ` right_primary = 0x2019; // ' left_secondary = 0x201c; // `` right_secondary = 0x201d; // '' break; } - case SwedishGQuotes: { + case QuoteStyle::SwedishG: { left_primary = 0x00bb; // >> right_primary = 0x00bb; // >> left_secondary = 0x2019; // ' right_secondary = 0x2019; // ' break; } - case FrenchQuotes: { + case QuoteStyle::French: { left_primary = 0x00ab; // << right_primary = 0x00bb; // >> left_secondary = 0x201c; // `` right_secondary = 0x201d; // '' break; } - case FrenchINQuotes:{ + case QuoteStyle::FrenchIN:{ left_primary = 0x00ab; // << right_primary = 0x00bb; // >> left_secondary = 0x00ab; // << right_secondary = 0x00bb; // >> break; } - case RussianQuotes:{ + case QuoteStyle::Russian:{ left_primary = 0x00ab; // << right_primary = 0x00bb; // >> left_secondary = 0x201e; // ,, right_secondary = 0x201c; // `` break; } - case CJKQuotes:{ + case QuoteStyle::CJK:{ left_primary = 0x300c; // LEFT CORNER BRACKET right_primary = 0x300d; // RIGHT CORNER BRACKET left_secondary = 0x300e; // LEFT WHITE CORNER BRACKET right_secondary = 0x300f; // RIGHT WHITE CORNER BRACKET break; } - case CJKAngleQuotes:{ + case QuoteStyle::CJKAngle:{ left_primary = 0x300a; // LEFT DOUBLE ANGLE BRACKET right_primary = 0x300b; // RIGHT DOUBLE ANGLE BRACKET left_secondary = 0x3008; // LEFT ANGLE BRACKET right_secondary = 0x3009; // RIGHT ANGLE BRACKET break; } - case DynamicQuotes: + case QuoteStyle::Hungarian: { + left_primary = 0x201e; // ,, + right_primary = 0x201d; // '' + left_secondary = 0x00bb; // >> + right_secondary = 0x00ab; // << + break; + } + case QuoteStyle::Dynamic: default: // should not happen left_primary = 0x003f; // ? @@ -214,10 +324,14 @@ char_type InsetQuotesParams::getQuoteChar(QuoteStyle const & style, QuoteLevel c } switch (level) { - case SecondaryQuotes: - return (side == OpeningQuote) ? left_secondary : right_secondary; - case PrimaryQuotes: - return (side == OpeningQuote) ? left_primary : right_primary; + case QuoteLevel::Secondary: + if (rtl) + return (side == QuoteSide::Closing) ? left_secondary : right_secondary; + return (side == QuoteSide::Opening) ? left_secondary : right_secondary; + case QuoteLevel::Primary: + if (rtl) + return (side == QuoteSide::Closing) ? left_primary : right_primary; + return (side == QuoteSide::Opening) ? left_primary : right_primary; default: break; } @@ -227,7 +341,8 @@ char_type InsetQuotesParams::getQuoteChar(QuoteStyle const & style, QuoteLevel c } -docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op) const +docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op, + bool const rtl) const { string res; @@ -241,14 +356,16 @@ docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op) const } case 0x2019: {// ' if (op == "int") - res = "\\textquoteleft"; + // This macro is redefined in rtl mode + res = rtl ? "\\textquoteleft" : "\\textquoteright"; else res = "'"; break; } case 0x2018: {// ` if (op == "int") - res = "\\textquoteright"; + // This macro is redefined in rtl mode + res = rtl ? "\\textquoteright" : "\\textquoteleft"; else res = "`"; break; @@ -282,14 +399,16 @@ docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op) const } case 0x201d: {// '' if (op == "int") - res = "\\textquotedblleft"; + // This macro is redefined in rtl mode + res = rtl ? "\\textquotedblleft" : "\\textquotedblright"; else res = "''"; break; } case 0x201c: {// `` if (op == "int") - res = "\\textquotedblright"; + // This macro is redefined in rtl mode + res = rtl ? "\\textquotedblright" : "\\textquotedblleft"; else res = "``"; break; @@ -318,7 +437,7 @@ docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op) const } // The following are fakes // This is just to get something symbolic - // in encodings where this chars would not be used ayway + // in encodings where this chars would not be used anyway case 0x300c: // LEFT CORNER BRACKET res = "\\ensuremath{\\lceil}"; break; @@ -346,7 +465,7 @@ docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op) const default: break; } - + return from_ascii(res); } @@ -419,11 +538,18 @@ docstring InsetQuotesParams::getHTMLQuote(char_type c) const default: break; } - + return from_ascii(res); } +docstring InsetQuotesParams::getXMLQuote(char_type c) const +{ + // Directly output the character Unicode form. + return from_ascii("&#" + to_string(c) + ";"); +} + + map InsetQuotesParams::getTypes() const { map res; @@ -437,12 +563,12 @@ map InsetQuotesParams::getTypes() const // get all quote types for (sty = 0; sty < stylescount(); ++sty) { style = QuoteStyle(sty); - if (style == DynamicQuotes) + if (style == QuoteStyle::Dynamic) continue; for (sid = 0; sid < 2; ++sid) { side = QuoteSide(sid); for (lev = 0; lev < 2; ++lev) { - type += style_char[style]; + type += style_char[static_cast(style)]; type += side_char[sid]; level = QuoteLevel(lev); type += level_char[lev]; @@ -455,14 +581,36 @@ map InsetQuotesParams::getTypes() const } -docstring const InsetQuotesParams::getGuiLabel(QuoteStyle const & qs) +docstring const InsetQuotesParams::getGuiLabel(QuoteStyle const & qs, bool langdef) const +{ + docstring const styledesc = + bformat(_("%1$souter%2$s and %3$sinner%4$s[[quotation marks]]"), + docstring(1, getQuoteChar(qs, QuoteLevel::Primary, QuoteSide::Opening)), + docstring(1, getQuoteChar(qs, QuoteLevel::Primary, QuoteSide::Closing)), + docstring(1, getQuoteChar(qs, QuoteLevel::Secondary, QuoteSide::Opening)), + docstring(1, getQuoteChar(qs, QuoteLevel::Secondary, QuoteSide::Closing)) + ); + + if (!langdef) + return styledesc; + + return bformat(_("%1$s[[quot. mark description]] (language default)"), + styledesc); +} + + +docstring const InsetQuotesParams::getShortGuiLabel(docstring const & str) const { - return bformat(_("%1$souter%2$s and %3$sinner%4$s[[quotation marks]]"), - docstring(1, quoteparams.getQuoteChar(qs, PrimaryQuotes, OpeningQuote)), - docstring(1, quoteparams.getQuoteChar(qs, PrimaryQuotes, ClosingQuote)), - docstring(1, quoteparams.getQuoteChar(qs, SecondaryQuotes, OpeningQuote)), - docstring(1, quoteparams.getQuoteChar(qs, SecondaryQuotes, ClosingQuote)) - ); + string const s = to_ascii(str); + QuoteStyle const style = getQuoteStyle(s); + QuoteSide const side = getQuoteSide(s); + QuoteLevel const level = getQuoteLevel(s); + + return (side == QuoteSide::Opening) ? + bformat(_("%1$stext"), + docstring(1, getQuoteChar(style, level, side))) : + bformat(_("text%1$s"), + docstring(1, getQuoteChar(style, level, side))); } @@ -472,46 +620,37 @@ docstring const InsetQuotesParams::getGuiLabel(QuoteStyle const & qs) // /////////////////////////////////////////////////////////////////////// -InsetQuotes::InsetQuotes(Buffer * buf, string const & str) : Inset(buf) +InsetQuotes::InsetQuotes(Buffer * buf, string const & str) + : Inset(buf) { if (buf) { global_style_ = buf->masterBuffer()->params().quotes_style; fontspec_ = buf->masterBuffer()->params().useNonTeXFonts; } - else { - global_style_ = InsetQuotesParams::EnglishQuotes; - fontspec_ = false; - } parseString(str); } -InsetQuotes::InsetQuotes(Buffer * buf, char_type c, InsetQuotesParams::QuoteLevel level, +InsetQuotes::InsetQuotes(Buffer * buf, char_type c, QuoteLevel level, string const & side, string const & style) - : Inset(buf), level_(level), pass_thru_(false), fontspec_(false) + : Inset(buf), level_(level) { bool dynamic = false; if (buf) { global_style_ = buf->masterBuffer()->params().quotes_style; - fontenc_ = (buf->masterBuffer()->params().fontenc == "global") - ? lyxrc.fontenc : buf->params().fontenc; dynamic = buf->masterBuffer()->params().dynamic_quotes; fontspec_ = buf->masterBuffer()->params().useNonTeXFonts; - } else { - global_style_ = InsetQuotesParams::EnglishQuotes; - fontenc_ = lyxrc.fontenc; - fontspec_ = false; } if (style.empty()) - style_ = dynamic ? InsetQuotesParams::DynamicQuotes : global_style_; + style_ = dynamic ? QuoteStyle::Dynamic : global_style_; else style_ = getStyle(style); if (side == "left" || side == "opening") - side_ = InsetQuotesParams::OpeningQuote; + side_ = QuoteSide::Opening; else if (side == "right" || side == "closing") - side_ = InsetQuotesParams::ClosingQuote; + side_ = QuoteSide::Closing; else setSide(c); } @@ -526,110 +665,57 @@ docstring InsetQuotes::layoutName() const void InsetQuotes::setSide(char_type c) { // Decide whether opening or closing quote - switch (c) { - case ' ': - case '(': - case '[': - side_ = InsetQuotesParams::OpeningQuote;// opening quote - break; - default: - side_ = InsetQuotesParams::ClosingQuote;// closing quote - } + if (lyx::isSpace(c) || isOpenPunctuation(c)) + side_ = QuoteSide::Opening;// opening quote + else + side_ = QuoteSide::Closing;// closing quote } void InsetQuotes::parseString(string const & s, bool const allow_wildcards) { - string str = s; - if (str.length() != 3) { - lyxerr << "ERROR (InsetQuotes::InsetQuotes):" - " bad string length." << endl; - str = "eld"; - } - - int i; - - // '.' wildcard means: keep current stylee - if (!allow_wildcards || str[0] != '.') { - for (i = 0; i < quoteparams.stylescount(); ++i) { - if (str[0] == style_char[i]) { - style_ = InsetQuotesParams::QuoteStyle(i); - break; - } - } - if (i >= quoteparams.stylescount()) { - LYXERR0("ERROR (InsetQuotes::InsetQuotes):" - " bad style specification."); - style_ = InsetQuotesParams::EnglishQuotes; - } - } - - // '.' wildcard means: keep current side - if (!allow_wildcards || str[1] != '.') { - for (i = 0; i < 2; ++i) { - if (str[1] == side_char[i]) { - side_ = InsetQuotesParams::QuoteSide(i); - break; - } - } - if (i >= 2) { - LYXERR0("ERROR (InsetQuotes::InsetQuotes):" - " bad side specification."); - side_ = InsetQuotesParams::OpeningQuote; - } - } - - // '.' wildcard means: keep current level - if (!allow_wildcards || str[2] != '.') { - for (i = 0; i < 2; ++i) { - if (str[2] == level_char[i]) { - level_ = InsetQuotesParams::QuoteLevel(i); - break; - } - } - if (i >= 2) { - LYXERR0("ERROR (InsetQuotes::InsetQuotes):" - " bad level specification."); - level_ = InsetQuotesParams::PrimaryQuotes; - } - } + style_ = quoteparams.getQuoteStyle(s, allow_wildcards, style_); + side_ = quoteparams.getQuoteSide(s, allow_wildcards, side_); + level_ = quoteparams.getQuoteLevel(s, allow_wildcards, level_); } -InsetQuotesParams::QuoteStyle InsetQuotes::getStyle(string const & s) +QuoteStyle InsetQuotes::getStyle(string const & s) { - InsetQuotesParams::QuoteStyle qs = InsetQuotesParams::EnglishQuotes; - + QuoteStyle qs = QuoteStyle::English; + if (s == "english") - qs = InsetQuotesParams::EnglishQuotes; + qs = QuoteStyle::English; else if (s == "swedish") - qs = InsetQuotesParams::SwedishQuotes; + qs = QuoteStyle::Swedish; else if (s == "german") - qs = InsetQuotesParams::GermanQuotes; + qs = QuoteStyle::German; else if (s == "polish") - qs = InsetQuotesParams::PolishQuotes; + qs = QuoteStyle::Polish; else if (s == "swiss") - qs = InsetQuotesParams::SwissQuotes; + qs = QuoteStyle::Swiss; else if (s == "danish") - qs = InsetQuotesParams::DanishQuotes; + qs = QuoteStyle::Danish; else if (s == "plain") - qs = InsetQuotesParams::PlainQuotes; + qs = QuoteStyle::Plain; else if (s == "british") - qs = InsetQuotesParams::BritishQuotes; + qs = QuoteStyle::British; else if (s == "swedishg") - qs = InsetQuotesParams::SwedishGQuotes; + qs = QuoteStyle::SwedishG; else if (s == "french") - qs = InsetQuotesParams::FrenchQuotes; + qs = QuoteStyle::French; else if (s == "frenchin") - qs = InsetQuotesParams::FrenchINQuotes; + qs = QuoteStyle::FrenchIN; else if (s == "russian") - qs = InsetQuotesParams::RussianQuotes; + qs = QuoteStyle::Russian; else if (s == "cjk") - qs = InsetQuotesParams::CJKQuotes; + qs = QuoteStyle::CJK; else if (s == "cjkangle") - qs = InsetQuotesParams::CJKAngleQuotes; + qs = QuoteStyle::CJKAngle; + else if (s == "hungarian") + qs = QuoteStyle::Hungarian; else if (s == "dynamic") - qs = InsetQuotesParams::DynamicQuotes; + qs = QuoteStyle::Dynamic; return qs; } @@ -639,23 +725,23 @@ docstring InsetQuotes::displayString() const { // In PassThru, we use straight quotes if (pass_thru_) - return (level_ == InsetQuotesParams::PrimaryQuotes) ? + return (level_ == QuoteLevel::Primary) ? from_ascii("\"") : from_ascii("'"); - InsetQuotesParams::QuoteStyle style = - (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; + QuoteStyle style = + (style_ == QuoteStyle::Dynamic) ? global_style_ : style_; - docstring retdisp = docstring(1, quoteparams.getQuoteChar(style, level_, side_)); + docstring retdisp = docstring(1, quoteparams.getQuoteChar(style, level_, side_, rtl_)); // in French, thin spaces are added inside double guillemets if (prefixIs(context_lang_, "fr") - && level_ == InsetQuotesParams::PrimaryQuotes - && (style == InsetQuotesParams::SwissQuotes - || style == InsetQuotesParams::FrenchQuotes - || style == InsetQuotesParams::FrenchINQuotes)) { + && level_ == QuoteLevel::Primary + && (style == QuoteStyle::Swiss + || style == QuoteStyle::French + || style == QuoteStyle::FrenchIN)) { // THIN SPACE (U+2009) char_type const thin_space = 0x2009; - if (side_ == InsetQuotesParams::OpeningQuote) + if (side_ == QuoteSide::Opening) retdisp += thin_space; else retdisp = thin_space + retdisp; @@ -678,7 +764,7 @@ void InsetQuotes::metrics(MetricsInfo & mi, Dimension & dim) const void InsetQuotes::draw(PainterInfo & pi, int x, int y) const { FontInfo font = pi.base.font; - if (style_ == InsetQuotesParams::DynamicQuotes) + if (style_ == QuoteStyle::Dynamic) font.setPaintColor(Color_special); else font.setPaintColor(pi.textColor(font.realColor())); @@ -689,9 +775,9 @@ void InsetQuotes::draw(PainterInfo & pi, int x, int y) const string InsetQuotes::getType() const { string text; - text += style_char[style_]; - text += side_char[side_]; - text += level_char[level_]; + text += style_char[static_cast(style_)]; + text += side_char[static_cast(side_)]; + text += level_char[static_cast(level_)]; return text; } @@ -725,7 +811,7 @@ void InsetQuotes::doDispatch(Cursor & cur, FuncRequest & cmd) } cur.recordUndoInset(this); parseString(cmd.getArg(1), true); - cur.buffer()->updateBuffer(); + cur.forceBufferUpdate(); break; } default: @@ -759,43 +845,50 @@ bool InsetQuotes::getStatus(Cursor & cur, FuncRequest const & cmd, void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const { - InsetQuotesParams::QuoteStyle style = - (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; - char_type quotechar = quoteparams.getQuoteChar(style, level_, side_); + QuoteStyle style = + (style_ == QuoteStyle::Dynamic) ? global_style_ : style_; + char_type quotechar = quoteparams.getQuoteChar(style, level_, side_, rtl_); docstring qstr; // In pass-thru context, we output plain quotes - if (runparams.pass_thru) - qstr = (level_ == InsetQuotesParams::PrimaryQuotes) ? from_ascii("\"") : from_ascii("'"); - else if (style == InsetQuotesParams::PlainQuotes && fontspec_) { + if (runparams.pass_thru || runparams.for_search != OutputParams::NoSearch) + qstr = (level_ == QuoteLevel::Primary) ? from_ascii("\"") : from_ascii("'"); + else if (style == QuoteStyle::Plain && fontspec_) { // For XeTeX and LuaTeX,we need to disable mapping to get straight // quotes. We define our own commands that do this - qstr = (level_ == InsetQuotesParams::PrimaryQuotes) ? + qstr = (level_ == QuoteLevel::Primary) ? from_ascii("\\textquotedblplain") : from_ascii("\\textquotesingleplain"); } else if (runparams.use_polyglossia) { - // For polyglossia, we directly output the respective unicode chars + // For polyglossia, we directly output the respective unicode chars // (spacing and kerning is then handled respectively) qstr = docstring(1, quotechar); } - else if (style == InsetQuotesParams::CJKQuotes || style == InsetQuotesParams::CJKAngleQuotes) { - if (runparams.encoding && runparams.encoding->encodable(quotechar)) + // The CJK marks are not yet covered by utf8 inputenc (we don't have the entry in + // unicodesymbols, since we don't want to add fake synbols there). + else if (style == QuoteStyle::CJK || style == QuoteStyle::CJKAngle) { + if (runparams.encoding && runparams.encoding->name() != "utf8" + && runparams.encoding->encodable(quotechar)) qstr = docstring(1, quotechar); else qstr = quoteparams.getLaTeXQuote(quotechar, "int"); } - else if ((style == InsetQuotesParams::SwissQuotes - || style == InsetQuotesParams::FrenchQuotes - || style == InsetQuotesParams::FrenchINQuotes) - && level_ == InsetQuotesParams::PrimaryQuotes + else if ((style == QuoteStyle::Swiss + || style == QuoteStyle::French + || style == QuoteStyle::FrenchIN) + && level_ == QuoteLevel::Primary && prefixIs(runparams.local_font->language()->code(), "fr")) { // Specific guillemets of French babel // including correct French spacing - if (side_ == InsetQuotesParams::OpeningQuote) + if (side_ == QuoteSide::Opening) qstr = from_ascii("\\og"); else qstr = from_ascii("\\fg"); - } else if (fontenc_ == "T1" + } else if (runparams.use_hyperref && runparams.moving_arg) { + // Use internal commands in headings with hyperref + // (ligatures not featured in PDF strings) + qstr = quoteparams.getLaTeXQuote(quotechar, "int", rtl_); + } else if (runparams.main_fontenc == "T1" && !runparams.local_font->language()->internalFontEncoding()) { // Quotation marks for T1 font encoding // (using ligatures) @@ -803,12 +896,14 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const } else if (runparams.local_font->language()->internalFontEncoding()) { // Quotation marks for internal font encodings // (ligatures not featured) - qstr = quoteparams.getLaTeXQuote(quotechar, "int"); + qstr = quoteparams.getLaTeXQuote(quotechar, "int", rtl_); #ifdef DO_USE_DEFAULT_LANGUAGE - } else if (doclang == "default") { + } else if ((doclang == "default" #else - } else if (!runparams.use_babel || runparams.isFullUnicode()) { + } else if ((!runparams.use_babel #endif + || (runparams.main_fontenc != "T1" && runparams.main_fontenc != "OT1")) + || runparams.isFullUnicode()) { // Standard quotation mark macros // These are also used by babel // without fontenc (XeTeX/LuaTeX) @@ -821,15 +916,21 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const if (!runparams.pass_thru) { // Guard against unwanted ligatures with preceding text char_type const lastchar = os.lastChar(); + // LuaTeX does not respect {} as ligature breaker by design, + // see https://tex.stackexchange.com/q/349725/19291 + docstring const nolig = + (runparams.flavor == Flavor::LuaTeX + || runparams.flavor == Flavor::DviLuaTeX) ? + from_ascii("\\/") : from_ascii("{}"); // !` ?` => !{}` ?{}` if (prefixIs(qstr, from_ascii("`")) && (lastchar == '!' || lastchar == '?')) - os << "{}"; + os << nolig; // ``` ''' ,,, <<< >>> // => `{}`` '{}'' ,{},, <{}<< >{}>> if (contains(from_ascii(",'`<>"), lastchar) && prefixIs(qstr, lastchar)) - os << "{}"; + os << nolig; } os << qstr; @@ -840,46 +941,56 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const } -int InsetQuotes::plaintext(odocstringstream & os, - OutputParams const &, size_t) const +int InsetQuotes::plaintext(odocstringstream & os, + OutputParams const & op, size_t) const { - docstring const str = displayString(); - os << str; - return str.size(); + if (op.for_search == OutputParams::NoSearch) { + docstring const str = displayString(); + os << str; + return str.size(); + } + else { + if (level_ == QuoteLevel::Primary) + os << from_ascii("\""); + else + os << from_ascii("'"); + return 1; + } } -docstring InsetQuotes::getQuoteEntity() const { - InsetQuotesParams::QuoteStyle style = - (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; - docstring res = quoteparams.getHTMLQuote(quoteparams.getQuoteChar(style, level_, side_)); +docstring InsetQuotes::getQuoteEntity(bool isHTML) const { + QuoteStyle style = + (style_ == QuoteStyle::Dynamic) ? global_style_ : style_; + docstring res = isHTML ? quoteparams.getHTMLQuote(quoteparams.getQuoteChar(style, level_, side_)) : + quoteparams.getXMLQuote(quoteparams.getQuoteChar(style, level_, side_)); + // in French, thin spaces are added inside double guillemets if (prefixIs(context_lang_, "fr") - && level_ == InsetQuotesParams::PrimaryQuotes - && (style == InsetQuotesParams::FrenchQuotes - || style == InsetQuotesParams::FrenchINQuotes - || style == InsetQuotesParams::SwissQuotes)) { + && level_ == QuoteLevel::Primary + && (style == QuoteStyle::French + || style == QuoteStyle::FrenchIN + || style == QuoteStyle::Swiss)) { // THIN SPACE (U+2009) docstring const thin_space = from_ascii(" "); - if (side_ == InsetQuotesParams::OpeningQuote) + if (side_ == QuoteSide::Opening) // Open quote: space after res += thin_space; - else + else // Close quote: space before res = thin_space + res; } return res; } -int InsetQuotes::docbook(odocstream & os, OutputParams const &) const +void InsetQuotes::docbook(XMLStream & xs, OutputParams const &) const { - os << getQuoteEntity(); - return 0; + xs << XMLStream::ESCAPE_NONE << getQuoteEntity(false); } -docstring InsetQuotes::xhtml(XHTMLStream & xs, OutputParams const &) const +docstring InsetQuotes::xhtml(XMLStream & xs, OutputParams const &) const { - xs << XHTMLStream::ESCAPE_NONE << getQuoteEntity(); + xs << XMLStream::ESCAPE_NONE << getQuoteEntity(true); return docstring(); } @@ -896,21 +1007,23 @@ void InsetQuotes::forOutliner(docstring & os, size_t const, bool const) const } -void InsetQuotes::updateBuffer(ParIterator const & it, UpdateType /* utype*/) +void InsetQuotes::updateBuffer(ParIterator const & it, UpdateType /* utype*/, bool const /*deleted*/) { BufferParams const & bp = buffer().masterBuffer()->params(); + Font const & font = it.paragraph().getFontSettings(bp, it.pos()); pass_thru_ = it.paragraph().isPassThru(); - context_lang_ = it.paragraph().getFontSettings(bp, it.pos()).language()->code(); - fontenc_ = (bp.fontenc == "global") ? lyxrc.fontenc : bp.fontenc; + context_lang_ = font.language()->code(); + internal_fontenc_ = font.language()->internalFontEncoding(); global_style_ = bp.quotes_style; fontspec_ = bp.useNonTeXFonts; + rtl_ = font.isRightToLeft(); } void InsetQuotes::validate(LaTeXFeatures & features) const { - InsetQuotesParams::QuoteStyle style = - (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; + QuoteStyle style = + (style_ == QuoteStyle::Dynamic) ? global_style_ : style_; char_type type = quoteparams.getQuoteChar(style, level_, side_); // Handle characters that are not natively supported by @@ -920,7 +1033,8 @@ void InsetQuotes::validate(LaTeXFeatures & features) const #else if (!features.useBabel() #endif - && !features.runparams().isFullUnicode() && fontenc_ != "T1") { + && !features.runparams().isFullUnicode() + && features.runparams().main_fontenc != "T1") { switch (type) { case 0x201a: features.require("quotesinglbase"); @@ -949,22 +1063,24 @@ void InsetQuotes::validate(LaTeXFeatures & features) const switch (type) { case 0x0027: { if (features.runparams().isFullUnicode() && fontspec_) - features.require("textquotesinglep"); - else - features.require("textcomp"); - break; + features.require("textquotesinglep"); + else + features.require("textcomp"); + break; } case 0x0022: { if (features.runparams().isFullUnicode() && fontspec_) features.require("textquotedblp"); - else if (fontenc_ != "T1") + else if (features.runparams().main_fontenc != "T1" || internal_fontenc_) features.require("textquotedbl"); break; } - // we fake these from math + // we fake these from math (also for utf8 inputenc + // currently; see above) case 0x300e: // LEFT WHITE CORNER BRACKET case 0x300f: // RIGHT WHITE CORNER BRACKET if (!features.runparams().encoding + || features.runparams().encoding->name() == "utf8" || !features.runparams().encoding->encodable(type)) features.require("stmaryrd"); break;