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