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