]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Run codespell on src/frontends
[lyx.git] / src / TextClass.cpp
1 /**
2  * \file TextClass.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "TextClass.h"
18
19 #include "LayoutFile.h"
20 #include "CiteEnginesList.h"
21 #include "Color.h"
22 #include "Counters.h"
23 #include "Floating.h"
24 #include "FloatList.h"
25 #include "Layout.h"
26 #include "Lexer.h"
27 #include "Font.h"
28 #include "ModuleList.h"
29
30 #include "frontends/alert.h"
31
32 #include "support/lassert.h"
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/FileName.h"
36 #include "support/filetools.h"
37 #include "support/gettext.h"
38 #include "support/lstrings.h"
39 #include "support/os.h"
40 #include "support/TempFile.h"
41
42 #include <algorithm>
43 #include <fstream>
44 #include <sstream>
45
46 #ifdef ERROR
47 #undef ERROR
48 #endif
49
50 using namespace std;
51 using namespace lyx::support;
52
53 namespace lyx {
54
55 // Keep the changes documented in the Customization manual.
56 //
57 // If you change this format, then you MUST also make sure that
58 // your changes do not invalidate the hardcoded layout file in
59 // LayoutFile.cpp. Additions will never do so, but syntax changes
60 // could. See LayoutFileList::addEmptyClass() and, especially, the
61 // definition of the layoutpost string.
62 // You should also run the development/tools/updatelayouts.py script,
63 // to update the format of all of our layout files.
64 //
65 int const LAYOUT_FORMAT = 81; // rikiheck: GuiName for counters
66
67
68 // Layout format for the current lyx file format. Controls which format is
69 // targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
70 int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
71
72
73 namespace {
74
75 bool layout2layout(FileName const & filename, FileName const & tempfile,
76                    int const format = LAYOUT_FORMAT)
77 {
78         FileName const script = libFileSearch("scripts", "layout2layout.py");
79         if (script.empty()) {
80                 LYXERR0("Could not find layout conversion "
81                         "script layout2layout.py.");
82                 return false;
83         }
84
85         ostringstream command;
86         command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
87                 << " -t " << format
88                 << ' ' << quoteName(filename.toFilesystemEncoding())
89                 << ' ' << quoteName(tempfile.toFilesystemEncoding());
90         string const command_str = command.str();
91
92         LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
93
94         cmd_ret const ret = runCommand(command_str);
95         if (ret.first != 0) {
96                 if (format == LAYOUT_FORMAT)
97                         LYXERR0("Conversion of layout with layout2layout.py has failed.");
98                 return false;
99         }
100         return true;
101 }
102
103
104 string translateReadType(TextClass::ReadType rt)
105 {
106         switch (rt) {
107         case TextClass::BASECLASS:
108                 return "textclass";
109         case TextClass::MERGE:
110                 return "input file";
111         case TextClass::MODULE:
112                 return "module file";
113         case TextClass::CITE_ENGINE:
114                 return "cite engine";
115         case TextClass::VALIDATION:
116                 return "validation";
117         }
118         // shutup warning
119         return string();
120 }
121
122 } // namespace
123
124
125 // This string should not be translated here,
126 // because it is a layout identifier.
127 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
128
129
130 /////////////////////////////////////////////////////////////////////////
131 //
132 // TextClass
133 //
134 /////////////////////////////////////////////////////////////////////////
135
136 TextClass::TextClass()
137         : loaded_(false), tex_class_avail_(false),
138           opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
139           opt_pagesize_("default|a4|a5|b5|letter|legal|executive"),
140           opt_pagestyle_("empty|plain|headings|fancy"), fontsize_format_("$$spt"), pagesize_("default"),
141           pagesize_format_("$$spaper"), pagestyle_("default"), tablestyle_("default"),
142           columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
143           outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font), 
144           titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
145           min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
146           cite_full_author_list_(true), bibintoc_(false)
147 {
148 }
149
150
151 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
152 {
153         LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
154         if (!lay.read(lexrc, *this)) {
155                 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
156                 return false;
157         }
158         // Resolve fonts
159         lay.resfont = lay.font;
160         lay.resfont.realize(defaultfont_);
161         lay.reslabelfont = lay.labelfont;
162         lay.reslabelfont.realize(defaultfont_);
163         return true; // no errors
164 }
165
166
167 enum TextClassTags {
168         TC_OUTPUTTYPE = 1,
169         TC_OUTPUTFORMAT,
170         TC_INPUT,
171         TC_STYLE,
172         TC_MODIFYSTYLE,
173         TC_PROVIDESTYLE,
174         TC_DEFAULTSTYLE,
175         TC_INSETLAYOUT,
176         TC_NOINSETLAYOUT,
177         TC_NOSTYLE,
178         TC_COLUMNS,
179         TC_SIDES,
180         TC_PAGESTYLE,
181         TC_PAGESIZE,
182         TC_DEFAULTFONT,
183         TC_SECNUMDEPTH,
184         TC_TOCDEPTH,
185         TC_CLASSOPTIONS,
186         TC_PREAMBLE,
187         TC_HTMLPREAMBLE,
188         TC_HTMLSTYLES,
189         TC_PROVIDES,
190         TC_REQUIRES,
191         TC_PKGOPTS,
192         TC_LEFTMARGIN,
193         TC_RIGHTMARGIN,
194         TC_FLOAT,
195         TC_COUNTER,
196         TC_NOCOUNTER,
197         TC_IFCOUNTER,
198         TC_NOFLOAT,
199         TC_TITLELATEXNAME,
200         TC_TITLELATEXTYPE,
201         TC_FORMAT,
202         TC_ADDTOPREAMBLE,
203         TC_ADDTOHTMLPREAMBLE,
204         TC_ADDTOHTMLSTYLES,
205         TC_DEFAULTMODULE,
206         TC_PROVIDESMODULE,
207         TC_EXCLUDESMODULE,
208         TC_HTMLTOCSECTION,
209         TC_CITEENGINE,
210         TC_ADDTOCITEENGINE,
211         TC_CITEENGINETYPE,
212         TC_CITEFORMAT,
213         TC_CITEFRAMEWORK,
214         TC_MAXCITENAMES,
215         TC_DEFAULTBIBLIO,
216         TC_FULLAUTHORLIST,
217         TC_OUTLINERNAME,
218         TC_TABLESTYLE,
219         TC_BIBINTOC
220 };
221
222
223 namespace {
224
225 LexerKeyword textClassTags[] = {
226         { "addtociteengine",   TC_ADDTOCITEENGINE },
227         { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
228         { "addtohtmlstyles",   TC_ADDTOHTMLSTYLES },
229         { "addtopreamble",     TC_ADDTOPREAMBLE },
230         { "bibintoc",          TC_BIBINTOC },
231         { "citeengine",        TC_CITEENGINE },
232         { "citeenginetype",    TC_CITEENGINETYPE },
233         { "citeformat",        TC_CITEFORMAT },
234         { "citeframework",     TC_CITEFRAMEWORK },
235         { "classoptions",      TC_CLASSOPTIONS },
236         { "columns",           TC_COLUMNS },
237         { "counter",           TC_COUNTER },
238         { "defaultbiblio",     TC_DEFAULTBIBLIO },
239         { "defaultfont",       TC_DEFAULTFONT },
240         { "defaultmodule",     TC_DEFAULTMODULE },
241         { "defaultstyle",      TC_DEFAULTSTYLE },
242         { "excludesmodule",    TC_EXCLUDESMODULE },
243         { "float",             TC_FLOAT },
244         { "format",            TC_FORMAT },
245         { "fullauthorlist",    TC_FULLAUTHORLIST },
246         { "htmlpreamble",      TC_HTMLPREAMBLE },
247         { "htmlstyles",        TC_HTMLSTYLES },
248         { "htmltocsection",    TC_HTMLTOCSECTION },
249         { "ifcounter",         TC_IFCOUNTER },
250         { "input",             TC_INPUT },
251         { "insetlayout",       TC_INSETLAYOUT },
252         { "leftmargin",        TC_LEFTMARGIN },
253         { "maxcitenames",      TC_MAXCITENAMES },
254         { "modifystyle",       TC_MODIFYSTYLE },
255         { "nocounter",         TC_NOCOUNTER },
256         { "nofloat",           TC_NOFLOAT },
257         { "noinsetlayout",     TC_NOINSETLAYOUT },
258         { "nostyle",           TC_NOSTYLE },
259         { "outlinername",      TC_OUTLINERNAME },
260         { "outputformat",      TC_OUTPUTFORMAT },
261         { "outputtype",        TC_OUTPUTTYPE },
262         { "packageoptions",    TC_PKGOPTS },
263         { "pagesize",          TC_PAGESIZE },
264         { "pagestyle",         TC_PAGESTYLE },
265         { "preamble",          TC_PREAMBLE },
266         { "provides",          TC_PROVIDES },
267         { "providesmodule",    TC_PROVIDESMODULE },
268         { "providestyle",      TC_PROVIDESTYLE },
269         { "requires",          TC_REQUIRES },
270         { "rightmargin",       TC_RIGHTMARGIN },
271         { "secnumdepth",       TC_SECNUMDEPTH },
272         { "sides",             TC_SIDES },
273         { "style",             TC_STYLE },
274         { "tablestyle",        TC_TABLESTYLE },
275         { "titlelatexname",    TC_TITLELATEXNAME },
276         { "titlelatextype",    TC_TITLELATEXTYPE },
277         { "tocdepth",          TC_TOCDEPTH }
278 };
279
280 } // namespace
281
282
283 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
284 {
285         LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
286         TempFile tmp("convertXXXXXX.layout");
287         FileName const tempfile = tmp.name();
288         bool success = layout2layout(filename, tempfile);
289         if (success)
290                 success = readWithoutConv(tempfile, rt) == OK;
291         return success;
292 }
293
294
295 std::string TextClass::convert(std::string const & str)
296 {
297         TempFile tmp1("localXXXXXX.layout");
298         FileName const fn = tmp1.name();
299         ofstream os(fn.toFilesystemEncoding().c_str());
300         os << str;
301         os.close();
302         TempFile tmp2("convert_localXXXXXX.layout");
303         FileName const tempfile = tmp2.name();
304         bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
305         if (!success)
306                 return "";
307         ifstream is(tempfile.toFilesystemEncoding().c_str());
308         string ret;
309         string tmp;
310         while (!is.eof()) {
311                 getline(is, tmp);
312                 ret += tmp + '\n';
313         }
314         is.close();
315         return ret;
316 }
317
318
319 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
320 {
321         if (!filename.isReadableFile()) {
322                 lyxerr << "Cannot read layout file `" << filename << "'."
323                        << endl;
324                 return ERROR;
325         }
326
327         LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
328                 to_utf8(makeDisplayPath(filename.absFileName())));
329
330         // Define the plain layout used in table cells, ert, etc. Note that
331         // we do this before loading any layout file, so that classes can
332         // override features of this layout if they should choose to do so.
333         if (rt == BASECLASS && !hasLayout(plain_layout_))
334                 layoutlist_.push_back(createBasicLayout(plain_layout_));
335
336         Lexer lexrc(textClassTags);
337         lexrc.setFile(filename);
338         ReturnValues retval = read(lexrc, rt);
339
340         LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
341                         to_utf8(makeDisplayPath(filename.absFileName())));
342
343         return retval;
344 }
345
346
347 bool TextClass::read(FileName const & filename, ReadType rt)
348 {
349         ReturnValues const retval = readWithoutConv(filename, rt);
350         if (retval != FORMAT_MISMATCH)
351                 return retval == OK;
352
353         bool const worx = convertLayoutFormat(filename, rt);
354         if (!worx)
355                 LYXERR0 ("Unable to convert " << filename <<
356                         " to format " << LAYOUT_FORMAT);
357         return worx;
358 }
359
360
361 TextClass::ReturnValues TextClass::validate(std::string const & str)
362 {
363         TextClass tc;
364         return tc.read(str, VALIDATION);
365 }
366
367
368 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
369 {
370         Lexer lexrc(textClassTags);
371         istringstream is(str);
372         lexrc.setStream(is);
373         ReturnValues retval = read(lexrc, rt);
374
375         if (retval != FORMAT_MISMATCH)
376                 return retval;
377
378         // write the layout string to a temporary file
379         TempFile tmp("TextClass_read");
380         FileName const tempfile = tmp.name();
381         ofstream os(tempfile.toFilesystemEncoding().c_str());
382         if (!os) {
383                 LYXERR0("Unable to create temporary file");
384                 return ERROR;
385         }
386         os << str;
387         os.close();
388
389         // now try to convert it to LAYOUT_FORMAT
390         if (!convertLayoutFormat(tempfile, rt)) {
391                 LYXERR0("Unable to convert internal layout information to format "
392                         << LAYOUT_FORMAT);
393                 return ERROR;
394         }
395
396         return OK_OLDFORMAT;
397 }
398
399
400 // Reads a textclass structure from file.
401 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
402 {
403         if (!lexrc.isOK())
404                 return ERROR;
405
406         // The first usable line should be
407         // Format LAYOUT_FORMAT
408         if (lexrc.lex() != TC_FORMAT || !lexrc.next()
409             || lexrc.getInteger() != LAYOUT_FORMAT)
410                 return FORMAT_MISMATCH;
411
412         // parsing
413         bool error = false;
414         while (lexrc.isOK() && !error) {
415                 int le = lexrc.lex();
416
417                 switch (le) {
418                 case Lexer::LEX_FEOF:
419                         continue;
420
421                 case Lexer::LEX_UNDEF:
422                         lexrc.printError("Unknown TextClass tag `$$Token'");
423                         error = true;
424                         continue;
425
426                 default:
427                         break;
428                 }
429
430                 // used below to track whether we are in an IfStyle or IfCounter tag.
431                 bool modifystyle  = false;
432                 bool providestyle = false;
433                 bool ifcounter    = false;
434
435                 switch (static_cast<TextClassTags>(le)) {
436
437                 case TC_FORMAT:
438                         lexrc.next();
439                         lexrc.printError("Duplicate Format directive");
440                         break;
441
442                 case TC_OUTPUTFORMAT:
443                         if (lexrc.next()) {
444                                 outputFormat_ = lexrc.getString();
445                                 has_output_format_ = true;
446                         }
447                         break;
448
449                 case TC_OUTPUTTYPE:
450                         readOutputType(lexrc);
451                         switch(outputType_) {
452                         case LATEX:
453                                 outputFormat_ = "latex";
454                                 break;
455                         case DOCBOOK:
456                                 outputFormat_ = "docbook";
457                                 break;
458                         case LITERATE:
459                                 outputFormat_ = "literate";
460                                 break;
461                         }
462                         break;
463
464                 case TC_INPUT: // Include file
465                         if (lexrc.next()) {
466                                 FileName tmp;
467                                 string const inc = lexrc.getString();
468                                 if (!path().empty() && (prefixIs(inc, "./") ||
469                                                         prefixIs(inc, "../")))
470                                         tmp = fileSearch(path(), inc, "layout");
471                                 else
472                                         tmp = libFileSearch("layouts", inc,
473                                                             "layout");
474
475                                 if (tmp.empty()) {
476                                         lexrc.printError("Could not find input file: " + inc);
477                                         error = true;
478                                 } else if (!read(tmp, MERGE)) {
479                                         lexrc.printError("Error reading input file: " + tmp.absFileName());
480                                         error = true;
481                                 }
482                         }
483                         break;
484
485                 case TC_DEFAULTSTYLE:
486                         if (lexrc.next()) {
487                                 docstring const name = from_utf8(subst(lexrc.getString(),
488                                                           '_', ' '));
489                                 defaultlayout_ = name;
490                         }
491                         break;
492
493                 case TC_MODIFYSTYLE:
494                         modifystyle = true;
495                 // fall through
496                 case TC_PROVIDESTYLE:
497                         // if modifystyle is true, then we got here by falling through
498                         // so we are not in an ProvideStyle block
499                         if (!modifystyle)
500                                 providestyle = true;
501                 // fall through
502                 case TC_STYLE: {
503                         if (!lexrc.next()) {
504                                 lexrc.printError("No name given for style: `$$Token'.");
505                                 error = true;
506                                 break;
507                         }
508                         docstring const name = from_utf8(subst(lexrc.getString(),
509                                                         '_', ' '));
510                         if (name.empty()) {
511                                 string s = "Could not read name for style: `$$Token' "
512                                         + lexrc.getString() + " is probably not valid UTF-8!";
513                                 lexrc.printError(s);
514                                 Layout lay;
515                                 // Since we couldn't read the name, we just scan the rest
516                                 // of the style and discard it.
517                                 error = !readStyle(lexrc, lay);
518                                 break;
519                         }
520
521                         bool const have_layout = hasLayout(name);
522
523                         // If the layout already exists, then we want to add it to
524                         // the existing layout, as long as we are not in an ProvideStyle
525                         // block.
526                         if (have_layout && !providestyle) {
527                                 Layout & lay = operator[](name);
528                                 error = !readStyle(lexrc, lay);
529                         }
530                         // If the layout does not exist, then we want to create a new
531                         // one, but not if we are in a ModifyStyle block.
532                         else if (!have_layout && !modifystyle) {
533                                 Layout layout;
534                                 layout.setName(name);
535                                 error = !readStyle(lexrc, layout);
536                                 if (!error)
537                                         layoutlist_.push_back(layout);
538
539                                 if (defaultlayout_.empty()) {
540                                         // We do not have a default layout yet, so we choose
541                                         // the first layout we encounter.
542                                         defaultlayout_ = name;
543                                 }
544                         }
545                         // There are two ways to get here:
546                         //  (i)  The layout exists but we are in an ProvideStyle block
547                         //  (ii) The layout doesn't exist, but we are in an ModifyStyle
548                         //       block.
549                         // Either way, we just scan the rest and discard it
550                         else {
551                                 Layout lay;
552                                 // signal to coverity that we do not care about the result
553                                 (void)readStyle(lexrc, lay);
554                         }
555                         break;
556                 }
557
558                 case TC_NOSTYLE:
559                         if (lexrc.next()) {
560                                 docstring const style = from_utf8(subst(lexrc.getString(),
561                                                      '_', ' '));
562                                 if (!deleteLayout(style))
563                                         lyxerr << "Cannot delete style `"
564                                                << to_utf8(style) << '\'' << endl;
565                         }
566                         break;
567
568                 case TC_NOINSETLAYOUT:
569                         if (lexrc.next()) {
570                                 docstring const style = from_utf8(subst(lexrc.getString(),
571                                                                  '_', ' '));
572                                 if (!deleteInsetLayout(style))
573                                         LYXERR0("Style `" << style << "' cannot be removed\n"
574                                                 "because it was not found!");
575                         }
576                         break;
577
578                 case TC_COLUMNS:
579                         if (lexrc.next())
580                                 columns_ = lexrc.getInteger();
581                         break;
582
583                 case TC_SIDES:
584                         if (lexrc.next()) {
585                                 switch (lexrc.getInteger()) {
586                                 case 1: sides_ = OneSide; break;
587                                 case 2: sides_ = TwoSides; break;
588                                 default:
589                                         lyxerr << "Impossible number of page"
590                                                 " sides, setting to one."
591                                                << endl;
592                                         sides_ = OneSide;
593                                         break;
594                                 }
595                         }
596                         break;
597
598                 case TC_PAGESIZE:
599                         lexrc.next();
600                         pagesize_ = rtrim(lexrc.getString());
601                         break;
602
603                 case TC_PAGESTYLE:
604                         lexrc.next();
605                         pagestyle_ = rtrim(lexrc.getString());
606                         break;
607
608                 case TC_DEFAULTFONT:
609                         defaultfont_ = lyxRead(lexrc);
610                         if (!defaultfont_.resolved()) {
611                                 lexrc.printError("Warning: defaultfont should "
612                                                  "be fully instantiated!");
613                                 defaultfont_.realize(sane_font);
614                         }
615                         break;
616
617                 case TC_SECNUMDEPTH:
618                         lexrc.next();
619                         secnumdepth_ = lexrc.getInteger();
620                         break;
621
622                 case TC_TOCDEPTH:
623                         lexrc.next();
624                         tocdepth_ = lexrc.getInteger();
625                         break;
626
627                 // First step to support options
628                 case TC_CLASSOPTIONS:
629                         readClassOptions(lexrc);
630                         break;
631
632                 case TC_PREAMBLE:
633                         preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
634                         break;
635
636                 case TC_HTMLPREAMBLE:
637                         htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
638                         break;
639
640                 case TC_HTMLSTYLES:
641                         htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
642                         break;
643
644                 case TC_HTMLTOCSECTION:
645                         html_toc_section_ = from_utf8(trim(lexrc.getString()));
646                         break;
647
648                 case TC_ADDTOPREAMBLE:
649                         preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
650                         break;
651
652                 case TC_ADDTOHTMLPREAMBLE:
653                         htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
654                         break;
655
656                 case TC_ADDTOHTMLSTYLES:
657                         htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
658                         break;
659
660                 case TC_PROVIDES: {
661                         lexrc.next();
662                         string const feature = lexrc.getString();
663                         lexrc.next();
664                         if (lexrc.getInteger())
665                                 provides_.insert(feature);
666                         else
667                                 provides_.erase(feature);
668                         break;
669                 }
670
671                 case TC_REQUIRES: {
672                         lexrc.eatLine();
673                         vector<string> const req
674                                 = getVectorFromString(lexrc.getString());
675                         required_.insert(req.begin(), req.end());
676                         break;
677                 }
678
679                 case TC_PKGOPTS : {
680                         lexrc.next();
681                         string const pkg = lexrc.getString();
682                         lexrc.next();
683                         string const options = lexrc.getString();
684                         package_options_[pkg] = options;
685                         break;
686                 }
687
688                 case TC_DEFAULTMODULE: {
689                         lexrc.next();
690                         string const module = lexrc.getString();
691                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
692                                 default_modules_.push_back(module);
693                         break;
694                 }
695
696                 case TC_PROVIDESMODULE: {
697                         lexrc.next();
698                         string const module = lexrc.getString();
699                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
700                                 provided_modules_.push_back(module);
701                         break;
702                 }
703
704                 case TC_EXCLUDESMODULE: {
705                         lexrc.next();
706                         string const module = lexrc.getString();
707                         // modules already have their own way to exclude other modules
708                         if (rt == MODULE) {
709                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
710                                 break;
711                         }
712                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
713                                 excluded_modules_.push_back(module);
714                         break;
715                 }
716
717                 case TC_LEFTMARGIN:     // left margin type
718                         if (lexrc.next())
719                                 leftmargin_ = lexrc.getDocString();
720                         break;
721
722                 case TC_RIGHTMARGIN:    // right margin type
723                         if (lexrc.next())
724                                 rightmargin_ = lexrc.getDocString();
725                         break;
726
727                 case TC_INSETLAYOUT: {
728                         if (!lexrc.next()) {
729                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
730                                 error = true;
731                                 break;
732                         }
733                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
734                         bool const validating = (rt == VALIDATION);
735                         if (name.empty()) {
736                                 string s = "Could not read name for InsetLayout: `$$Token' "
737                                         + lexrc.getString() + " is probably not valid UTF-8!";
738                                 lexrc.printError(s);
739                                 InsetLayout il;
740                                 // Since we couldn't read the name, we just scan the rest
741                                 // of the style and discard it.
742                                 il.read(lexrc, *this);
743                                 // Let's try to continue rather than abort, unless we're validating
744                                 // in which case we want to report the error
745                                 if (validating)
746                                         error = true;
747                         } else if (hasInsetLayout(name)) {
748                                 InsetLayout & il = insetlayoutlist_[name];
749                                 error = !il.read(lexrc, *this, validating);
750                         } else {
751                                 InsetLayout il;
752                                 il.setName(name);
753                                 error = !il.read(lexrc, *this, validating);
754                                 if (!error)
755                                         insetlayoutlist_[name] = il;
756                         }
757                         break;
758                 }
759
760                 case TC_FLOAT:
761                         error = !readFloat(lexrc);
762                         break;
763
764                 case TC_CITEENGINE:
765                         error = !readCiteEngine(lexrc, rt);
766                         break;
767
768                 case TC_ADDTOCITEENGINE:
769                         error = !readCiteEngine(lexrc, rt, true);
770                         break;
771
772                 case TC_CITEENGINETYPE:
773                         if (lexrc.next())
774                                 opt_enginetype_ = rtrim(lexrc.getString());
775                         break;
776
777                 case TC_CITEFORMAT:
778                         error = !readCiteFormat(lexrc, rt);
779                         break;
780
781                 case TC_CITEFRAMEWORK:
782                         lexrc.next();
783                         citeframework_ = rtrim(lexrc.getString());
784                         break;
785
786                 case TC_MAXCITENAMES:
787                         lexrc.next();
788                         maxcitenames_ = size_t(lexrc.getInteger());
789                         break;
790
791                 case TC_DEFAULTBIBLIO:
792                         if (lexrc.next()) {
793                                 vector<string> const dbs =
794                                         getVectorFromString(rtrim(lexrc.getString()), "|");
795                                 for (auto const & dbase : dbs) {
796                                         if (!contains(dbase, ':')) {
797                                                 vector<string> const enginetypes =
798                                                         getVectorFromString(opt_enginetype_, "|");
799                                                 for (string const & s: enginetypes)
800                                                         cite_default_biblio_style_[s] = dbase;
801                                         } else {
802                                                 string eng;
803                                                 string const db = split(dbase, eng, ':');
804                                                 cite_default_biblio_style_[eng] = db;
805                                         }
806                                 }
807                         }
808                         break;
809
810                 case TC_BIBINTOC:
811                         if (lexrc.next())
812                                 bibintoc_ = lexrc.getBool();
813                         break;
814
815                 case TC_FULLAUTHORLIST:
816                         if (lexrc.next())
817                                 cite_full_author_list_ &= lexrc.getBool();
818                         break;
819
820                 case TC_NOCOUNTER:
821                         if (lexrc.next()) {
822                                 docstring const cnt = lexrc.getDocString();
823                                 if (!counters_.remove(cnt))
824                                         LYXERR0("Unable to remove counter: " + to_utf8(cnt));
825                         }
826                         break;
827
828                 case TC_IFCOUNTER:
829                         ifcounter = true;
830                         // fall through
831                 case TC_COUNTER:
832                         if (lexrc.next()) {
833                                 docstring const name = lexrc.getDocString();
834                                 if (name.empty()) {
835                                         string s = "Could not read name for counter: `$$Token' "
836                                                         + lexrc.getString() + " is probably not valid UTF-8!";
837                                         lexrc.printError(s.c_str());
838                                         Counter c;
839                                         // Since we couldn't read the name, we just scan the rest
840                                         // and discard it.
841                                         c.read(lexrc);
842                                 } else
843                                         error = !counters_.read(lexrc, name, !ifcounter);
844                         }
845                         else {
846                                 lexrc.printError("No name given for style: `$$Token'.");
847                                 error = true;
848                         }
849                         break;
850
851                 case TC_TITLELATEXTYPE:
852                         readTitleType(lexrc);
853                         break;
854
855                 case TC_TITLELATEXNAME:
856                         if (lexrc.next())
857                                 titlename_ = lexrc.getString();
858                         break;
859
860                 case TC_NOFLOAT:
861                         if (lexrc.next()) {
862                                 string const nofloat = lexrc.getString();
863                                 floatlist_.erase(nofloat);
864                         }
865                         break;
866
867                 case TC_OUTLINERNAME:
868                         error = !readOutlinerName(lexrc);
869                         break;
870
871                 case TC_TABLESTYLE:
872                         lexrc.next();
873                         tablestyle_ = rtrim(lexrc.getString());
874                         break;
875                 } // end of switch
876         }
877
878         // at present, we abort if we encounter an error,
879         // so there is no point continuing.
880         if (error)
881                 return ERROR;
882
883         if (rt != BASECLASS)
884                 return OK;
885
886         if (defaultlayout_.empty()) {
887                 LYXERR0("Error: Textclass '" << name_
888                                                 << "' is missing a defaultstyle.");
889                 return ERROR;
890         }
891
892         // Try to erase "stdinsets" from the provides_ set.
893         // The
894         //   Provides stdinsets 1
895         // declaration simply tells us that the standard insets have been
896         // defined. (It's found in stdinsets.inc but could also be used in
897         // user-defined files.) There isn't really any such package. So we
898         // might as well go ahead and erase it.
899         // If we do not succeed, then it was not there, which means that
900         // the textclass did not provide the definitions of the standard
901         // insets. So we need to try to load them.
902         size_type const erased = provides_.erase("stdinsets");
903         if (!erased) {
904                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
905
906                 if (tmp.empty()) {
907                         frontend::Alert::warning(_("Missing File"),
908                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
909                         error = true;
910                 } else if (!read(tmp, MERGE)) {
911                         frontend::Alert::warning(_("Corrupt File"),
912                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
913                         error = true;
914                 }
915         }
916
917         min_toclevel_ = Layout::NOT_IN_TOC;
918         max_toclevel_ = Layout::NOT_IN_TOC;
919         for (auto const & lay : *this) {
920                 int const toclevel = lay.toclevel;
921                 if (toclevel != Layout::NOT_IN_TOC) {
922                         if (min_toclevel_ == Layout::NOT_IN_TOC)
923                                 min_toclevel_ = toclevel;
924                         else
925                                 min_toclevel_ = min(min_toclevel_, toclevel);
926                         max_toclevel_ = max(max_toclevel_, toclevel);
927                 }
928         }
929         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
930                 << ", maximum is " << max_toclevel_);
931
932         return (error ? ERROR : OK);
933 }
934
935
936 void TextClass::readTitleType(Lexer & lexrc)
937 {
938         LexerKeyword titleTypeTags[] = {
939                 { "commandafter", TITLE_COMMAND_AFTER },
940                 { "environment",  TITLE_ENVIRONMENT }
941         };
942
943         PushPopHelper pph(lexrc, titleTypeTags);
944
945         int le = lexrc.lex();
946         switch (le) {
947         case Lexer::LEX_UNDEF:
948                 lexrc.printError("Unknown output type `$$Token'");
949                 break;
950         case TITLE_COMMAND_AFTER:
951         case TITLE_ENVIRONMENT:
952                 titletype_ = static_cast<TitleLatexType>(le);
953                 break;
954         default:
955                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
956                 break;
957         }
958 }
959
960
961 void TextClass::readOutputType(Lexer & lexrc)
962 {
963         LexerKeyword outputTypeTags[] = {
964                 { "docbook",  DOCBOOK },
965                 { "latex",    LATEX },
966                 { "literate", LITERATE }
967         };
968
969         PushPopHelper pph(lexrc, outputTypeTags);
970
971         int le = lexrc.lex();
972         switch (le) {
973         case Lexer::LEX_UNDEF:
974                 lexrc.printError("Unknown output type `$$Token'");
975                 return;
976         case LATEX:
977         case DOCBOOK:
978         case LITERATE:
979                 outputType_ = static_cast<OutputType>(le);
980                 break;
981         default:
982                 LYXERR0("Unhandled value " << le);
983                 break;
984         }
985 }
986
987
988 void TextClass::readClassOptions(Lexer & lexrc)
989 {
990         enum {
991                 CO_FONTSIZE = 1,
992                 CO_FONTSIZE_FORMAT,
993                 CO_PAGESIZE,
994                 CO_PAGESIZE_FORMAT,
995                 CO_PAGESTYLE,
996                 CO_OTHER,
997                 CO_HEADER,
998                 CO_END
999         };
1000
1001         LexerKeyword classOptionsTags[] = {
1002                 {"end",       CO_END },
1003                 {"fontsize",  CO_FONTSIZE },
1004                 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1005                 {"header",    CO_HEADER },
1006                 {"other",     CO_OTHER },
1007                 {"pagesize",  CO_PAGESIZE },
1008                 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1009                 {"pagestyle", CO_PAGESTYLE }
1010         };
1011
1012         lexrc.pushTable(classOptionsTags);
1013         bool getout = false;
1014         while (!getout && lexrc.isOK()) {
1015                 int le = lexrc.lex();
1016                 switch (le) {
1017                 case Lexer::LEX_UNDEF:
1018                         lexrc.printError("Unknown ClassOption tag `$$Token'");
1019                         continue;
1020                 default:
1021                         break;
1022                 }
1023                 switch (le) {
1024                 case CO_FONTSIZE:
1025                         lexrc.next();
1026                         opt_fontsize_ = rtrim(lexrc.getString());
1027                         break;
1028                 case CO_FONTSIZE_FORMAT:
1029                         lexrc.next();
1030                         fontsize_format_ = rtrim(lexrc.getString());
1031                         break;
1032                 case CO_PAGESIZE:
1033                         lexrc.next();
1034                         opt_pagesize_ = rtrim(lexrc.getString());
1035                         break;
1036                 case CO_PAGESIZE_FORMAT:
1037                         lexrc.next();
1038                         pagesize_format_ = rtrim(lexrc.getString());
1039                         break;
1040                 case CO_PAGESTYLE:
1041                         lexrc.next();
1042                         opt_pagestyle_ = rtrim(lexrc.getString());
1043                         break;
1044                 case CO_OTHER:
1045                         lexrc.next();
1046                         if (options_.empty())
1047                                 options_ = lexrc.getString();
1048                         else
1049                                 options_ += ',' + lexrc.getString();
1050                         break;
1051                 case CO_HEADER:
1052                         lexrc.next();
1053                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
1054                         break;
1055                 case CO_END:
1056                         getout = true;
1057                         break;
1058                 }
1059         }
1060         lexrc.popTable();
1061 }
1062
1063
1064 vector<CitationStyle> const & TextClass::getCiteStyles(
1065         CiteEngineType const & type) const
1066 {
1067         static vector<CitationStyle> empty;
1068         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1069         if (it == cite_styles_.end())
1070                 return empty;
1071         return it->second;
1072 }
1073
1074
1075 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1076 {
1077         int const type = readCiteEngineType(lexrc);
1078         bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1079         bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1080         bool defce = (type & ENGINE_TYPE_DEFAULT);
1081
1082         if (rt == CITE_ENGINE) {
1083                 // The cite engines are not supposed to overwrite
1084                 // CiteStyle defined by the class or a module.
1085                 if (authoryear)
1086                         authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1087                 if (numerical)
1088                         numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1089                 if (defce)
1090                         defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1091         }
1092
1093         if (rt != CITE_ENGINE && !add) {
1094                 // Reset if we defined CiteStyle
1095                 // from the class or a module
1096                 if (authoryear)
1097                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1098                 if (numerical)
1099                         cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1100                 if (defce)
1101                         cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1102         }
1103
1104         string def;
1105         bool getout = false;
1106         while (!getout && lexrc.isOK()) {
1107                 lexrc.eatLine();
1108                 def = lexrc.getString();
1109                 def = subst(def, " ", "");
1110                 def = subst(def, "\t", "");
1111                 if (compare_ascii_no_case(def, "end") == 0) {
1112                         getout = true;
1113                         continue;
1114                 }
1115                 CitationStyle cs;
1116                 char ichar = def[0];
1117                 if (ichar == '#')
1118                         continue;
1119                 if (isUpperCase(ichar)) {
1120                         cs.forceUpperCase = true;
1121                         def[0] = lowercase(ichar);
1122                 }
1123
1124                 /** For portability reasons (between different
1125                  *  cite engines such as natbib and biblatex),
1126                  *  we distinguish between:
1127                  *  1. The LyX name as output in the LyX file
1128                  *  2. Possible aliases that might fall back to
1129                  *     the given LyX name in the current engine
1130                  *  3. The actual LaTeX command that is output
1131                  *  (2) and (3) are optional.
1132                  *  Also, the GUI string for the starred version can
1133                  *  be changed
1134                  *  The syntax is:
1135                  *  LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1136                  */
1137                 enum ScanMode {
1138                         LyXName,
1139                         Alias,
1140                         LaTeXCmd,
1141                         StarDesc
1142                 };
1143
1144                 ScanMode mode = LyXName;
1145                 ScanMode oldmode = LyXName;
1146                 string lyx_cmd;
1147                 string alias;
1148                 string latex_cmd;
1149                 string stardesc;
1150                 size_t const n = def.size();
1151                 for (size_t i = 0; i != n; ++i) {
1152                         ichar = def[i];
1153                         if (ichar == '|')
1154                                 mode = Alias;
1155                         else if (ichar == '=')
1156                                 mode = LaTeXCmd;
1157                         else if (ichar == '<') {
1158                                 oldmode = mode;
1159                                 mode = StarDesc;
1160                         } else if (ichar == '>')
1161                                 mode = oldmode;
1162                         else if (mode == LaTeXCmd)
1163                                 latex_cmd += ichar;
1164                         else if (mode == StarDesc)
1165                                 stardesc += ichar;
1166                         else if (ichar == '$')
1167                                 cs.hasQualifiedList = true;
1168                         else if (ichar == '*')
1169                                 cs.hasStarredVersion = true;
1170                         else if (ichar == '[' && cs.textAfter)
1171                                 cs.textBefore = true;
1172                         else if (ichar == '[')
1173                                 cs.textAfter = true;
1174                         else if (ichar != ']') {
1175                                 if (mode == Alias)
1176                                         alias += ichar;
1177                                 else
1178                                         lyx_cmd += ichar;
1179                         }
1180                 }
1181                 cs.name = lyx_cmd;
1182                 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1183                 if (!alias.empty()) {
1184                         vector<string> const aliases = getVectorFromString(alias);
1185                         for (string const & s: aliases)
1186                                 cite_command_aliases_[s] = lyx_cmd;
1187                 }
1188                 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1189                 int size = int(stardesc.size());
1190                 if (size > 0)
1191                         cs.stardesc = stardescs[0];
1192                 if (size > 1)
1193                         cs.startooltip = stardescs[1];
1194                 if (add) {
1195                         if (authoryear)
1196                                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1197                         if (numerical)
1198                                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1199                         if (defce)
1200                                 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1201                 } else {
1202                         if (authoryear)
1203                                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1204                         if (numerical)
1205                                 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1206                         if (defce)
1207                                 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1208                 }
1209         }
1210         // If we do AddToCiteEngine, do not apply yet,
1211         // except if we have already a style to add something to
1212         bool apply_ay = !add;
1213         bool apply_num = !add;
1214         bool apply_def = !add;
1215         if (add) {
1216                 if (type & ENGINE_TYPE_AUTHORYEAR)
1217                         apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1218                 if (type & ENGINE_TYPE_NUMERICAL)
1219                         apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1220                 if (type & ENGINE_TYPE_DEFAULT)
1221                         apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1222         }
1223
1224         // Add the styles from AddToCiteEngine to the class' styles
1225         // (but only if they are not yet defined)
1226         for (auto const & cis : class_cite_styles_) {
1227                 // Only consider the current CiteEngineType
1228                 if (!(type & cis.first))
1229                         continue;
1230                 for (auto const & ciss : cis.second) {
1231                         bool defined = false;
1232                         // Check if the style "name" is already def'ed
1233                         for (auto const & av : getCiteStyles(cis.first))
1234                                 if (av.name == ciss.name)
1235                                         defined = true;
1236                         if (!defined) {
1237                                 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1238                                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1239                                 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1240                                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1241                                 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1242                                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1243                         }
1244                 }
1245         }
1246         if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1247                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1248         if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1249                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1250         if (type & ENGINE_TYPE_DEFAULT && apply_def)
1251                 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1252         return getout;
1253 }
1254
1255
1256 int TextClass::readCiteEngineType(Lexer & lexrc) const
1257 {
1258         static_assert(ENGINE_TYPE_DEFAULT ==
1259                                   (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1260                                   "Incorrect default engine type");
1261         if (!lexrc.next()) {
1262                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1263                 return ENGINE_TYPE_DEFAULT;
1264         }
1265         string const type = rtrim(lexrc.getString());
1266         if (compare_ascii_no_case(type, "authoryear") == 0)
1267                 return ENGINE_TYPE_AUTHORYEAR;
1268         else if (compare_ascii_no_case(type, "numerical") == 0)
1269                 return ENGINE_TYPE_NUMERICAL;
1270         else if (compare_ascii_no_case(type, "default") != 0) {
1271                 string const s = "Unknown cite engine type `" + type
1272                         + "' given for token: `$$Token',";
1273                 lexrc.printError(s);
1274         }
1275         return ENGINE_TYPE_DEFAULT;
1276 }
1277
1278
1279 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1280 {
1281         int const type = readCiteEngineType(lexrc);
1282         string etype;
1283         string definition;
1284         // Cite engine definitions do not overwrite existing
1285         // definitions from the class or a module
1286         bool const overwrite = rt != CITE_ENGINE;
1287         while (lexrc.isOK()) {
1288                 lexrc.next();
1289                 etype = lexrc.getString();
1290                 if (compare_ascii_no_case(etype, "end") == 0)
1291                         break;
1292                 if (!lexrc.isOK())
1293                         return false;
1294                 lexrc.eatLine();
1295                 definition = lexrc.getString();
1296                 char initchar = etype[0];
1297                 if (initchar == '#')
1298                         continue;
1299                 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1300                         bool defined = false;
1301                         bool aydefined = false;
1302                         bool numdefined = false;
1303                         // Check if the macro is already def'ed
1304                         for (auto const & cm : cite_macros_) {
1305                                 if (!(type & cm.first))
1306                                         continue;
1307                                 if (cm.second.find(etype) != cm.second.end()) {
1308                                         if (type == cm.first)
1309                                                 // defined as default or specific type
1310                                                 defined = true;
1311                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1312                                                 // defined for author-year
1313                                                 aydefined = true;
1314                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1315                                                 // defined for numerical
1316                                                 numdefined = true;
1317                                 }
1318                         }
1319                         if (!defined || overwrite) {
1320                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1321                                         cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1322                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1323                                         cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1324                                 if (type == ENGINE_TYPE_DEFAULT)
1325                                         cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1326                         }
1327                 } else {
1328                         bool defined = false;
1329                         bool aydefined = false;
1330                         bool numdefined = false;
1331                         // Check if the format is already def'ed
1332                         for (auto const & cm : cite_formats_) {
1333                                 if (!(type & cm.first))
1334                                         continue;
1335                                 if (cm.second.find(etype) != cm.second.end()) {
1336                                         if (type == cm.first)
1337                                                 // defined as default or specific type
1338                                                 defined = true;
1339                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1340                                                 // defined for author-year
1341                                                 aydefined = true;
1342                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1343                                                 // defined for numerical
1344                                                 numdefined = true;
1345                                 }
1346                         }
1347                         if (!defined || overwrite){
1348                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1349                                         cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1350                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1351                                         cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1352                                 if (type == ENGINE_TYPE_DEFAULT)
1353                                         cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1354                         }
1355                 }
1356         }
1357         return true;
1358 }
1359
1360
1361 bool TextClass::readFloat(Lexer & lexrc)
1362 {
1363         enum {
1364                 FT_TYPE = 1,
1365                 FT_NAME,
1366                 FT_PLACEMENT,
1367                 FT_EXT,
1368                 FT_WITHIN,
1369                 FT_STYLE,
1370                 FT_LISTNAME,
1371                 FT_USESFLOAT,
1372                 FT_PREDEFINED,
1373                 FT_HTMLSTYLE,
1374                 FT_HTMLATTR,
1375                 FT_HTMLTAG,
1376                 FT_LISTCOMMAND,
1377                 FT_REFPREFIX,
1378                 FT_ALLOWED_PLACEMENT,
1379                 FT_ALLOWS_SIDEWAYS,
1380                 FT_ALLOWS_WIDE,
1381                 FT_REQUIRES,
1382                 FT_END
1383         };
1384
1385         LexerKeyword floatTags[] = {
1386                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1387                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1388                 { "allowswide", FT_ALLOWS_WIDE },
1389                 { "end", FT_END },
1390                 { "extension", FT_EXT },
1391                 { "guiname", FT_NAME },
1392                 { "htmlattr", FT_HTMLATTR },
1393                 { "htmlstyle", FT_HTMLSTYLE },
1394                 { "htmltag", FT_HTMLTAG },
1395                 { "ispredefined", FT_PREDEFINED },
1396                 { "listcommand", FT_LISTCOMMAND },
1397                 { "listname", FT_LISTNAME },
1398                 { "numberwithin", FT_WITHIN },
1399                 { "placement", FT_PLACEMENT },
1400                 { "refprefix", FT_REFPREFIX },
1401                 { "requires", FT_REQUIRES },
1402                 { "style", FT_STYLE },
1403                 { "type", FT_TYPE },
1404                 { "usesfloatpkg", FT_USESFLOAT }
1405         };
1406
1407         lexrc.pushTable(floatTags);
1408
1409         string ext;
1410         string htmlattr;
1411         docstring htmlstyle;
1412         string htmltag;
1413         string listname;
1414         string listcommand;
1415         string name;
1416         string placement;
1417         string allowed_placement = "!htbpH";
1418         string refprefix;
1419         string style;
1420         string type;
1421         string within;
1422         string required;
1423         bool usesfloat = true;
1424         bool ispredefined = false;
1425         bool allowswide = true;
1426         bool allowssideways = true;
1427
1428         bool getout = false;
1429         while (!getout && lexrc.isOK()) {
1430                 int le = lexrc.lex();
1431                 switch (le) {
1432                 case Lexer::LEX_UNDEF:
1433                         lexrc.printError("Unknown float tag `$$Token'");
1434                         continue;
1435                 default:
1436                         break;
1437                 }
1438                 switch (le) {
1439                 case FT_TYPE:
1440                         lexrc.next();
1441                         type = lexrc.getString();
1442                         if (floatlist_.typeExist(type)) {
1443                                 Floating const & fl = floatlist_.getType(type);
1444                                 placement = fl.placement();
1445                                 ext = fl.ext();
1446                                 within = fl.within();
1447                                 style = fl.style();
1448                                 name = fl.name();
1449                                 listname = fl.listName();
1450                                 usesfloat = fl.usesFloatPkg();
1451                                 ispredefined = fl.isPredefined();
1452                                 listcommand = fl.listCommand();
1453                                 refprefix = fl.refPrefix();
1454                         }
1455                         break;
1456                 case FT_NAME:
1457                         lexrc.next();
1458                         name = lexrc.getString();
1459                         break;
1460                 case FT_PLACEMENT:
1461                         lexrc.next();
1462                         placement = lexrc.getString();
1463                         break;
1464                 case FT_ALLOWED_PLACEMENT:
1465                         lexrc.next();
1466                         allowed_placement = lexrc.getString();
1467                         break;
1468                 case FT_EXT:
1469                         lexrc.next();
1470                         ext = lexrc.getString();
1471                         break;
1472                 case FT_WITHIN:
1473                         lexrc.next();
1474                         within = lexrc.getString();
1475                         if (within == "none")
1476                                 within.erase();
1477                         break;
1478                 case FT_STYLE:
1479                         lexrc.next();
1480                         style = lexrc.getString();
1481                         break;
1482                 case FT_LISTCOMMAND:
1483                         lexrc.next();
1484                         listcommand = lexrc.getString();
1485                         break;
1486                 case FT_REFPREFIX:
1487                         lexrc.next();
1488                         refprefix = lexrc.getString();
1489                         break;
1490                 case FT_LISTNAME:
1491                         lexrc.next();
1492                         listname = lexrc.getString();
1493                         break;
1494                 case FT_USESFLOAT:
1495                         lexrc.next();
1496                         usesfloat = lexrc.getBool();
1497                         break;
1498                 case FT_REQUIRES:
1499                         lexrc.next();
1500                         required = lexrc.getString();
1501                         break;
1502                 case FT_PREDEFINED:
1503                         lexrc.next();
1504                         ispredefined = lexrc.getBool();
1505                         break;
1506                 case FT_ALLOWS_SIDEWAYS:
1507                         lexrc.next();
1508                         allowssideways = lexrc.getBool();
1509                         break;
1510                 case FT_ALLOWS_WIDE:
1511                         lexrc.next();
1512                         allowswide = lexrc.getBool();
1513                         break;
1514                 case FT_HTMLATTR:
1515                         lexrc.next();
1516                         htmlattr = lexrc.getString();
1517                         break;
1518                 case FT_HTMLSTYLE:
1519                         lexrc.next();
1520                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1521                         break;
1522                 case FT_HTMLTAG:
1523                         lexrc.next();
1524                         htmltag = lexrc.getString();
1525                         break;
1526                 case FT_END:
1527                         getout = true;
1528                         break;
1529                 }
1530         }
1531
1532         lexrc.popTable();
1533
1534         // Here we have a full float if getout == true
1535         if (getout) {
1536                 if (!usesfloat && listcommand.empty()) {
1537                         // if this float uses the same auxfile as an existing one,
1538                         // there is no need for it to provide a list command.
1539                         bool found_ext = false;
1540                         for (auto const & f : floatlist_) {
1541                                 if (f.second.ext() == ext) {
1542                                         found_ext = true;
1543                                         break;
1544                                 }
1545                         }
1546                         if (!found_ext)
1547                                 LYXERR0("The layout does not provide a list command " <<
1548                                   "for the float `" << type << "'. LyX will " <<
1549                                   "not be able to produce a float list.");
1550                 }
1551                 Floating fl(type, placement, ext, within, style, name,
1552                         listname, listcommand, refprefix, allowed_placement,
1553                         htmltag, htmlattr, htmlstyle, required, usesfloat,
1554                         ispredefined, allowswide, allowssideways);
1555                 floatlist_.newFloat(fl);
1556                 // each float has its own counter
1557                 counters_.newCounter(from_ascii(type), from_ascii(within),
1558                                 docstring(), docstring(),
1559                                 bformat(_("%1$s (Float)"), _(name)));
1560                 // also define sub-float counters
1561                 docstring const subtype = "sub-" + from_ascii(type);
1562                 counters_.newCounter(subtype, from_ascii(type),
1563                                 "\\alph{" + subtype + "}", docstring(),
1564                                  bformat(_("Sub-%1$s (Float)"), _(name)));
1565         }
1566         return getout;
1567 }
1568
1569
1570 bool TextClass::readOutlinerName(Lexer & lexrc)
1571 {
1572         std::string type;
1573         docstring name;
1574         if (lexrc.next())
1575                 type = lexrc.getString();
1576         else {
1577                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1578                 return false;
1579         }
1580         if (lexrc.next())
1581                 name = lexrc.getDocString();
1582         else {
1583                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1584                 return false;
1585         }
1586         outliner_names_[type] = name;
1587     return true;
1588 }
1589
1590
1591 string const & TextClass::prerequisites(string const & sep) const
1592 {
1593         if (contains(prerequisites_, ',')) {
1594                 vector<string> const pres = getVectorFromString(prerequisites_);
1595                 prerequisites_ = getStringFromVector(pres, sep);
1596         }
1597         return prerequisites_;
1598 }
1599
1600
1601 bool TextClass::hasLayout(docstring const & n) const
1602 {
1603         docstring const name = n.empty() ? defaultLayoutName() : n;
1604         return getLayout(name) != nullptr;
1605 }
1606
1607
1608 bool TextClass::hasInsetLayout(docstring const & n) const
1609 {
1610         if (n.empty())
1611                 return false;
1612         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1613         return it != insetlayoutlist_.end();
1614 }
1615
1616
1617 Layout const & TextClass::operator[](docstring const & name) const
1618 {
1619         LATTEST(!name.empty());
1620
1621         Layout const * c = getLayout(name);
1622         if (!c) {
1623                 LYXERR0("We failed to find the layout '" << name
1624                        << "' in the layout list. You MUST investigate!");
1625                 for (auto const & lay : *this)
1626                         lyxerr  << " " << to_utf8(lay.name()) << endl;
1627
1628                 // We require the name to exist
1629                 static const Layout dummy;
1630                 LASSERT(false, return dummy);
1631         }
1632
1633         return *c;
1634 }
1635
1636
1637 Layout & TextClass::operator[](docstring const & name)
1638 {
1639         LATTEST(!name.empty());
1640         // Safe to continue, given what we do below.
1641
1642         Layout * c = getLayout(name);
1643         if (!c) {
1644                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1645                        << "' in the layout list. You MUST investigate!");
1646                 for (auto const & lay : *this)
1647                         LYXERR0(" " << to_utf8(lay.name()));
1648
1649                 // we require the name to exist
1650                 LATTEST(false);
1651                 // we are here only in release mode
1652                 layoutlist_.push_back(createBasicLayout(name, true));
1653                 c = getLayout(name);
1654         }
1655
1656         return *c;
1657 }
1658
1659
1660 bool TextClass::deleteLayout(docstring const & name)
1661 {
1662         if (name == defaultLayoutName() || name == plainLayoutName())
1663                 return false;
1664
1665         LayoutList::iterator it =
1666                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1667                         [name](const Layout &c) { return c.name() == name; });
1668
1669         LayoutList::iterator const end = layoutlist_.end();
1670         bool const ret = (it != end);
1671         layoutlist_.erase(it, end);
1672         return ret;
1673 }
1674
1675
1676 bool TextClass::deleteInsetLayout(docstring const & name)
1677 {
1678         return insetlayoutlist_.erase(name);
1679 }
1680
1681
1682 // Load textclass info if not loaded yet
1683 bool TextClass::load(string const & path) const
1684 {
1685         if (loaded_)
1686                 return true;
1687
1688         // Read style-file, provided path is searched before the system ones
1689         // If path is a file, it is loaded directly.
1690         FileName layout_file(path);
1691         if (!path.empty() && !layout_file.isReadableFile())
1692                 layout_file = FileName(addName(path, name_ + ".layout"));
1693         if (layout_file.empty() || !layout_file.exists())
1694                 layout_file = libFileSearch("layouts", name_, "layout");
1695         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1696
1697         if (!loaded_) {
1698                 lyxerr << "Error reading `"
1699                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1700                        << "'\n(Check `" << name_
1701                        << "')\nCheck your installation and "
1702                           "try Options/Reconfigure..."
1703                        << endl;
1704         }
1705
1706         return loaded_;
1707 }
1708
1709
1710 Layout const * TextClass::getLayout(docstring const & name) const
1711 {
1712         LayoutList::const_iterator cit =
1713                 find_if(begin(), end(),
1714                         [name](const Layout &c) { return c.name() == name; });
1715         if (cit == layoutlist_.end())
1716                 return nullptr;
1717
1718         return &(*cit);
1719 }
1720
1721
1722 Layout * TextClass::getLayout(docstring const & name)
1723 {
1724         LayoutList::iterator it =
1725                 find_if(layoutlist_.begin(), layoutlist_.end(),
1726                         [name](const Layout &c) { return c.name() == name; });
1727         if (it == layoutlist_.end())
1728                 return nullptr;
1729
1730         return &(*it);
1731 }
1732
1733
1734 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1735 {
1736         if (hasLayout(n))
1737                 return false;
1738
1739         layoutlist_.push_back(createBasicLayout(n, true));
1740         return true;
1741 }
1742
1743
1744 string DocumentClass::forcedLayouts() const
1745 {
1746         ostringstream os;
1747         bool first = true;
1748         for (auto const & lay : *this) {
1749                 if (lay.forcelocal > 0) {
1750                         if (first) {
1751                                 os << "Format " << LAYOUT_FORMAT << '\n';
1752                                 first = false;
1753                         }
1754                         lay.write(os);
1755                 }
1756         }
1757         return os.str();
1758 }
1759
1760
1761 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1762 {
1763         // FIXME The fix for the InsetLayout part of 4812 would be here:
1764         // Add the InsetLayout to the document class if it is not found.
1765         docstring n = name;
1766         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1767         while (!n.empty()) {
1768                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1769                 if (cit != cen && cit->first == n) {
1770                         if (cit->second.obsoleted_by().empty())
1771                                 return cit->second;
1772                         n = cit->second.obsoleted_by();
1773                         return insetLayout(n);
1774                 }
1775                 // If we have a generic prefix (e.g., "Note:"),
1776                 // try if this one alone is found.
1777                 size_t i = n.find(':');
1778                 if (i == string::npos)
1779                         break;
1780                 n = n.substr(0, i);
1781         }
1782         // Layout "name" not found.
1783         return plainInsetLayout();
1784 }
1785
1786
1787 InsetLayout const & DocumentClass::plainInsetLayout() {
1788         static const InsetLayout plain_insetlayout_;
1789         return plain_insetlayout_;
1790 }
1791
1792
1793 docstring const & TextClass::defaultLayoutName() const
1794 {
1795         return defaultlayout_;
1796 }
1797
1798
1799 Layout const & TextClass::defaultLayout() const
1800 {
1801         return operator[](defaultLayoutName());
1802 }
1803
1804
1805 bool TextClass::isDefaultLayout(Layout const & layout) const
1806 {
1807         return layout.name() == defaultLayoutName();
1808 }
1809
1810
1811 bool TextClass::isPlainLayout(Layout const & layout) const
1812 {
1813         return layout.name() == plainLayoutName();
1814 }
1815
1816
1817 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1818 {
1819         static Layout * defaultLayout = nullptr;
1820
1821         if (defaultLayout) {
1822                 defaultLayout->setUnknown(unknown);
1823                 defaultLayout->setName(name);
1824                 return *defaultLayout;
1825         }
1826
1827         static char const * s = "Margin Static\n"
1828                         "LatexType Paragraph\n"
1829                         "LatexName dummy\n"
1830                         "Align Block\n"
1831                         "AlignPossible Left, Right, Center\n"
1832                         "LabelType No_Label\n"
1833                         "End";
1834         istringstream ss(s);
1835         Lexer lex(textClassTags);
1836         lex.setStream(ss);
1837         defaultLayout = new Layout;
1838         defaultLayout->setUnknown(unknown);
1839         defaultLayout->setName(name);
1840         if (!readStyle(lex, *defaultLayout)) {
1841                 // The only way this happens is because the hardcoded layout above
1842                 // is wrong.
1843                 LATTEST(false);
1844         };
1845         return *defaultLayout;
1846 }
1847
1848
1849 DocumentClassPtr getDocumentClass(
1850                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1851                 string const & cengine, bool const clone)
1852 {
1853         DocumentClassPtr doc_class =
1854             DocumentClassPtr(new DocumentClass(baseClass));
1855         for (auto const & mod : modlist) {
1856                 LyXModule * lm = theModuleList[mod];
1857                 if (!lm) {
1858                         docstring const msg =
1859                                                 bformat(_("The module %1$s has been requested by\n"
1860                                                 "this document but has not been found in the list of\n"
1861                                                 "available modules. If you recently installed it, you\n"
1862                                                 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1863                         if (!clone)
1864                                 frontend::Alert::warning(_("Module not available"), msg);
1865                         continue;
1866                 }
1867                 if (!lm->isAvailable() && !clone) {
1868                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1869                         docstring const msg =
1870                                 bformat(_("The module %1$s requires a package that is not\n"
1871                                         "available in your LaTeX installation, or a converter that\n"
1872                                         "you have not installed. LaTeX output may not be possible.\n"
1873                                         "Missing prerequisites:\n"
1874                                                 "\t%2$s\n"
1875                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1876                                 from_utf8(mod), prereqs);
1877                         frontend::Alert::warning(_("Package not available"), msg, true);
1878                 }
1879                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1880                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1881                         docstring const msg =
1882                                                 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1883                         frontend::Alert::warning(_("Read Error"), msg);
1884                 }
1885         }
1886
1887         if (cengine.empty())
1888                 return doc_class;
1889
1890         LyXCiteEngine * ce = theCiteEnginesList[cengine];
1891         if (!ce) {
1892                 docstring const msg =
1893                                         bformat(_("The cite engine %1$s has been requested by\n"
1894                                         "this document but has not been found in the list of\n"
1895                                         "available engines. If you recently installed it, you\n"
1896                                         "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1897                 if (!clone)
1898                         frontend::Alert::warning(_("Cite Engine not available"), msg);
1899         } else if (!ce->isAvailable() && !clone) {
1900                 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1901                 docstring const msg =
1902                         bformat(_("The cite engine %1$s requires a package that is not\n"
1903                                 "available in your LaTeX installation, or a converter that\n"
1904                                 "you have not installed. LaTeX output may not be possible.\n"
1905                                 "Missing prerequisites:\n"
1906                                         "\t%2$s\n"
1907                                 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1908                         from_utf8(cengine), prereqs);
1909                 frontend::Alert::warning(_("Package not available"), msg, true);
1910         } else {
1911                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1912                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1913                         docstring const msg =
1914                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1915                         frontend::Alert::warning(_("Read Error"), msg);
1916                 }
1917         }
1918
1919         return doc_class;
1920 }
1921
1922
1923 /////////////////////////////////////////////////////////////////////////
1924 //
1925 // DocumentClass
1926 //
1927 /////////////////////////////////////////////////////////////////////////
1928
1929 DocumentClass::DocumentClass(LayoutFile const & tc)
1930         : TextClass(tc)
1931 {}
1932
1933
1934 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1935 {
1936         for (auto const & l : layoutlist_)
1937                 if (l.latexname() == lay)
1938                         return true;
1939         return false;
1940 }
1941
1942
1943 bool DocumentClass::provides(string const & p) const
1944 {
1945         return provides_.find(p) != provides_.end();
1946 }
1947
1948
1949 bool DocumentClass::hasTocLevels() const
1950 {
1951         return min_toclevel_ != Layout::NOT_IN_TOC;
1952 }
1953
1954
1955 Layout const & DocumentClass::getTOCLayout() const
1956 {
1957         // we're going to look for the layout with the minimum toclevel
1958         int minlevel = 1000;
1959         Layout const * lay = nullptr;
1960         for (auto const & l : *this) {
1961                 int const level = l.toclevel;
1962                 // we don't want Part or unnumbered sections
1963                 if (level == Layout::NOT_IN_TOC || level < 0
1964                         || level >= minlevel || l.counter.empty())
1965                         continue;
1966                 lay = &l;
1967                 minlevel = level;
1968         }
1969         if (lay)
1970                 return *lay;
1971         // hmm. that is very odd, so we'll do our best.
1972         return operator[](defaultLayoutName());
1973 }
1974
1975
1976 Layout const & DocumentClass::htmlTOCLayout() const
1977 {
1978         if (html_toc_section_.empty())
1979                 html_toc_section_ = getTOCLayout().name();
1980         return operator[](html_toc_section_);
1981 }
1982
1983
1984 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1985         string const & entry, bool const punct, string const & fallback) const
1986 {
1987         string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
1988                                 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
1989                                 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1990         if (punct)
1991                 default_format += ".";
1992
1993         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1994         if (itype == cite_formats_.end())
1995                 return default_format;
1996         map<string, string>::const_iterator it = itype->second.find(entry);
1997         if (it == itype->second.end() && !fallback.empty())
1998                 it = itype->second.find(fallback);
1999         if (it == itype->second.end())
2000                 return default_format;
2001         if (punct)
2002                 return it->second + ".";
2003         return it->second;
2004 }
2005
2006
2007 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2008         string const & macro) const
2009 {
2010         static string empty;
2011         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2012         if (itype == cite_macros_.end())
2013                 return empty;
2014         map<string, string>::const_iterator it = itype->second.find(macro);
2015         if (it == itype->second.end())
2016                 return empty;
2017         return it->second;
2018 }
2019
2020
2021 vector<string> const DocumentClass::citeCommands(
2022         CiteEngineType const & type) const
2023 {
2024         vector<CitationStyle> const styles = citeStyles(type);
2025         vector<string> cmds;
2026         for (auto const & cs : styles)
2027                 cmds.push_back(cs.name);
2028
2029         return cmds;
2030 }
2031
2032
2033 vector<CitationStyle> const & DocumentClass::citeStyles(
2034         CiteEngineType const & type) const
2035 {
2036         static vector<CitationStyle> empty;
2037         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2038         if (it == cite_styles_.end())
2039                 return empty;
2040         return it->second;
2041 }
2042
2043
2044 /////////////////////////////////////////////////////////////////////////
2045 //
2046 // PageSides
2047 //
2048 /////////////////////////////////////////////////////////////////////////
2049
2050 ostream & operator<<(ostream & os, PageSides p)
2051 {
2052         switch (p) {
2053         case OneSide:
2054                 os << '1';
2055                 break;
2056         case TwoSides:
2057                 os << '2';
2058                 break;
2059         }
2060         return os;
2061 }
2062
2063
2064 } // namespace lyx