X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetQuotes.cpp;h=bc5d6d527f832a3e57e68c66f893f6d944aa997e;hb=89ce0c9069d36748bea0701c52225a32d5e5d66b;hp=147e17787d840dfe4a8918bb23bdec6fc7dc6d3c;hpb=197c193e210cea1697a1217e9f252a35f266273a;p=lyx.git diff --git a/src/insets/InsetQuotes.cpp b/src/insets/InsetQuotes.cpp index 147e17787d..bc5d6d527f 100644 --- a/src/insets/InsetQuotes.cpp +++ b/src/insets/InsetQuotes.cpp @@ -4,6 +4,7 @@ * Licence details can be found in the file COPYING. * * \author Jean-Marc Lasgouttes + * \author Jürgen Spitzmüller * * Full author contact details are available in file CREDITS. */ @@ -17,6 +18,7 @@ #include "BufferView.h" #include "Cursor.h" #include "Dimension.h" +#include "Encoding.h" #include "Font.h" #include "FuncStatus.h" #include "FuncRequest.h" @@ -37,8 +39,11 @@ #include "support/debug.h" #include "support/docstring.h" #include "support/docstream.h" +#include "support/gettext.h" #include "support/lstrings.h" +#include + using namespace std; using namespace lyx::support; @@ -47,90 +52,460 @@ namespace lyx { namespace { /* codes used to read/write quotes to LyX files - * e ``english'' - * s ''swedish'' - * g ,,german`` - * p ,,polish'' - * f <> - * a >>danish<< + * available styles: + * e ``english'' (`inner quotation') + * s ''swedish'' ('inner quotation') + * g ,,german`` (,inner quotation`) + * p ,,polish'' (,inner quotation') + * c <> () + * a >>danish<< (>inner quotation<) + * q "plain" ('inner quotation') + * b `british' (``inner quotation'') + * w >>swedishg>> ('inner quotation') ["g" = Guillemets] + * f <> (``inner quotation'') + * i <> (<>) ["in" = Imprimerie Nationale] + * 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] + * x dynamic style (inherits document settings) */ -char const * const language_char = "esgpfa"; +char const * const style_char = "esgpcaqbwfirjkx"; char const * const side_char = "lr" ; -char const * const times_char = "sd"; - -// List of known quote chars -char const * const quote_char = ",'`<>"; - -// Unicode characters needed by each quote type -char_type const display_quote_char[2][5] = { - { 0x201a, 0x2019, 0x2018, 0x2039, 0x203a}, - { 0x201e, 0x201d, 0x201c, 0x00ab, 0x00bb} -}; - -// Index of chars used for the quote. Index is [side, language] -int quote_index[2][6] = { - { 2, 1, 0, 0, 3, 4 }, // "'',,<>" - { 1, 1, 2, 1, 4, 3 } // "`'`'><" -}; - -// Corresponding LaTeX code, for double and single quotes. -char const * const latex_quote_t1[2][5] = { - { "\\quotesinglbase", "'", "`", - "\\guilsinglleft", "\\guilsinglright" }, - { ",,", "''", "``", "<<", ">>" } -}; - -char const * const latex_quote_ot1[2][5] = { - { "\\quotesinglbase", "'", "`", - "\\guilsinglleft", "\\guilsinglright" }, - { "\\quotedblbase", "''", "``", - "\\guillemotleft", "\\guillemotright" } -}; - -char const * const latex_quote_noligatures[2][5] = { - { "\\quotesinglbase", "\\textquoteleft", "\\textquoteright", - "\\guilsinglleft", "\\guilsinglright" }, - { "\\quotedblbase", "\\textquotedblleft", "\\textquotedblright", - "\\guillemotleft", "\\guillemotright" } -}; - -char const * const latex_quote_babel[2][5] = { - { "\\glq", "'", "`", "\\flq", "\\frq" }, - { "\\glqq", "''", "``", "\\flqq", "\\frqq" } -}; - -char const * const html_quote[2][5] = { - { "‚", "’", "‘", - "‹", "›" }, - { "„", "”", "“", "«", "»" } -}; +char const * const level_char = "sd"; } // namespace anon +///////////////////////////////////////////////////////////////////// +// +// InsetQuotesParams +// +/////////////////////////////////////////////////////////////////////// + +InsetQuotesParams quoteparams; + + +int InsetQuotesParams::stylescount() const +{ + return strlen(style_char); +} + + +char_type InsetQuotesParams::getQuoteChar(QuoteStyle const & style, QuoteLevel const & level, + QuoteSide const & side) const +{ + // main opening quotation mark + char_type left_primary; + // main closing quotation mark + char_type right_primary; + // secondary (inner, 'single') opening quotation mark + char_type left_secondary; + // secondary (inner, 'single') closing quotation mark + char_type right_secondary; + + switch (style) { + case EnglishQuotes: { + left_primary = 0x201c; // `` + right_primary = 0x201d; // '' + left_secondary = 0x2018; // ` + right_secondary = 0x2019; // ' + break; + } + case SwedishQuotes: { + left_primary = 0x201d; // '' + right_primary = 0x201d; // '' + left_secondary = 0x2019; // ' + right_secondary = 0x2019; // ' + break; + } + case GermanQuotes: { + left_primary = 0x201e; // ,, + right_primary = 0x201c; // `` + left_secondary = 0x201a; // , + right_secondary = 0x2018; // ` + break; + } + case PolishQuotes: { + left_primary = 0x201e; // ,, + right_primary = 0x201d; // '' + left_secondary = 0x201a; // , + right_secondary = 0x2019; // ' + break; + } + case SwissQuotes: { + left_primary = 0x00ab; // << + right_primary = 0x00bb; // >> + left_secondary = 0x2039; // < + right_secondary = 0x203a; // > + break; + } + case DanishQuotes: { + left_primary = 0x00bb; // >> + right_primary = 0x00ab; // << + left_secondary = 0x203a; // > + right_secondary = 0x2039; // < + break; + } + case PlainQuotes: { + left_primary = 0x0022; // " + right_primary = 0x0022; // " + left_secondary = 0x0027; // ' + right_secondary = 0x0027; // ' + break; + } + case BritishQuotes: { + left_primary = 0x2018; // ` + right_primary = 0x2019; // ' + left_secondary = 0x201c; // `` + right_secondary = 0x201d; // '' + break; + } + case SwedishGQuotes: { + left_primary = 0x00bb; // >> + right_primary = 0x00bb; // >> + left_secondary = 0x2019; // ' + right_secondary = 0x2019; // ' + break; + } + case FrenchQuotes: { + left_primary = 0x00ab; // << + right_primary = 0x00bb; // >> + left_secondary = 0x201c; // `` + right_secondary = 0x201d; // '' + break; + } + case FrenchINQuotes:{ + left_primary = 0x00ab; // << + right_primary = 0x00bb; // >> + left_secondary = 0x00ab; // << + right_secondary = 0x00bb; // >> + break; + } + case RussianQuotes:{ + left_primary = 0x00ab; // << + right_primary = 0x00bb; // >> + left_secondary = 0x201e; // ,, + right_secondary = 0x201c; // `` + break; + } + case CJKQuotes:{ + 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:{ + 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: + default: + // should not happen + left_primary = 0x003f; // ? + right_primary = 0x003f; // ? + left_secondary = 0x003f; // ? + right_secondary = 0x003f; // ? + break; + } + + switch (level) { + case SecondaryQuotes: + return (side == OpeningQuote) ? left_secondary : right_secondary; + case PrimaryQuotes: + return (side == OpeningQuote) ? left_primary : right_primary; + default: + break; + } + + // should not happen + return 0x003f; +} + + +docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op) const +{ + string res; + + switch (c){ + case 0x201a: {// , + if (op == "babel") + res = "\\glq"; + else + res = "\\quotesinglbase"; + break; + } + case 0x2019: {// ' + if (op == "int") + res = "\\textquoteleft"; + else + res = "'"; + break; + } + case 0x2018: {// ` + if (op == "int") + res = "\\textquoteright"; + else + res = "`"; + break; + } + case 0x2039: {// < + if (op == "babel") + res = "\\flq"; + else + res = "\\guilsinglleft"; + break; + } + case 0x203a: {// > + if (op == "babel") + res = "\\frq"; + else + res = "\\guilsinglright"; + break; + } + case 0x0027: {// ' (plain) + res = "\\textquotesingle"; + break; + } + case 0x201e: {// ,, + if (op == "t1") + res = ",,"; + else if (op == "babel") + res = "\\glqq"; + else + res = "\\quotedblbase"; + break; + } + case 0x201d: {// '' + if (op == "int") + res = "\\textquotedblleft"; + else + res = "''"; + break; + } + case 0x201c: {// `` + if (op == "int") + res = "\\textquotedblright"; + else + res = "``"; + break; + } + case 0x00ab: {// << + if (op == "t1") + res = "<<"; + else if (op == "babel") + res = "\\flqq"; + else + res = "\\guillemotleft"; + break; + } + case 0x00bb: {// >> + if (op == "t1") + res = ">>"; + else if (op == "babel") + res = "\\frqq"; + else + res = "\\guillemotright"; + break; + } + case 0x0022: {// " + res = "\\textquotedbl"; + break; + } + // The following are fakes + // This is just to get something symbolic + // in encodings where this chars would not be used ayway + case 0x300c: // LEFT CORNER BRACKET + res = "\\ensuremath{\\lceil}"; + break; + case 0x300d: // RIGHT CORNER BRACKET + res = "\\ensuremath{\\rfloor}"; + break; + case 0x300e: // LEFT WHITE CORNER BRACKET + res = "\\ensuremath{\\llceil}"; + break; + case 0x300f: // RIGHT WHITE CORNER BRACKET + res = "\\ensuremath{\\rrfloor}"; + break; + case 0x300a: // LEFT DOUBLE ANGLE BRACKET + res = "\\ensuremath{\\langle\\kern-2.5pt\\langle}"; + break; + case 0x300b: // RIGHT DOUBLE ANGLE BRACKET + res = "\\ensuremath{\\rangle\\kern-2.5pt\\rangle}"; + break; + case 0x3008: // LEFT ANGLE BRACKET + res = "\\ensuremath{\\langle}"; + break; + case 0x3009: // RIGHT ANGLE BRACKET + res = "\\ensuremath{\\rangle}"; + break; + default: + break; + } + + return from_ascii(res); +} + + +docstring InsetQuotesParams::getHTMLQuote(char_type c) const +{ + string res; + + switch (c){ + case 0x201a: // , + res = "‚"; + break; + case 0x2019: // ' + res = "’"; + break; + case 0x2018: // ` + res = "‘"; + break; + case 0x2039: // < + res = "‹"; + break; + case 0x203a: // > + res = "›"; + break; + case 0x0027: // ' (plain) + res = "'"; + break; + case 0x201e: // ,, + res = "„"; + break; + case 0x201d: // '' + res = "”"; + break; + case 0x201c: // `` + res = "“"; + break; + case 0x00ab: // << + res = "«"; + break; + case 0x00bb: // >> + res = "»"; + break; + case 0x0022: // " + res = """; + break; + case 0x300c: // LEFT CORNER BRACKET + res = "「"; + break; + case 0x300d: // RIGHT CORNER BRACKET + res = "」"; + break; + case 0x300e: // LEFT WHITE CORNER BRACKET + res = "『"; + break; + case 0x300f: // RIGHT WHITE CORNER BRACKET + res = "』"; + break; + case 0x300a: // LEFT DOUBLE ANGLE BRACKET + res = "《"; + break; + case 0x300b: // RIGHT DOUBLE ANGLE BRACKET + res = "》"; + break; + case 0x3008: // LEFT ANGLE BRACKET + res = "〈"; + break; + case 0x3009: // RIGHT ANGLE BRACKET + res = "〉"; + break; + default: + break; + } + + return from_ascii(res); +} + + +map InsetQuotesParams::getTypes() const +{ + map res; + + int sty, sid, lev; + QuoteStyle style; + QuoteSide side; + QuoteLevel level; + string type; + + // get all quote types + for (sty = 0; sty < stylescount(); ++sty) { + style = QuoteStyle(sty); + if (style == DynamicQuotes) + continue; + for (sid = 0; sid < 2; ++sid) { + side = QuoteSide(sid); + for (lev = 0; lev < 2; ++lev) { + type += style_char[style]; + type += side_char[sid]; + level = QuoteLevel(lev); + type += level_char[lev]; + res[type] = docstring(1, getQuoteChar(style, level, side)); + type.clear(); + } + } + } + return res; +} + + +docstring const InsetQuotesParams::getGuiLabel(QuoteStyle const & qs) +{ + 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)) + ); +} + + +///////////////////////////////////////////////////////////////////// +// +// InsetQuotes +// +/////////////////////////////////////////////////////////////////////// + InsetQuotes::InsetQuotes(Buffer * buf, string const & str) : Inset(buf) { + if (buf) + global_style_ = buf->masterBuffer()->params().quotes_style; + else + global_style_ = InsetQuotesParams::EnglishQuotes; + parseString(str); } -InsetQuotes::InsetQuotes(Buffer * buf, char_type c, QuoteTimes t, - string const & s, string const & l) - : Inset(buf), times_(t), pass_thru_(false) + +InsetQuotes::InsetQuotes(Buffer * buf, char_type c, InsetQuotesParams::QuoteLevel level, + string const & side, string const & style) + : Inset(buf), level_(level), pass_thru_(false) { + bool dynamic = false; if (buf) { - language_ = l.empty() ? buf->params().quotes_language : getLanguage(l); - fontenc_ = (buf->params().fontenc == "global") + global_style_ = buf->masterBuffer()->params().quotes_style; + fontenc_ = (buf->masterBuffer()->params().fontenc == "global") ? lyxrc.fontenc : buf->params().fontenc; + dynamic = buf->masterBuffer()->params().dynamic_quotes; } else { - language_ = l.empty() ? EnglishQuotes : getLanguage(l); + global_style_ = InsetQuotesParams::EnglishQuotes; fontenc_ = lyxrc.fontenc; } + if (style.empty()) + style_ = dynamic ? InsetQuotesParams::DynamicQuotes : global_style_; + else + style_ = getStyle(style); - if (s == "left") - side_ = LeftQuote; - else if (s == "right") - side_ = RightQuote; + if (side == "left" || side == "opening") + side_ = InsetQuotesParams::OpeningQuote; + else if (side == "right" || side == "closing") + side_ = InsetQuotesParams::ClosingQuote; else setSide(c); } @@ -144,20 +519,20 @@ docstring InsetQuotes::layoutName() const void InsetQuotes::setSide(char_type c) { - // Decide whether left or right + // Decide whether opening or closing quote switch (c) { case ' ': case '(': case '[': - side_ = LeftQuote; // left quote + side_ = InsetQuotesParams::OpeningQuote;// opening quote break; default: - side_ = RightQuote; // right quote + side_ = InsetQuotesParams::ClosingQuote;// closing quote } } -void InsetQuotes::parseString(string const & s) +void InsetQuotes::parseString(string const & s, bool const allow_wildcards) { string str = s; if (str.length() != 3) { @@ -168,90 +543,89 @@ void InsetQuotes::parseString(string const & s) int i; - for (i = 0; i < 6; ++i) { - if (str[0] == language_char[i]) { - language_ = QuoteLanguage(i); - break; + // '.' 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; } - } - if (i >= 6) { - lyxerr << "ERROR (InsetQuotes::InsetQuotes):" - " bad language specification." << endl; - language_ = EnglishQuotes; } - for (i = 0; i < 2; ++i) { - if (str[1] == side_char[i]) { - side_ = QuoteSide(i); - break; + // '.' 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; } - } - if (i >= 2) { - lyxerr << "ERROR (InsetQuotes::InsetQuotes):" - " bad side specification." << endl; - side_ = LeftQuote; } - for (i = 0; i < 2; ++i) { - if (str[2] == times_char[i]) { - times_ = QuoteTimes(i); - break; + // '.' 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; } - } - if (i >= 2) { - lyxerr << "ERROR (InsetQuotes::InsetQuotes):" - " bad times specification." << endl; - times_ = DoubleQuotes; } } -InsetQuotes::QuoteLanguage InsetQuotes::getLanguage(string const & s) + +InsetQuotesParams::QuoteStyle InsetQuotes::getStyle(string const & s) { - QuoteLanguage ql = EnglishQuotes; + InsetQuotesParams::QuoteStyle qs = InsetQuotesParams::EnglishQuotes; if (s == "english") - ql = EnglishQuotes; + qs = InsetQuotesParams::EnglishQuotes; else if (s == "swedish") - ql = SwedishQuotes; + qs = InsetQuotesParams::SwedishQuotes; else if (s == "german") - ql = GermanQuotes; + qs = InsetQuotesParams::GermanQuotes; else if (s == "polish") - ql = PolishQuotes; - else if (s == "french") - ql = FrenchQuotes; + qs = InsetQuotesParams::PolishQuotes; + else if (s == "swiss") + qs = InsetQuotesParams::SwissQuotes; else if (s == "danish") - ql = DanishQuotes; - - return ql; -} - - -map InsetQuotes::getTypes() const -{ - map res; - - int l, s, t; - QuoteLanguage lang; - QuoteSide side; - QuoteTimes times; - string type; - - // get all quote types - for (l = 0; l < 6; ++l) { - lang = QuoteLanguage(l); - for (s = 0; s < 2; ++s) { - side = QuoteSide(s); - for (t = 0; t < 2; ++t) { - type += language_char[lang]; - type += side_char[s]; - times = QuoteTimes(t); - type += times_char[t]; - res[type] = docstring(1, display_quote_char[times][quote_index[side][lang]]); - type.clear(); - } - } - } - return res; + qs = InsetQuotesParams::DanishQuotes; + else if (s == "plain") + qs = InsetQuotesParams::PlainQuotes; + else if (s == "british") + qs = InsetQuotesParams::BritishQuotes; + else if (s == "swedishg") + qs = InsetQuotesParams::SwedishGQuotes; + else if (s == "french") + qs = InsetQuotesParams::FrenchQuotes; + else if (s == "frenchin") + qs = InsetQuotesParams::FrenchINQuotes; + else if (s == "russian") + qs = InsetQuotesParams::RussianQuotes; + else if (s == "cjk") + qs = InsetQuotesParams::CJKQuotes; + else if (s == "cjkangle") + qs = InsetQuotesParams::CJKAngleQuotes; + else if (s == "dynamic") + qs = InsetQuotesParams::DynamicQuotes; + + return qs; } @@ -259,18 +633,23 @@ docstring InsetQuotes::displayString() const { // In PassThru, we use straight quotes if (pass_thru_) - return (times_ == DoubleQuotes) ? from_ascii("\"") : from_ascii("'"); + return (level_ == InsetQuotesParams::PrimaryQuotes) ? + from_ascii("\"") : from_ascii("'"); + + InsetQuotesParams::QuoteStyle style = + (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; - int const index = quote_index[side_][language_]; - docstring retdisp = docstring(1, display_quote_char[times_][index]); + docstring retdisp = docstring(1, quoteparams.getQuoteChar(style, level_, side_)); // in French, thin spaces are added inside double guillemets - // FIXME: this should be done by a separate quote type. if (prefixIs(context_lang_, "fr") - && times_ == DoubleQuotes && language_ == FrenchQuotes) { + && level_ == InsetQuotesParams::PrimaryQuotes + && (style == InsetQuotesParams::SwissQuotes + || style == InsetQuotesParams::FrenchQuotes + || style == InsetQuotesParams::FrenchINQuotes)) { // THIN SPACE (U+2009) char_type const thin_space = 0x2009; - if (side_ == LeftQuote) + if (side_ == InsetQuotesParams::OpeningQuote) retdisp += thin_space; else retdisp = thin_space + retdisp; @@ -293,19 +672,24 @@ void InsetQuotes::metrics(MetricsInfo & mi, Dimension & dim) const void InsetQuotes::draw(PainterInfo & pi, int x, int y) const { FontInfo font = pi.base.font; - font.setPaintColor(pi.textColor(font.realColor())); + if (style_ == InsetQuotesParams::DynamicQuotes) + font.setPaintColor(Color_special); + else + font.setPaintColor(pi.textColor(font.realColor())); pi.pain.text(x, y, displayString(), font); } + string InsetQuotes::getType() const { string text; - text += language_char[language_]; + text += style_char[style_]; text += side_char[side_]; - text += times_char[times_]; + text += level_char[level_]; return text; } - + + void InsetQuotes::write(ostream & os) const { os << "Quotes " << getType(); @@ -334,7 +718,7 @@ void InsetQuotes::doDispatch(Cursor & cur, FuncRequest & cmd) return; } cur.recordUndoInset(this); - parseString(cmd.getArg(1)); + parseString(cmd.getArg(1), true); cur.buffer()->updateBuffer(); break; } @@ -369,22 +753,39 @@ bool InsetQuotes::getStatus(Cursor & cur, FuncRequest const & cmd, void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const { - const int quoteind = quote_index[side_][language_]; + InsetQuotesParams::QuoteStyle style = + (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; + char_type quotechar = quoteparams.getQuoteChar(style, level_, side_); docstring qstr; // In pass-thru context, we output plain quotes if (runparams.pass_thru) - qstr = (times_ == DoubleQuotes) ? from_ascii("\"") : from_ascii("'"); + qstr = (level_ == InsetQuotesParams::PrimaryQuotes) ? from_ascii("\"") : from_ascii("'"); + else if (style == InsetQuotesParams::PlainQuotes && runparams.isFullUnicode()) { + // 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) ? + from_ascii("\\textquotedblplain") : from_ascii("\\textquotesingleplain"); + } else if (runparams.use_polyglossia) { // For polyglossia, we directly output the respective unicode chars // (spacing and kerning is then handled respectively) - qstr = docstring(1, display_quote_char[times_][quoteind]); + qstr = docstring(1, quotechar); + } + else if (style == InsetQuotesParams::CJKQuotes || style == InsetQuotesParams::CJKAngleQuotes) { + if (runparams.encoding && runparams.encoding->encodable(quotechar)) + qstr = docstring(1, quotechar); + else + qstr = quoteparams.getLaTeXQuote(quotechar, "int"); } - else if (language_ == FrenchQuotes && times_ == DoubleQuotes + else if ((style == InsetQuotesParams::SwissQuotes + || style == InsetQuotesParams::FrenchQuotes + || style == InsetQuotesParams::FrenchINQuotes) + && level_ == InsetQuotesParams::PrimaryQuotes && prefixIs(runparams.local_font->language()->code(), "fr")) { // Specific guillemets of French babel // including correct French spacing - if (side_ == LeftQuote) + if (side_ == InsetQuotesParams::OpeningQuote) qstr = from_ascii("\\og"); else qstr = from_ascii("\\fg"); @@ -392,11 +793,11 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const && !runparams.local_font->language()->internalFontEncoding()) { // Quotation marks for T1 font encoding // (using ligatures) - qstr = from_ascii(latex_quote_t1[times_][quoteind]); + qstr = quoteparams.getLaTeXQuote(quotechar, "t1"); } else if (runparams.local_font->language()->internalFontEncoding()) { // Quotation marks for internal font encodings // (ligatures not featured) - qstr = from_ascii(latex_quote_noligatures[times_][quoteind]); + qstr = quoteparams.getLaTeXQuote(quotechar, "int"); #ifdef DO_USE_DEFAULT_LANGUAGE } else if (doclang == "default") { #else @@ -405,10 +806,10 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const // Standard quotation mark macros // These are also used by babel // without fontenc (XeTeX/LuaTeX) - qstr = from_ascii(latex_quote_ot1[times_][quoteind]); + qstr = quoteparams.getLaTeXQuote(quotechar, "ot1"); } else { // Babel shorthand quotation marks (for T1/OT1) - qstr = from_ascii(latex_quote_babel[times_][quoteind]); + qstr = quoteparams.getLaTeXQuote(quotechar, "babel"); } if (!runparams.pass_thru) { @@ -427,7 +828,7 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const os << qstr; - if (prefixIs(qstr, from_ascii("\\"))) + if (prefixIs(qstr, from_ascii("\\")) && !suffixIs(qstr, '}')) // properly terminate the command depending on the context os << termcmd; } @@ -443,15 +844,18 @@ int InsetQuotes::plaintext(odocstringstream & os, docstring InsetQuotes::getQuoteEntity() const { - const int quoteind = quote_index[side_][language_]; - docstring res = from_ascii(html_quote[times_][quoteind]); + InsetQuotesParams::QuoteStyle style = + (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; + docstring res = quoteparams.getHTMLQuote(quoteparams.getQuoteChar(style, level_, side_)); // in French, thin spaces are added inside double guillemets - // FIXME: this should be done by a separate quote type. if (prefixIs(context_lang_, "fr") - && times_ == DoubleQuotes && language_ == FrenchQuotes) { + && level_ == InsetQuotesParams::PrimaryQuotes + && (style == InsetQuotesParams::FrenchQuotes + || style == InsetQuotesParams::FrenchINQuotes + || style == InsetQuotesParams::SwissQuotes)) { // THIN SPACE (U+2009) docstring const thin_space = from_ascii(" "); - if (side_ == LeftQuote) + if (side_ == InsetQuotesParams::OpeningQuote) res += thin_space; else res = thin_space + res; @@ -491,33 +895,74 @@ void InsetQuotes::updateBuffer(ParIterator const & it, UpdateType /* utype*/) BufferParams const & bp = buffer().masterBuffer()->params(); pass_thru_ = it.paragraph().isPassThru(); context_lang_ = it.paragraph().getFontSettings(bp, it.pos()).language()->code(); + fontenc_ = (bp.fontenc == "global") ? lyxrc.fontenc : bp.fontenc; + global_style_ = bp.quotes_style; } void InsetQuotes::validate(LaTeXFeatures & features) const { - char type = quote_char[quote_index[side_][language_]]; + InsetQuotesParams::QuoteStyle style = + (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_; + char_type type = quoteparams.getQuoteChar(style, level_, side_); + // Handle characters that are not natively supported by + // specific font encodings (we roll our own definitions) #ifdef DO_USE_DEFAULT_LANGUAGE if (features.bufferParams().language->lang() == "default" #else if (!features.useBabel() #endif - && !features.usePolyglossia() && fontenc_ != "T1") { - if (times_ == SingleQuotes) - switch (type) { - case ',': features.require("quotesinglbase"); break; - case '<': features.require("guilsinglleft"); break; - case '>': features.require("guilsinglright"); break; - default: break; - } - else - switch (type) { - case ',': features.require("quotedblbase"); break; - case '<': features.require("guillemotleft"); break; - case '>': features.require("guillemotright"); break; - default: break; - } + && !features.runparams().isFullUnicode() && fontenc_ != "T1") { + switch (type) { + case 0x201a: + features.require("quotesinglbase"); + break; + case 0x2039: + features.require("guilsinglleft"); + break; + case 0x203a: + features.require("guilsinglright"); + break; + case 0x201e: + features.require("quotedblbase"); + break; + case 0x00ab: + features.require("guillemotleft"); + break; + case 0x00bb: + features.require("guillemotright"); + break; + default: + break; + } + } + // Handle straight quotation marks. These need special care + // in most output formats + switch (type) { + case 0x0027: { + if (features.runparams().isFullUnicode()) + features.require("textquotesinglep"); + else + features.require("textcomp"); + break; + } + case 0x0022: { + if (features.runparams().isFullUnicode()) + features.require("textquotedblp"); + else if (fontenc_ != "T1") + features.require("textquotedbl"); + break; + } + // we fake these from math + case 0x300e: // LEFT WHITE CORNER BRACKET + case 0x300f: // RIGHT WHITE CORNER BRACKET + if (!features.runparams().encoding + || !features.runparams().encoding->encodable(type)) + features.require("stmaryrd"); + break; + default: + break; } }