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