]> git.lyx.org Git - lyx.git/blob - src/insets/InsetQuotes.cpp
Handle the case of TeX fonts LuaTeX
[lyx.git] / src / insets / InsetQuotes.cpp
1 /**
2  * \file InsetQuotes.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jean-Marc Lasgouttes
7  * \author Jürgen Spitzmüller
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetQuotes.h"
15
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "BufferView.h"
19 #include "Cursor.h"
20 #include "Dimension.h"
21 #include "Encoding.h"
22 #include "Font.h"
23 #include "FuncStatus.h"
24 #include "FuncRequest.h"
25 #include "Language.h"
26 #include "LaTeXFeatures.h"
27 #include "Lexer.h"
28 #include "LyXRC.h"
29 #include "MetricsInfo.h"
30 #include "OutputParams.h"
31 #include "output_xhtml.h"
32 #include "Paragraph.h"
33 #include "ParIterator.h"
34 #include "texstream.h"
35
36 #include "frontends/FontMetrics.h"
37 #include "frontends/Painter.h"
38
39 #include "support/debug.h"
40 #include "support/docstring.h"
41 #include "support/docstream.h"
42 #include "support/gettext.h"
43 #include "support/lstrings.h"
44
45 #include <string.h>
46
47 using namespace std;
48 using namespace lyx::support;
49
50 namespace lyx {
51
52 namespace {
53
54 /* codes used to read/write quotes to LyX files
55  * available styles:
56  * e    ``english''  (`inner quotation')
57  * s    ''swedish''  ('inner quotation')
58  * g    ,,german``   (,inner quotation`)
59  * p    ,,polish''   (,inner quotation')
60  * c    <<swiss>>    (<inner quotation>)
61  * a    >>danish<<   (>inner quotation<)
62  * q    "plain"      ('inner quotation')
63  * b    `british'    (``inner quotation'')
64  * w    >>swedishg>> ('inner quotation') ["g" = Guillemets]
65  * f    <<french>>   (``inner quotation'')
66  * i    <<frenchin>> (<<inner quotation>>) ["in" = Imprimerie Nationale]
67  * r    <<russian>>  (,,inner quotation``)
68  * j    [U+300C]cjk[U+300D]  ([U+300E]inner quotation[U+300F]) [CORNER BRACKETS]
69  * k    [U+300A]cjkangle[U+300B]  ([U+3008]inner quotation[U+3009]) [ANGLE BRACKETS]
70  * x    dynamic style (inherits document settings)
71  */
72
73 char const * const style_char = "esgpcaqbwfirjkx";
74 char const * const side_char = "lr" ;
75 char const * const level_char = "sd";
76
77 } // namespace anon
78
79
80 /////////////////////////////////////////////////////////////////////
81 //
82 // InsetQuotesParams
83 //
84 ///////////////////////////////////////////////////////////////////////
85
86 InsetQuotesParams quoteparams;
87
88
89 int InsetQuotesParams::stylescount() const
90 {
91         return strlen(style_char);
92 }
93
94
95 char_type InsetQuotesParams::getQuoteChar(QuoteStyle const & style, QuoteLevel const & level,
96                                     QuoteSide const & side) const
97 {
98         // main opening quotation mark
99         char_type left_primary;
100         // main closing quotation mark
101         char_type right_primary;
102         // secondary (inner, 'single') opening quotation mark
103         char_type left_secondary;
104         // secondary (inner, 'single') closing quotation mark
105         char_type right_secondary;
106
107         switch (style) {
108         case EnglishQuotes: {
109                 left_primary = 0x201c; // ``
110                 right_primary = 0x201d; // ''
111                 left_secondary = 0x2018; // `
112                 right_secondary = 0x2019; // '
113                 break;
114         }
115         case SwedishQuotes: {
116                 left_primary = 0x201d; // ''
117                 right_primary = 0x201d; // ''
118                 left_secondary = 0x2019; // '
119                 right_secondary = 0x2019; // '
120                 break;
121         }
122         case GermanQuotes: {
123                 left_primary = 0x201e; // ,,
124                 right_primary = 0x201c; // ``
125                 left_secondary = 0x201a; // ,
126                 right_secondary = 0x2018; // `
127                 break;
128         }
129         case PolishQuotes: {
130                 left_primary =  0x201e; // ,,
131                 right_primary = 0x201d; // ''
132                 left_secondary = 0x201a; // ,
133                 right_secondary = 0x2019; // '
134                 break;
135         }
136         case SwissQuotes: {
137                 left_primary = 0x00ab; // <<
138                 right_primary = 0x00bb; // >>
139                 left_secondary = 0x2039; // <
140                 right_secondary = 0x203a; // >
141                 break;
142         }
143         case DanishQuotes: {
144                 left_primary = 0x00bb; // >>
145                 right_primary = 0x00ab; // <<
146                 left_secondary = 0x203a; // >
147                 right_secondary = 0x2039; // <
148                 break;
149         }
150         case PlainQuotes: {
151                 left_primary = 0x0022; // "
152                 right_primary = 0x0022; // "
153                 left_secondary = 0x0027; // '
154                 right_secondary = 0x0027; // '
155                 break;
156         }
157         case BritishQuotes: {
158                 left_primary = 0x2018; // `
159                 right_primary = 0x2019; // '
160                 left_secondary = 0x201c; // ``
161                 right_secondary = 0x201d; // ''
162                 break;
163         }
164         case SwedishGQuotes: {
165                 left_primary = 0x00bb; // >>
166                 right_primary = 0x00bb; // >>
167                 left_secondary = 0x2019; // '
168                 right_secondary = 0x2019; // '
169                 break;
170         }
171         case FrenchQuotes: {
172                 left_primary = 0x00ab; // <<
173                 right_primary = 0x00bb; // >>
174                 left_secondary = 0x201c; // ``
175                 right_secondary = 0x201d; // ''
176                 break;
177         }
178         case FrenchINQuotes:{
179                 left_primary = 0x00ab; // <<
180                 right_primary = 0x00bb; // >>
181                 left_secondary =  0x00ab; // <<
182                 right_secondary = 0x00bb; // >>
183                 break;
184         }
185         case RussianQuotes:{
186                 left_primary = 0x00ab; // <<
187                 right_primary = 0x00bb; // >>
188                 left_secondary =  0x201e; // ,,
189                 right_secondary = 0x201c; // ``
190                 break;
191         }
192         case CJKQuotes:{
193                 left_primary = 0x300c; // LEFT CORNER BRACKET
194                 right_primary = 0x300d; // RIGHT CORNER BRACKET
195                 left_secondary =  0x300e; // LEFT WHITE CORNER BRACKET
196                 right_secondary = 0x300f; // RIGHT WHITE CORNER BRACKET
197                 break;
198         }
199         case CJKAngleQuotes:{
200                 left_primary = 0x300a; // LEFT DOUBLE ANGLE BRACKET
201                 right_primary = 0x300b; // RIGHT DOUBLE ANGLE BRACKET
202                 left_secondary =  0x3008; // LEFT ANGLE BRACKET
203                 right_secondary = 0x3009; // RIGHT ANGLE BRACKET
204                 break;
205         }
206         case DynamicQuotes:
207         default:
208                 // should not happen
209                 left_primary = 0x003f; // ?
210                 right_primary = 0x003f; // ?
211                 left_secondary =  0x003f; // ?
212                 right_secondary = 0x003f; // ?
213                 break;
214         }
215
216         switch (level) {
217         case SecondaryQuotes:
218                 return (side == OpeningQuote) ? left_secondary : right_secondary;
219         case PrimaryQuotes:
220                 return (side == OpeningQuote) ? left_primary : right_primary;
221         default:
222                 break;
223         }
224
225         // should not happen
226         return 0x003f;
227 }
228
229
230 docstring InsetQuotesParams::getLaTeXQuote(char_type c, string const & op) const
231 {
232         string res;
233
234         switch (c){
235         case 0x201a: {// ,
236                 if (op == "babel")
237                         res = "\\glq";
238                 else
239                         res = "\\quotesinglbase";
240                 break;
241         }
242         case 0x2019: {// '
243                 if (op == "int")
244                         res = "\\textquoteleft";
245                 else
246                         res = "'";
247                 break;
248         }
249         case 0x2018: {// `
250                 if (op == "int")
251                         res = "\\textquoteright";
252                 else
253                         res = "`";
254                 break;
255         }
256         case 0x2039: {// <
257                 if (op == "babel")
258                         res = "\\flq";
259                 else
260                         res = "\\guilsinglleft";
261                 break;
262         }
263         case 0x203a: {// >
264                 if (op == "babel")
265                         res = "\\frq";
266                 else
267                         res = "\\guilsinglright";
268                 break;
269         }
270         case 0x0027: {// ' (plain)
271                 res = "\\textquotesingle";
272                 break;
273         }
274         case 0x201e: {// ,,
275                 if (op == "t1")
276                         res = ",,";
277                 else if (op == "babel")
278                         res = "\\glqq";
279                 else
280                         res = "\\quotedblbase";
281                 break;
282         }
283         case 0x201d: {// ''
284                 if (op == "int")
285                         res = "\\textquotedblleft";
286                 else
287                         res = "''";
288                 break;
289         }
290         case 0x201c: {// ``
291                 if (op == "int")
292                         res = "\\textquotedblright";
293                 else
294                         res = "``";
295                 break;
296         }
297         case 0x00ab: {// <<
298                 if (op == "t1")
299                         res = "<<";
300                 else if (op == "babel")
301                         res = "\\flqq";
302                 else
303                         res = "\\guillemotleft";
304                 break;
305         }
306         case 0x00bb: {// >>
307                 if (op == "t1")
308                         res = ">>";
309                 else if (op == "babel")
310                         res = "\\frqq";
311                 else
312                         res = "\\guillemotright";
313                 break;
314         }
315         case 0x0022: {// "
316                 res = "\\textquotedbl";
317                 break;
318         }
319         // The following are fakes
320         // This is just to get something symbolic
321         // in encodings where this chars would not be used ayway
322         case 0x300c: // LEFT CORNER BRACKET
323                 res = "\\ensuremath{\\lceil}";
324                 break;
325         case 0x300d: // RIGHT CORNER BRACKET
326                 res = "\\ensuremath{\\rfloor}";
327                 break;
328         case 0x300e: // LEFT WHITE CORNER BRACKET
329                 res = "\\ensuremath{\\llceil}";
330                 break;
331         case 0x300f: // RIGHT WHITE CORNER BRACKET
332                 res = "\\ensuremath{\\rrfloor}";
333                 break;
334         case 0x300a: // LEFT DOUBLE ANGLE BRACKET
335                 res = "\\ensuremath{\\langle\\kern-2.5pt\\langle}";
336                 break;
337         case 0x300b: // RIGHT DOUBLE ANGLE BRACKET
338                 res = "\\ensuremath{\\rangle\\kern-2.5pt\\rangle}";
339                 break;
340         case 0x3008: // LEFT ANGLE BRACKET
341                 res = "\\ensuremath{\\langle}";
342                 break;
343         case 0x3009: // RIGHT ANGLE BRACKET
344                 res = "\\ensuremath{\\rangle}";
345                 break;
346         default:
347                 break;
348         }
349         
350         return from_ascii(res);
351 }
352
353
354 docstring InsetQuotesParams::getHTMLQuote(char_type c) const
355 {
356         string res;
357
358         switch (c){
359         case 0x201a: // ,
360                 res = "&sbquo;";
361                 break;
362         case 0x2019: // '
363                 res = "&rsquo;";
364                 break;
365         case 0x2018: // `
366                 res = "&lsquo;";
367                 break;
368         case 0x2039: // <
369                 res = "&lsaquo;";
370                 break;
371         case 0x203a: // >
372                 res = "&rsaquo;";
373                 break;
374         case 0x0027: // ' (plain)
375                 res = "&#x27;";
376                 break;
377         case 0x201e: // ,,
378                 res = "&bdquo;";
379                 break;
380         case 0x201d: // ''
381                 res = "&rdquo;";
382                 break;
383         case 0x201c: // ``
384                 res = "&ldquo;";
385                 break;
386         case 0x00ab: // <<
387                 res = "&laquo;";
388                 break;
389         case 0x00bb: // >>
390                 res = "&raquo;";
391                 break;
392         case 0x0022: // "
393                 res = "&quot;";
394                 break;
395         case 0x300c: // LEFT CORNER BRACKET
396                 res = "&#x300c;";
397                 break;
398         case 0x300d: // RIGHT CORNER BRACKET
399                 res = "&#x300d;";
400                 break;
401         case 0x300e: // LEFT WHITE CORNER BRACKET
402                 res = "&#x300e;";
403                 break;
404         case 0x300f: // RIGHT WHITE CORNER BRACKET
405                 res = "&#x300f;";
406                 break;
407         case 0x300a: // LEFT DOUBLE ANGLE BRACKET
408                 res = "&#x300a;";
409                 break;
410         case 0x300b: // RIGHT DOUBLE ANGLE BRACKET
411                 res = "&#x300b;";
412                 break;
413         case 0x3008: // LEFT ANGLE BRACKET
414                 res = "&#x3008;";
415                 break;
416         case 0x3009: // RIGHT ANGLE BRACKET
417                 res = "&#x3009;";
418                 break;
419         default:
420                 break;
421         }
422         
423         return from_ascii(res);
424 }
425
426
427 map<string, docstring> InsetQuotesParams::getTypes() const
428 {
429         map<string, docstring> res;
430
431         int sty, sid, lev;
432         QuoteStyle style;
433         QuoteSide side;
434         QuoteLevel level;
435         string type;
436
437         // get all quote types
438         for (sty = 0; sty < stylescount(); ++sty) {
439                 style = QuoteStyle(sty);
440                 if (style == DynamicQuotes)
441                         continue;
442                 for (sid = 0; sid < 2; ++sid) {
443                         side = QuoteSide(sid);
444                         for (lev = 0; lev < 2; ++lev) {
445                                 type += style_char[style];
446                                 type += side_char[sid];
447                                 level = QuoteLevel(lev);
448                                 type += level_char[lev];
449                                 res[type] = docstring(1, getQuoteChar(style, level, side));
450                                 type.clear();
451                         }
452                 }
453         }
454         return res;
455 }
456
457
458 docstring const InsetQuotesParams::getGuiLabel(QuoteStyle const & qs)
459 {
460         return bformat(_("%1$souter%2$s and %3$sinner%4$s[[quotation marks]]"),
461                 docstring(1, quoteparams.getQuoteChar(qs, PrimaryQuotes, OpeningQuote)),
462                 docstring(1, quoteparams.getQuoteChar(qs, PrimaryQuotes, ClosingQuote)),
463                 docstring(1, quoteparams.getQuoteChar(qs, SecondaryQuotes, OpeningQuote)),
464                 docstring(1, quoteparams.getQuoteChar(qs, SecondaryQuotes, ClosingQuote))
465                 );
466 }
467
468
469 /////////////////////////////////////////////////////////////////////
470 //
471 // InsetQuotes
472 //
473 ///////////////////////////////////////////////////////////////////////
474
475 InsetQuotes::InsetQuotes(Buffer * buf, string const & str) : Inset(buf)
476 {
477         if (buf) {
478                 global_style_ = buf->masterBuffer()->params().quotes_style;
479                 fontspec_ = buf->masterBuffer()->params().useNonTeXFonts;
480         }
481         else {
482                 global_style_ = InsetQuotesParams::EnglishQuotes;
483                 fontspec_ = false;
484         }
485
486         parseString(str);
487 }
488
489
490 InsetQuotes::InsetQuotes(Buffer * buf, char_type c, InsetQuotesParams::QuoteLevel level,
491                          string const & side, string const & style)
492         : Inset(buf), level_(level), pass_thru_(false), fontspec_(false)
493 {
494         bool dynamic = false;
495         if (buf) {
496                 global_style_ = buf->masterBuffer()->params().quotes_style;
497                 fontenc_ = (buf->masterBuffer()->params().fontenc == "global")
498                         ? lyxrc.fontenc : buf->params().fontenc;
499                 dynamic = buf->masterBuffer()->params().dynamic_quotes;
500                 fontspec_ = buf->masterBuffer()->params().useNonTeXFonts;
501         } else {
502                 global_style_ = InsetQuotesParams::EnglishQuotes;
503                 fontenc_ = lyxrc.fontenc;
504                 fontspec_ = false;
505         }
506         if (style.empty())
507                 style_ = dynamic ? InsetQuotesParams::DynamicQuotes : global_style_;
508         else
509                 style_ = getStyle(style);
510
511         if (side == "left" || side == "opening")
512                 side_ = InsetQuotesParams::OpeningQuote;
513         else if (side == "right" || side == "closing")
514                 side_ = InsetQuotesParams::ClosingQuote;
515         else
516                 setSide(c);
517 }
518
519
520 docstring InsetQuotes::layoutName() const
521 {
522         return from_ascii("Quotes");
523 }
524
525
526 void InsetQuotes::setSide(char_type c)
527 {
528         // Decide whether opening or closing quote
529         switch (c) {
530         case ' ':
531         case '(':
532         case '[':
533                 side_ = InsetQuotesParams::OpeningQuote;// opening quote
534                 break;
535         default:
536                 side_ = InsetQuotesParams::ClosingQuote;// closing quote
537         }
538 }
539
540
541 void InsetQuotes::parseString(string const & s, bool const allow_wildcards)
542 {
543         string str = s;
544         if (str.length() != 3) {
545                 lyxerr << "ERROR (InsetQuotes::InsetQuotes):"
546                         " bad string length." << endl;
547                 str = "eld";
548         }
549
550         int i;
551
552         // '.' wildcard means: keep current stylee
553         if (!allow_wildcards || str[0] != '.') {
554                 for (i = 0; i < quoteparams.stylescount(); ++i) {
555                         if (str[0] == style_char[i]) {
556                                 style_ = InsetQuotesParams::QuoteStyle(i);
557                                 break;
558                         }
559                 }
560                 if (i >= quoteparams.stylescount()) {
561                         LYXERR0("ERROR (InsetQuotes::InsetQuotes):"
562                                 " bad style specification.");
563                         style_ = InsetQuotesParams::EnglishQuotes;
564                 }
565         }
566
567         // '.' wildcard means: keep current side
568         if (!allow_wildcards || str[1] != '.') {
569                 for (i = 0; i < 2; ++i) {
570                         if (str[1] == side_char[i]) {
571                                 side_ = InsetQuotesParams::QuoteSide(i);
572                                 break;
573                         }
574                 }
575                 if (i >= 2) {
576                         LYXERR0("ERROR (InsetQuotes::InsetQuotes):"
577                                 " bad side specification.");
578                         side_ = InsetQuotesParams::OpeningQuote;
579                 }
580         }
581
582         // '.' wildcard means: keep current level
583         if (!allow_wildcards || str[2] != '.') {
584                 for (i = 0; i < 2; ++i) {
585                         if (str[2] == level_char[i]) {
586                                 level_ = InsetQuotesParams::QuoteLevel(i);
587                                 break;
588                         }
589                 }
590                 if (i >= 2) {
591                         LYXERR0("ERROR (InsetQuotes::InsetQuotes):"
592                                 " bad level specification.");
593                         level_ = InsetQuotesParams::PrimaryQuotes;
594                 }
595         }
596 }
597
598
599 InsetQuotesParams::QuoteStyle InsetQuotes::getStyle(string const & s)
600 {
601         InsetQuotesParams::QuoteStyle qs = InsetQuotesParams::EnglishQuotes;
602         
603         if (s == "english")
604                 qs = InsetQuotesParams::EnglishQuotes;
605         else if (s == "swedish")
606                 qs = InsetQuotesParams::SwedishQuotes;
607         else if (s == "german")
608                 qs = InsetQuotesParams::GermanQuotes;
609         else if (s == "polish")
610                 qs = InsetQuotesParams::PolishQuotes;
611         else if (s == "swiss")
612                 qs = InsetQuotesParams::SwissQuotes;
613         else if (s == "danish")
614                 qs = InsetQuotesParams::DanishQuotes;
615         else if (s == "plain")
616                 qs = InsetQuotesParams::PlainQuotes;
617         else if (s == "british")
618                 qs = InsetQuotesParams::BritishQuotes;
619         else if (s == "swedishg")
620                 qs = InsetQuotesParams::SwedishGQuotes;
621         else if (s == "french")
622                 qs = InsetQuotesParams::FrenchQuotes;
623         else if (s == "frenchin")
624                 qs = InsetQuotesParams::FrenchINQuotes;
625         else if (s == "russian")
626                 qs = InsetQuotesParams::RussianQuotes;
627         else if (s == "cjk")
628                 qs = InsetQuotesParams::CJKQuotes;
629         else if (s == "cjkangle")
630                 qs = InsetQuotesParams::CJKAngleQuotes;
631         else if (s == "dynamic")
632                 qs = InsetQuotesParams::DynamicQuotes;
633
634         return qs;
635 }
636
637
638 docstring InsetQuotes::displayString() const
639 {
640         // In PassThru, we use straight quotes
641         if (pass_thru_)
642                 return (level_ == InsetQuotesParams::PrimaryQuotes) ?
643                                         from_ascii("\"") : from_ascii("'");
644
645         InsetQuotesParams::QuoteStyle style =
646                         (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_;
647
648         docstring retdisp = docstring(1, quoteparams.getQuoteChar(style, level_, side_));
649
650         // in French, thin spaces are added inside double guillemets
651         if (prefixIs(context_lang_, "fr")
652             && level_ == InsetQuotesParams::PrimaryQuotes
653             && (style == InsetQuotesParams::SwissQuotes
654                 || style == InsetQuotesParams::FrenchQuotes
655                 || style == InsetQuotesParams::FrenchINQuotes)) {
656                 // THIN SPACE (U+2009)
657                 char_type const thin_space = 0x2009;
658                 if (side_ == InsetQuotesParams::OpeningQuote)
659                         retdisp += thin_space;
660                 else
661                         retdisp = thin_space + retdisp;
662         }
663
664         return retdisp;
665 }
666
667
668 void InsetQuotes::metrics(MetricsInfo & mi, Dimension & dim) const
669 {
670         FontInfo & font = mi.base.font;
671         frontend::FontMetrics const & fm = theFontMetrics(font);
672         dim.asc = fm.maxAscent();
673         dim.des = fm.maxDescent();
674         dim.wid = fm.width(displayString());
675 }
676
677
678 void InsetQuotes::draw(PainterInfo & pi, int x, int y) const
679 {
680         FontInfo font = pi.base.font;
681         if (style_ == InsetQuotesParams::DynamicQuotes)
682                 font.setPaintColor(Color_special);
683         else
684                 font.setPaintColor(pi.textColor(font.realColor()));
685         pi.pain.text(x, y, displayString(), font);
686 }
687
688
689 string InsetQuotes::getType() const
690 {
691         string text;
692         text += style_char[style_];
693         text += side_char[side_];
694         text += level_char[level_];
695         return text;
696 }
697
698
699 void InsetQuotes::write(ostream & os) const
700 {
701         os << "Quotes " << getType();
702 }
703
704
705 void InsetQuotes::read(Lexer & lex)
706 {
707         lex.setContext("InsetQuotes::read");
708         lex.next();
709         parseString(lex.getString());
710         lex >> "\\end_inset";
711 }
712
713
714 void InsetQuotes::doDispatch(Cursor & cur, FuncRequest & cmd)
715 {
716         switch (cmd.action()) {
717         case LFUN_INSET_MODIFY: {
718                 string const first_arg = cmd.getArg(0);
719                 bool const change_type = first_arg == "changetype";
720                 if (!change_type) {
721                         // not for us
722                         // this will not be handled higher up
723                         cur.undispatched();
724                         return;
725                 }
726                 cur.recordUndoInset(this);
727                 parseString(cmd.getArg(1), true);
728                 cur.buffer()->updateBuffer();
729                 break;
730         }
731         default:
732                 Inset::doDispatch(cur, cmd);
733                 break;
734         }
735 }
736
737
738 bool InsetQuotes::getStatus(Cursor & cur, FuncRequest const & cmd,
739                 FuncStatus & flag) const
740 {
741         switch (cmd.action()) {
742
743         case LFUN_INSET_MODIFY: {
744                 string const first_arg = cmd.getArg(0);
745                 if (first_arg == "changetype") {
746                         string const type = cmd.getArg(1);
747                         flag.setOnOff(type == getType());
748                         flag.setEnabled(!pass_thru_);
749                         return true;
750                 }
751                 return Inset::getStatus(cur, cmd, flag);
752         }
753
754         default:
755                 return Inset::getStatus(cur, cmd, flag);
756         }
757 }
758
759
760 void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const
761 {
762         InsetQuotesParams::QuoteStyle style =
763                         (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_;
764         char_type quotechar = quoteparams.getQuoteChar(style, level_, side_);
765         docstring qstr;
766
767         // In pass-thru context, we output plain quotes
768         if (runparams.pass_thru)
769                 qstr = (level_ == InsetQuotesParams::PrimaryQuotes) ? from_ascii("\"") : from_ascii("'");
770         else if (style == InsetQuotesParams::PlainQuotes && fontspec_) {
771                 // For XeTeX and LuaTeX,we need to disable mapping to get straight
772                 // quotes. We define our own commands that do this
773                 qstr = (level_ == InsetQuotesParams::PrimaryQuotes) ?
774                         from_ascii("\\textquotedblplain") : from_ascii("\\textquotesingleplain");
775         }
776         else if (runparams.use_polyglossia) {
777                 // For polyglossia, we directly output the respective unicode chars 
778                 // (spacing and kerning is then handled respectively)
779                 qstr = docstring(1, quotechar);
780         }
781         else if (style == InsetQuotesParams::CJKQuotes || style  == InsetQuotesParams::CJKAngleQuotes) {
782                 if (runparams.encoding && runparams.encoding->encodable(quotechar))
783                         qstr = docstring(1, quotechar);
784                 else
785                         qstr = quoteparams.getLaTeXQuote(quotechar, "int");
786         }
787         else if ((style == InsetQuotesParams::SwissQuotes
788                  || style == InsetQuotesParams::FrenchQuotes
789                  || style == InsetQuotesParams::FrenchINQuotes)
790                  && level_ == InsetQuotesParams::PrimaryQuotes
791                  && prefixIs(runparams.local_font->language()->code(), "fr")) {
792                 // Specific guillemets of French babel
793                 // including correct French spacing
794                 if (side_ == InsetQuotesParams::OpeningQuote)
795                         qstr = from_ascii("\\og");
796                 else
797                         qstr = from_ascii("\\fg");
798         } else if (fontenc_ == "T1"
799                    && !runparams.local_font->language()->internalFontEncoding()) {
800                 // Quotation marks for T1 font encoding
801                 // (using ligatures)
802                 qstr = quoteparams.getLaTeXQuote(quotechar, "t1");
803         } else if (runparams.local_font->language()->internalFontEncoding()) {
804                 // Quotation marks for internal font encodings
805                 // (ligatures not featured)
806                 qstr = quoteparams.getLaTeXQuote(quotechar, "int");
807 #ifdef DO_USE_DEFAULT_LANGUAGE
808         } else if (doclang == "default") {
809 #else
810         } else if (!runparams.use_babel || runparams.isFullUnicode()) {
811 #endif
812                 // Standard quotation mark macros
813                 // These are also used by babel
814                 // without fontenc (XeTeX/LuaTeX)
815                 qstr = quoteparams.getLaTeXQuote(quotechar, "ot1");
816         } else {
817                 // Babel shorthand quotation marks (for T1/OT1)
818                 qstr = quoteparams.getLaTeXQuote(quotechar, "babel");
819         }
820
821         if (!runparams.pass_thru) {
822                 // Guard against unwanted ligatures with preceding text
823                 char_type const lastchar = os.lastChar();
824                 // !` ?` => !{}` ?{}`
825                 if (prefixIs(qstr, from_ascii("`"))
826                     && (lastchar == '!' || lastchar == '?'))
827                         os << "{}";
828                 // ``` ''' ,,, <<< >>>
829                 // => `{}`` '{}'' ,{},, <{}<< >{}>>
830                 if (contains(from_ascii(",'`<>"), lastchar)
831                     && prefixIs(qstr, lastchar))
832                         os << "{}";
833         }
834
835         os << qstr;
836
837         if (prefixIs(qstr, from_ascii("\\")) && !suffixIs(qstr, '}'))
838                 // properly terminate the command depending on the context
839                 os << termcmd;
840 }
841
842
843 int InsetQuotes::plaintext(odocstringstream & os, 
844         OutputParams const &, size_t) const
845 {
846         docstring const str = displayString();
847         os << str;
848         return str.size();
849 }
850
851
852 docstring InsetQuotes::getQuoteEntity() const {
853         InsetQuotesParams::QuoteStyle style =
854                         (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_;
855         docstring res = quoteparams.getHTMLQuote(quoteparams.getQuoteChar(style, level_, side_));
856         // in French, thin spaces are added inside double guillemets
857         if (prefixIs(context_lang_, "fr")
858             && level_ == InsetQuotesParams::PrimaryQuotes
859             && (style == InsetQuotesParams::FrenchQuotes
860                 || style == InsetQuotesParams::FrenchINQuotes
861                 || style == InsetQuotesParams::SwissQuotes)) {
862                 // THIN SPACE (U+2009)
863                 docstring const thin_space = from_ascii("&#x2009;");
864                 if (side_ == InsetQuotesParams::OpeningQuote)
865                         res += thin_space;
866                 else
867                         res = thin_space + res;
868         }
869         return res;
870 }
871
872
873 int InsetQuotes::docbook(odocstream & os, OutputParams const &) const
874 {
875         os << getQuoteEntity();
876         return 0;
877 }
878
879
880 docstring InsetQuotes::xhtml(XHTMLStream & xs, OutputParams const &) const
881 {
882         xs << XHTMLStream::ESCAPE_NONE << getQuoteEntity();
883         return docstring();
884 }
885
886
887 void InsetQuotes::toString(odocstream & os) const
888 {
889         os << displayString();
890 }
891
892
893 void InsetQuotes::forOutliner(docstring & os, size_t const, bool const) const
894 {
895         os += displayString();
896 }
897
898
899 void InsetQuotes::updateBuffer(ParIterator const & it, UpdateType /* utype*/)
900 {
901         BufferParams const & bp = buffer().masterBuffer()->params();
902         pass_thru_ = it.paragraph().isPassThru();
903         context_lang_ = it.paragraph().getFontSettings(bp, it.pos()).language()->code();
904         fontenc_ = (bp.fontenc == "global") ? lyxrc.fontenc : bp.fontenc;
905         global_style_ = bp.quotes_style;
906         fontspec_ = bp.useNonTeXFonts;
907 }
908
909
910 void InsetQuotes::validate(LaTeXFeatures & features) const
911 {
912         InsetQuotesParams::QuoteStyle style =
913                         (style_ == InsetQuotesParams::DynamicQuotes) ? global_style_ : style_;
914         char_type type = quoteparams.getQuoteChar(style, level_, side_);
915
916         // Handle characters that are not natively supported by
917         // specific font encodings (we roll our own definitions)
918 #ifdef DO_USE_DEFAULT_LANGUAGE
919         if (features.bufferParams().language->lang() == "default"
920 #else
921         if (!features.useBabel()
922 #endif
923             && !features.runparams().isFullUnicode() && fontenc_ != "T1") {
924                 switch (type) {
925                 case 0x201a:
926                         features.require("quotesinglbase");
927                         break;
928                 case 0x2039:
929                         features.require("guilsinglleft");
930                         break;
931                 case 0x203a:
932                         features.require("guilsinglright");
933                         break;
934                 case 0x201e:
935                         features.require("quotedblbase");
936                         break;
937                 case 0x00ab:
938                         features.require("guillemotleft");
939                         break;
940                 case 0x00bb:
941                         features.require("guillemotright");
942                         break;
943                 default:
944                         break;
945                 }
946         }
947         // Handle straight quotation marks. These need special care
948         // in most output formats
949         switch (type) {
950         case 0x0027: {
951                 if (features.runparams().isFullUnicode() && fontspec_)
952                                 features.require("textquotesinglep");
953                         else
954                                 features.require("textcomp");
955                         break;
956         }
957         case 0x0022: {
958                 if (features.runparams().isFullUnicode() && fontspec_)
959                         features.require("textquotedblp");
960                 else if (fontenc_ != "T1")
961                         features.require("textquotedbl");
962                 break;
963         }
964         // we fake these from math
965         case 0x300e: // LEFT WHITE CORNER BRACKET
966         case 0x300f: // RIGHT WHITE CORNER BRACKET
967                 if (!features.runparams().encoding
968                     || !features.runparams().encoding->encodable(type))
969                         features.require("stmaryrd");
970                 break;
971         default:
972                 break;
973         }
974 }
975
976
977 string InsetQuotes::contextMenuName() const
978 {
979         return "context-quote";
980 }
981
982 } // namespace lyx