]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Fix functions that used functions but did not defined it
[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 "ModuleList.h"
26
27 #include "frontends/alert.h"
28
29 #include "support/lassert.h"
30 #include "support/debug.h"
31 #include "support/FileName.h"
32 #include "support/filetools.h"
33 #include "support/gettext.h"
34 #include "support/Lexer.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 = 105; // spitz: ParskipHalf and ParskipFull class options
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"), parskip_full_(""), parskip_half_(""),
139           tablestyle_("default"), 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.eatLine();
695                         string const options = lexrc.getString();
696                         package_options_[pkg] = trim(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_PARSKIP_FULL,
1031                 CO_PARSKIP_HALF,
1032                 CO_OTHER,
1033                 CO_END
1034         };
1035
1036         LexerKeyword classOptionsTags[] = {
1037                 {"end",       CO_END },
1038                 {"fontsize",  CO_FONTSIZE },
1039                 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1040                 {"other",     CO_OTHER },
1041                 {"pagesize",  CO_PAGESIZE },
1042                 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1043                 {"pagestyle", CO_PAGESTYLE },
1044                 {"parskipfull", CO_PARSKIP_FULL },
1045                 {"parskiphalf", CO_PARSKIP_HALF }
1046         };
1047
1048         lexrc.pushTable(classOptionsTags);
1049         bool getout = false;
1050         while (!getout && lexrc.isOK()) {
1051                 int le = lexrc.lex();
1052                 switch (le) {
1053                 case Lexer::LEX_UNDEF:
1054                         lexrc.printError("Unknown ClassOption tag `$$Token'");
1055                         continue;
1056                 default:
1057                         break;
1058                 }
1059                 switch (le) {
1060                 case CO_FONTSIZE:
1061                         lexrc.next();
1062                         opt_fontsize_ = rtrim(lexrc.getString());
1063                         break;
1064                 case CO_FONTSIZE_FORMAT:
1065                         lexrc.next();
1066                         fontsize_format_ = rtrim(lexrc.getString());
1067                         break;
1068                 case CO_PAGESIZE:
1069                         lexrc.next();
1070                         opt_pagesize_ = rtrim(lexrc.getString());
1071                         break;
1072                 case CO_PAGESIZE_FORMAT:
1073                         lexrc.next();
1074                         pagesize_format_ = rtrim(lexrc.getString());
1075                         break;
1076                 case CO_PAGESTYLE:
1077                         lexrc.next();
1078                         opt_pagestyle_ = rtrim(lexrc.getString());
1079                         break;
1080                 case CO_PARSKIP_FULL:
1081                         lexrc.next();
1082                         parskip_full_ = rtrim(lexrc.getString());
1083                         break;
1084                 case CO_PARSKIP_HALF:
1085                         lexrc.next();
1086                         parskip_half_ = rtrim(lexrc.getString());
1087                         break;
1088                 case CO_OTHER:
1089                         lexrc.next();
1090                         if (options_.empty())
1091                                 options_ = lexrc.getString();
1092                         else
1093                                 options_ += ',' + lexrc.getString();
1094                         break;
1095                 case CO_END:
1096                         getout = true;
1097                         break;
1098                 }
1099         }
1100         lexrc.popTable();
1101 }
1102
1103
1104 vector<CitationStyle> const & TextClass::getCiteStyles(
1105         CiteEngineType const & type) const
1106 {
1107         static vector<CitationStyle> empty;
1108         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1109         if (it == cite_styles_.end())
1110                 return empty;
1111         return it->second;
1112 }
1113
1114
1115 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1116 {
1117         int const type = readCiteEngineType(lexrc);
1118         bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1119         bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1120         bool defce = (type & ENGINE_TYPE_DEFAULT);
1121
1122         if (rt == CITE_ENGINE) {
1123                 // The cite engines are not supposed to overwrite
1124                 // CiteStyle defined by the class or a module.
1125                 if (authoryear)
1126                         authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1127                 if (numerical)
1128                         numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1129                 if (defce)
1130                         defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1131         }
1132
1133         if (rt != CITE_ENGINE && !add) {
1134                 // Reset if we defined CiteStyle
1135                 // from the class or a module
1136                 if (authoryear)
1137                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1138                 if (numerical)
1139                         cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1140                 if (defce)
1141                         cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1142         }
1143
1144         string def;
1145         bool getout = false;
1146         while (!getout && lexrc.isOK()) {
1147                 lexrc.eatLine();
1148                 def = lexrc.getString();
1149                 def = subst(def, " ", "");
1150                 def = subst(def, "\t", "");
1151                 if (compare_ascii_no_case(def, "end") == 0) {
1152                         getout = true;
1153                         continue;
1154                 }
1155                 CitationStyle cs;
1156                 char ichar = def[0];
1157                 if (ichar == '#')
1158                         continue;
1159                 if (isUpperCase(ichar)) {
1160                         cs.forceUpperCase = true;
1161                         def[0] = lowercase(ichar);
1162                 }
1163
1164                 /** For portability reasons (between different
1165                  *  cite engines such as natbib and biblatex),
1166                  *  we distinguish between:
1167                  *  1. The LyX name as output in the LyX file
1168                  *  2. Possible aliases that might fall back to
1169                  *     the given LyX name in the current engine
1170                  *  3. The actual LaTeX command that is output
1171                  *  (2) and (3) are optional.
1172                  *  Also, the GUI string for the starred version can
1173                  *  be changed
1174                  *  The syntax is:
1175                  *  LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1176                  */
1177                 enum ScanMode {
1178                         LyXName,
1179                         Alias,
1180                         LaTeXCmd,
1181                         StarDesc
1182                 };
1183
1184                 ScanMode mode = LyXName;
1185                 ScanMode oldmode = LyXName;
1186                 string lyx_cmd;
1187                 string alias;
1188                 string latex_cmd;
1189                 string stardesc;
1190                 size_t const n = def.size();
1191                 for (size_t i = 0; i != n; ++i) {
1192                         ichar = def[i];
1193                         if (ichar == '|')
1194                                 mode = Alias;
1195                         else if (ichar == '=')
1196                                 mode = LaTeXCmd;
1197                         else if (ichar == '<') {
1198                                 oldmode = mode;
1199                                 mode = StarDesc;
1200                         } else if (ichar == '>')
1201                                 mode = oldmode;
1202                         else if (mode == LaTeXCmd)
1203                                 latex_cmd += ichar;
1204                         else if (mode == StarDesc)
1205                                 stardesc += ichar;
1206                         else if (ichar == '$')
1207                                 cs.hasQualifiedList = true;
1208                         else if (ichar == '*')
1209                                 cs.hasStarredVersion = true;
1210                         else if (ichar == '[' && cs.textAfter)
1211                                 cs.textBefore = true;
1212                         else if (ichar == '[')
1213                                 cs.textAfter = true;
1214                         else if (ichar != ']') {
1215                                 if (mode == Alias)
1216                                         alias += ichar;
1217                                 else
1218                                         lyx_cmd += ichar;
1219                         }
1220                 }
1221                 cs.name = lyx_cmd;
1222                 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1223                 if (!alias.empty()) {
1224                         vector<string> const aliases = getVectorFromString(alias);
1225                         for (string const & s: aliases)
1226                                 cite_command_aliases_[s] = lyx_cmd;
1227                 }
1228                 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1229                 int size = int(stardesc.size());
1230                 if (size > 0)
1231                         cs.stardesc = stardescs[0];
1232                 if (size > 1)
1233                         cs.startooltip = stardescs[1];
1234                 if (add) {
1235                         if (authoryear)
1236                                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1237                         if (numerical)
1238                                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1239                         if (defce)
1240                                 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1241                 } else {
1242                         if (authoryear)
1243                                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1244                         if (numerical)
1245                                 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1246                         if (defce)
1247                                 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1248                 }
1249         }
1250         // If we do AddToCiteEngine, do not apply yet,
1251         // except if we have already a style to add something to
1252         bool apply_ay = !add;
1253         bool apply_num = !add;
1254         bool apply_def = !add;
1255         if (add) {
1256                 if (type & ENGINE_TYPE_AUTHORYEAR)
1257                         apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1258                 if (type & ENGINE_TYPE_NUMERICAL)
1259                         apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1260                 if (type & ENGINE_TYPE_DEFAULT)
1261                         apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1262         }
1263
1264         // Add the styles from AddToCiteEngine to the class' styles
1265         // (but only if they are not yet defined)
1266         for (auto const & cis : class_cite_styles_) {
1267                 // Only consider the current CiteEngineType
1268                 if (!(type & cis.first))
1269                         continue;
1270                 for (auto const & ciss : cis.second) {
1271                         bool defined = false;
1272                         // Check if the style "name" is already def'ed
1273                         for (auto const & av : getCiteStyles(cis.first))
1274                                 if (av.name == ciss.name)
1275                                         defined = true;
1276                         if (!defined) {
1277                                 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1278                                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1279                                 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1280                                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1281                                 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1282                                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1283                         }
1284                 }
1285         }
1286         if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1287                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1288         if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1289                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1290         if (type & ENGINE_TYPE_DEFAULT && apply_def)
1291                 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1292         return getout;
1293 }
1294
1295
1296 int TextClass::readCiteEngineType(Lexer & lexrc) const
1297 {
1298         static_assert(ENGINE_TYPE_DEFAULT ==
1299                                   (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1300                                   "Incorrect default engine type");
1301         if (!lexrc.next()) {
1302                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1303                 return ENGINE_TYPE_DEFAULT;
1304         }
1305         string const type = rtrim(lexrc.getString());
1306         if (compare_ascii_no_case(type, "authoryear") == 0)
1307                 return ENGINE_TYPE_AUTHORYEAR;
1308         else if (compare_ascii_no_case(type, "numerical") == 0)
1309                 return ENGINE_TYPE_NUMERICAL;
1310         else if (compare_ascii_no_case(type, "default") != 0) {
1311                 string const s = "Unknown cite engine type `" + type
1312                         + "' given for token: `$$Token',";
1313                 lexrc.printError(s);
1314         }
1315         return ENGINE_TYPE_DEFAULT;
1316 }
1317
1318
1319 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1320 {
1321         int const type = readCiteEngineType(lexrc);
1322         string etype;
1323         string definition;
1324         // Cite engine definitions do not overwrite existing
1325         // definitions from the class or a module
1326         bool const overwrite = rt != CITE_ENGINE;
1327         while (lexrc.isOK()) {
1328                 lexrc.next();
1329                 etype = lexrc.getString();
1330                 if (compare_ascii_no_case(etype, "end") == 0)
1331                         break;
1332                 if (!lexrc.isOK())
1333                         return false;
1334                 lexrc.eatLine();
1335                 definition = lexrc.getString();
1336                 char initchar = etype[0];
1337                 if (initchar == '#')
1338                         continue;
1339                 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1340                         bool defined = false;
1341                         bool aydefined = false;
1342                         bool numdefined = false;
1343                         // Check if the macro is already def'ed
1344                         for (auto const & cm : cite_macros_) {
1345                                 if (!(type & cm.first))
1346                                         continue;
1347                                 if (cm.second.find(etype) != cm.second.end()) {
1348                                         if (type == cm.first)
1349                                                 // defined as default or specific type
1350                                                 defined = true;
1351                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1352                                                 // defined for author-year
1353                                                 aydefined = true;
1354                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1355                                                 // defined for numerical
1356                                                 numdefined = true;
1357                                 }
1358                         }
1359                         if (!defined || overwrite) {
1360                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1361                                         cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1362                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1363                                         cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1364                                 if (type == ENGINE_TYPE_DEFAULT)
1365                                         cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1366                         }
1367                 } else {
1368                         bool defined = false;
1369                         bool aydefined = false;
1370                         bool numdefined = false;
1371                         // Check if the format is already def'ed
1372                         for (auto const & cm : cite_formats_) {
1373                                 if (!(type & cm.first))
1374                                         continue;
1375                                 if (cm.second.find(etype) != cm.second.end()) {
1376                                         if (type == cm.first)
1377                                                 // defined as default or specific type
1378                                                 defined = true;
1379                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1380                                                 // defined for author-year
1381                                                 aydefined = true;
1382                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1383                                                 // defined for numerical
1384                                                 numdefined = true;
1385                                 }
1386                         }
1387                         if (!defined || overwrite){
1388                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1389                                         cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1390                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1391                                         cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1392                                 if (type == ENGINE_TYPE_DEFAULT)
1393                                         cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1394                         }
1395                 }
1396         }
1397         return true;
1398 }
1399
1400
1401 bool TextClass::readFloat(Lexer & lexrc)
1402 {
1403         enum {
1404                 FT_TYPE = 1,
1405                 FT_NAME,
1406                 FT_PLACEMENT,
1407                 FT_EXT,
1408                 FT_WITHIN,
1409                 FT_STYLE,
1410                 FT_LISTNAME,
1411                 FT_USESFLOAT,
1412                 FT_PREDEFINED,
1413                 FT_HTMLSTYLE,
1414                 FT_HTMLATTR,
1415                 FT_HTMLTAG,
1416                 FT_DOCBOOKATTR,
1417                 FT_DOCBOOKFLOATTYPE,
1418                 FT_DOCBOOKTAG,
1419                 FT_DOCBOOKTAGTYPE,
1420                 FT_DOCBOOKCAPTION,
1421                 FT_LISTCOMMAND,
1422                 FT_REFPREFIX,
1423                 FT_ALLOWED_PLACEMENT,
1424                 FT_ALLOWS_SIDEWAYS,
1425                 FT_ALLOWS_WIDE,
1426                 FT_REQUIRES,
1427                 FT_PRETTYFORMAT,
1428                 FT_END
1429         };
1430
1431         LexerKeyword floatTags[] = {
1432                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1433                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1434                 { "allowswide", FT_ALLOWS_WIDE },
1435                 { "docbookattr", FT_DOCBOOKATTR },
1436                 { "docbookcaption", FT_DOCBOOKCAPTION },
1437                 { "docbookfloattype", FT_DOCBOOKFLOATTYPE },
1438                 { "docbooktag", FT_DOCBOOKTAG },
1439                 { "docbooktagtype", FT_DOCBOOKTAGTYPE },
1440                 { "end", FT_END },
1441                 { "extension", FT_EXT },
1442                 { "guiname", FT_NAME },
1443                 { "htmlattr", FT_HTMLATTR },
1444                 { "htmlstyle", FT_HTMLSTYLE },
1445                 { "htmltag", FT_HTMLTAG },
1446                 { "ispredefined", FT_PREDEFINED },
1447                 { "listcommand", FT_LISTCOMMAND },
1448                 { "listname", FT_LISTNAME },
1449                 { "numberwithin", FT_WITHIN },
1450                 { "placement", FT_PLACEMENT },
1451                 { "prettyformat", FT_PRETTYFORMAT },
1452                 { "refprefix", FT_REFPREFIX },
1453                 { "requires", FT_REQUIRES },
1454                 { "style", FT_STYLE },
1455                 { "type", FT_TYPE },
1456                 { "usesfloatpkg", FT_USESFLOAT }
1457         };
1458
1459         lexrc.pushTable(floatTags);
1460
1461         string ext;
1462         string htmlattr;
1463         docstring htmlstyle;
1464         string htmltag;
1465         string docbookattr;
1466         string docbookcaption;
1467         string docbooktag;
1468         string docbooktagtype;
1469         string docbookfloattype;
1470         string listname;
1471         string listcommand;
1472         string name;
1473         string placement;
1474         string allowed_placement = "!htbpH";
1475         string refprefix;
1476         string style;
1477         string type;
1478         string within;
1479         string required;
1480         docstring prettyformat;
1481         bool usesfloat = true;
1482         bool ispredefined = false;
1483         bool allowswide = true;
1484         bool allowssideways = true;
1485
1486         bool getout = false;
1487         while (!getout && lexrc.isOK()) {
1488                 int le = lexrc.lex();
1489                 switch (le) {
1490                 case Lexer::LEX_UNDEF:
1491                         lexrc.printError("Unknown float tag `$$Token'");
1492                         continue;
1493                 default:
1494                         break;
1495                 }
1496                 switch (le) {
1497                 case FT_TYPE:
1498                         lexrc.next();
1499                         type = lexrc.getString();
1500                         if (floatlist_.typeExist(type)) {
1501                                 Floating const & fl = floatlist_.getType(type);
1502                                 placement = fl.placement();
1503                                 ext = fl.ext();
1504                                 within = fl.within();
1505                                 style = fl.style();
1506                                 name = fl.name();
1507                                 listname = fl.listName();
1508                                 usesfloat = fl.usesFloatPkg();
1509                                 ispredefined = fl.isPredefined();
1510                                 listcommand = fl.listCommand();
1511                                 refprefix = fl.refPrefix();
1512                         }
1513                         break;
1514                 case FT_NAME:
1515                         lexrc.next();
1516                         name = lexrc.getString();
1517                         break;
1518                 case FT_PLACEMENT:
1519                         lexrc.next();
1520                         placement = lexrc.getString();
1521                         break;
1522                 case FT_ALLOWED_PLACEMENT:
1523                         lexrc.next();
1524                         allowed_placement = lexrc.getString();
1525                         break;
1526                 case FT_EXT:
1527                         lexrc.next();
1528                         ext = lexrc.getString();
1529                         break;
1530                 case FT_WITHIN:
1531                         lexrc.next();
1532                         within = lexrc.getString();
1533                         if (within == "none")
1534                                 within.erase();
1535                         break;
1536                 case FT_STYLE:
1537                         lexrc.next();
1538                         style = lexrc.getString();
1539                         break;
1540                 case FT_LISTCOMMAND:
1541                         lexrc.next();
1542                         listcommand = lexrc.getString();
1543                         break;
1544                 case FT_REFPREFIX:
1545                         lexrc.next();
1546                         refprefix = lexrc.getString();
1547                         break;
1548                 case FT_LISTNAME:
1549                         lexrc.next();
1550                         listname = lexrc.getString();
1551                         break;
1552                 case FT_USESFLOAT:
1553                         lexrc.next();
1554                         usesfloat = lexrc.getBool();
1555                         break;
1556                 case FT_REQUIRES:
1557                         lexrc.next();
1558                         required = lexrc.getString();
1559                         break;
1560                 case FT_PREDEFINED:
1561                         lexrc.next();
1562                         ispredefined = lexrc.getBool();
1563                         break;
1564                 case FT_ALLOWS_SIDEWAYS:
1565                         lexrc.next();
1566                         allowssideways = lexrc.getBool();
1567                         break;
1568                 case FT_ALLOWS_WIDE:
1569                         lexrc.next();
1570                         allowswide = lexrc.getBool();
1571                         break;
1572                 case FT_HTMLATTR:
1573                         lexrc.next();
1574                         htmlattr = lexrc.getString();
1575                         break;
1576                 case FT_HTMLSTYLE:
1577                         lexrc.next();
1578                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1579                         break;
1580                 case FT_HTMLTAG:
1581                         lexrc.next();
1582                         htmltag = lexrc.getString();
1583                         break;
1584                 case FT_PRETTYFORMAT:
1585                         lexrc.next();
1586                         prettyformat = lexrc.getDocString();
1587                         break;
1588                 case FT_DOCBOOKATTR:
1589                         lexrc.next();
1590                         docbookattr = lexrc.getString();
1591                         break;
1592                 case FT_DOCBOOKCAPTION:
1593                         lexrc.next();
1594                         docbookcaption = lexrc.getString();
1595                         break;
1596                 case FT_DOCBOOKTAG:
1597                         lexrc.next();
1598                         docbooktag = lexrc.getString();
1599                         break;
1600                 case FT_DOCBOOKTAGTYPE:
1601                         lexrc.next();
1602                         docbooktagtype = lexrc.getString();
1603                         break;
1604                 case FT_DOCBOOKFLOATTYPE:
1605                         lexrc.next();
1606                         docbookfloattype = lexrc.getString();
1607                         break;
1608                 case FT_END:
1609                         getout = true;
1610                         break;
1611                 default:
1612                         LYXERR0("Unhandled value " << le << " in TextClass::readFloat.");
1613                         break;
1614                 }
1615         }
1616
1617         lexrc.popTable();
1618
1619         // Here we have a full float if getout == true
1620         if (getout) {
1621                 if (!usesfloat && listcommand.empty()) {
1622                         // if this float uses the same auxfile as an existing one,
1623                         // there is no need for it to provide a list command.
1624                         bool found_ext = false;
1625                         for (auto const & f : floatlist_) {
1626                                 if (f.second.ext() == ext) {
1627                                         found_ext = true;
1628                                         break;
1629                                 }
1630                         }
1631                         if (!found_ext)
1632                                 LYXERR0("The layout does not provide a list command " <<
1633                                   "for the float `" << type << "'. LyX will " <<
1634                                   "not be able to produce a float list.");
1635                 }
1636                 Floating fl(type, placement, ext, within, style, name,
1637                         listname, listcommand, refprefix, allowed_placement,
1638                         htmltag, htmlattr, htmlstyle, docbooktag, docbookattr,
1639                         docbooktagtype, docbookfloattype, docbookcaption,
1640                         required, usesfloat, ispredefined,
1641                 allowswide, allowssideways);
1642                 floatlist_.newFloat(fl);
1643                 // each float has its own counter
1644                 counters_.newCounter(from_ascii(type), from_ascii(within),
1645                                 docstring(), docstring(),
1646                                 prettyformat.empty() ? bformat(_("%1$s ##"), _(name)) : prettyformat,
1647                                 bformat(_("%1$s (Float)"), _(name)));
1648                 // also define sub-float counters
1649                 docstring const subtype = "sub-" + from_ascii(type);
1650                 counters_.newCounter(subtype, from_ascii(type),
1651                                 "\\alph{" + subtype + "}", docstring(),
1652                                 prettyformat.empty() ? bformat(_("Sub-%1$s ##"), _(name)) : prettyformat,
1653                                 bformat(_("Sub-%1$s (Float)"), _(name)));
1654         }
1655         return getout;
1656 }
1657
1658
1659 bool TextClass::readOutlinerName(Lexer & lexrc)
1660 {
1661         std::string type;
1662         docstring name;
1663         if (lexrc.next())
1664                 type = lexrc.getString();
1665         else {
1666                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1667                 return false;
1668         }
1669         if (lexrc.next())
1670                 name = lexrc.getDocString();
1671         else {
1672                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1673                 return false;
1674         }
1675         outliner_names_[type] = name;
1676     return true;
1677 }
1678
1679
1680 string const & TextClass::prerequisites(string const & sep) const
1681 {
1682         if (contains(prerequisites_, ',')) {
1683                 vector<string> const pres = getVectorFromString(prerequisites_);
1684                 prerequisites_ = getStringFromVector(pres, sep);
1685         }
1686         return prerequisites_;
1687 }
1688
1689
1690 bool TextClass::hasLayout(docstring const & n) const
1691 {
1692         docstring const name = n.empty() ? defaultLayoutName() : n;
1693         return getLayout(name) != nullptr;
1694 }
1695
1696
1697 bool TextClass::hasInsetLayout(docstring const & n) const
1698 {
1699         if (n.empty())
1700                 return false;
1701         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1702         return it != insetlayoutlist_.end();
1703 }
1704
1705
1706 Layout const & TextClass::operator[](docstring const & name) const
1707 {
1708         LATTEST(!name.empty());
1709
1710         Layout const * c = getLayout(name);
1711         if (!c) {
1712                 LYXERR0("We failed to find the layout '" << name
1713                        << "' in the layout list. You MUST investigate!");
1714                 for (auto const & lay : *this)
1715                         lyxerr  << " " << to_utf8(lay.name()) << endl;
1716
1717                 // We require the name to exist
1718                 static const Layout dummy;
1719                 LASSERT(false, return dummy);
1720         }
1721
1722         return *c;
1723 }
1724
1725
1726 Layout & TextClass::operator[](docstring const & name)
1727 {
1728         LATTEST(!name.empty());
1729         // Safe to continue, given what we do below.
1730
1731         Layout * c = getLayout(name);
1732         if (!c) {
1733                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1734                        << "' in the layout list. You MUST investigate!");
1735                 for (auto const & lay : *this)
1736                         LYXERR0(" " << to_utf8(lay.name()));
1737
1738                 // we require the name to exist
1739                 LATTEST(false);
1740                 // we are here only in release mode
1741                 layoutlist_.push_back(createBasicLayout(name, true));
1742                 c = getLayout(name);
1743         }
1744
1745         return *c;
1746 }
1747
1748
1749 bool TextClass::deleteLayout(docstring const & name)
1750 {
1751         if (name == defaultLayoutName() || name == plainLayoutName())
1752                 return false;
1753
1754         LayoutList::iterator it =
1755                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1756                         [name](const Layout &c) { return c.name() == name; });
1757
1758         LayoutList::iterator const end = layoutlist_.end();
1759         bool const ret = (it != end);
1760         layoutlist_.erase(it, end);
1761         return ret;
1762 }
1763
1764
1765 bool TextClass::deleteInsetLayout(docstring const & name)
1766 {
1767         return insetlayoutlist_.erase(name);
1768 }
1769
1770
1771 // Load textclass info if not loaded yet
1772 bool TextClass::load(string const & path) const
1773 {
1774         if (loaded_)
1775                 return true;
1776
1777         // Read style-file, provided path is searched before the system ones
1778         // If path is a file, it is loaded directly.
1779         FileName layout_file(path);
1780         if (!path.empty() && !layout_file.isReadableFile())
1781                 layout_file = FileName(addName(path, name_ + ".layout"));
1782         if (layout_file.empty() || !layout_file.exists())
1783                 layout_file = libFileSearch("layouts", name_, "layout");
1784         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1785
1786         if (!loaded_) {
1787                 lyxerr << "Error reading `"
1788                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1789                        << "'\n(Check `" << name_
1790                        << "')\nCheck your installation and "
1791                           "try Tools/Reconfigure..."
1792                        << endl;
1793         }
1794
1795         return loaded_;
1796 }
1797
1798
1799 Layout const * TextClass::getLayout(docstring const & name) const
1800 {
1801         LayoutList::const_iterator cit =
1802                 find_if(begin(), end(),
1803                         [name](const Layout &c) { return c.name() == name; });
1804         if (cit == layoutlist_.end())
1805                 return nullptr;
1806
1807         return &(*cit);
1808 }
1809
1810
1811 Layout * TextClass::getLayout(docstring const & name)
1812 {
1813         LayoutList::iterator it =
1814                 find_if(layoutlist_.begin(), layoutlist_.end(),
1815                         [name](const Layout &c) { return c.name() == name; });
1816         if (it == layoutlist_.end())
1817                 return nullptr;
1818
1819         return &(*it);
1820 }
1821
1822
1823 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1824 {
1825         if (hasLayout(n))
1826                 return false;
1827
1828         layoutlist_.push_back(createBasicLayout(n, true));
1829         return true;
1830 }
1831
1832
1833 string DocumentClass::forcedLayouts() const
1834 {
1835         ostringstream os;
1836         bool first = true;
1837         for (auto const & lay : *this) {
1838                 if (lay.forcelocal > 0) {
1839                         if (first) {
1840                                 os << "Format " << LAYOUT_FORMAT << '\n';
1841                                 first = false;
1842                         }
1843                         lay.write(os);
1844                 }
1845         }
1846         return os.str();
1847 }
1848
1849
1850 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1851 {
1852         // FIXME The fix for the InsetLayout part of 4812 would be here:
1853         // Add the InsetLayout to the document class if it is not found.
1854         docstring n = name;
1855         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1856         while (!n.empty()) {
1857                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1858                 if (cit != cen && cit->first == n) {
1859                         if (cit->second.obsoleted_by().empty())
1860                                 return cit->second;
1861                         n = cit->second.obsoleted_by();
1862                         return insetLayout(n);
1863                 }
1864                 // If we have a generic prefix (e.g., "Note:"),
1865                 // try if this one alone is found.
1866                 size_t i = n.find(':');
1867                 if (i == string::npos)
1868                         break;
1869                 n = n.substr(0, i);
1870         }
1871         // Layout "name" not found.
1872         return plainInsetLayout();
1873 }
1874
1875
1876 InsetLayout const & DocumentClass::plainInsetLayout() {
1877         static const InsetLayout plain_insetlayout_;
1878         return plain_insetlayout_;
1879 }
1880
1881
1882 docstring const & TextClass::defaultLayoutName() const
1883 {
1884         return defaultlayout_;
1885 }
1886
1887
1888 Layout const & TextClass::defaultLayout() const
1889 {
1890         return operator[](defaultLayoutName());
1891 }
1892
1893
1894 bool TextClass::isDefaultLayout(Layout const & layout) const
1895 {
1896         return layout.name() == defaultLayoutName();
1897 }
1898
1899
1900 bool TextClass::isPlainLayout(Layout const & layout) const
1901 {
1902         return layout.name() == plainLayoutName();
1903 }
1904
1905
1906 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1907 {
1908         static Layout * defaultLayout = nullptr;
1909
1910         if (defaultLayout) {
1911                 defaultLayout->setUnknown(unknown);
1912                 defaultLayout->setName(name);
1913                 return *defaultLayout;
1914         }
1915
1916         static char const * s = "Margin Static\n"
1917                         "LatexType Paragraph\n"
1918                         "LatexName dummy\n"
1919                         "Align Block\n"
1920                         "AlignPossible Left, Right, Center\n"
1921                         "LabelType No_Label\n"
1922                         "End";
1923         istringstream ss(s);
1924         Lexer lex(textClassTags);
1925         lex.setStream(ss);
1926         defaultLayout = new Layout;
1927         defaultLayout->setUnknown(unknown);
1928         defaultLayout->setName(name);
1929         if (!readStyle(lex, *defaultLayout, BASECLASS)) {
1930                 // The only way this happens is because the hardcoded layout above
1931                 // is wrong.
1932                 LATTEST(false);
1933         };
1934         return *defaultLayout;
1935 }
1936
1937
1938 DocumentClassPtr getDocumentClass(LayoutFile const & baseClass, LayoutModuleList const & modlist,
1939                 string const & cengine, bool clone, bool internal)
1940 {
1941         bool const show_warnings = !clone && !internal;
1942         DocumentClassPtr doc_class =
1943             DocumentClassPtr(new DocumentClass(baseClass));
1944         for (auto const & mod : modlist) {
1945                 LyXModule * lm = theModuleList[mod];
1946                 if (!lm) {
1947                         if (show_warnings) {
1948                                 docstring const msg =
1949                                         bformat(_("The module %1$s has been requested by\n"
1950                                         "this document but has not been found in the list of\n"
1951                                         "available modules. If you recently installed it, you\n"
1952                                         "probably need to reconfigure LyX.\n"), from_utf8(mod));
1953                                 frontend::Alert::warning(_("Module not available"), msg);
1954                         }
1955                         continue;
1956                 }
1957                 if (!lm->isAvailable() && show_warnings) {
1958                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1959                         docstring const msg =
1960                                 bformat(_("The module %1$s requires a package that is not\n"
1961                                         "available in your LaTeX installation, or a converter that\n"
1962                                         "you have not installed. LaTeX output may not be possible.\n"
1963                                         "Missing prerequisites:\n"
1964                                                 "\t%2$s\n"
1965                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1966                                 from_utf8(mod), prereqs);
1967                         frontend::Alert::warning(_("Package not available"), msg, true);
1968                 }
1969                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1970                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1971                         docstring const msg =
1972                                 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1973                         frontend::Alert::warning(_("Read Error"), msg);
1974                 }
1975         }
1976
1977         if (cengine.empty())
1978                 return doc_class;
1979
1980         LyXCiteEngine * ce = theCiteEnginesList[cengine];
1981         if (!ce) {
1982                 if (show_warnings) {
1983                         docstring const msg =
1984                                 bformat(_("The cite engine %1$s has been requested by\n"
1985                                 "this document but has not been found in the list of\n"
1986                                 "available engines. If you recently installed it, you\n"
1987                                 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1988                         frontend::Alert::warning(_("Cite Engine not available"), msg);
1989                 }
1990         } else if (!ce->isAvailable() && show_warnings) {
1991                 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1992                 docstring const msg =
1993                         bformat(_("The cite engine %1$s requires a package that is not\n"
1994                                 "available in your LaTeX installation, or a converter that\n"
1995                                 "you have not installed. LaTeX output may not be possible.\n"
1996                                 "Missing prerequisites:\n"
1997                                         "\t%2$s\n"
1998                                 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1999                         from_utf8(cengine), prereqs);
2000                 frontend::Alert::warning(_("Package not available"), msg, true);
2001         } else {
2002                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
2003                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
2004                         docstring const msg =
2005                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
2006                         frontend::Alert::warning(_("Read Error"), msg);
2007                 }
2008         }
2009
2010         return doc_class;
2011 }
2012
2013
2014 /////////////////////////////////////////////////////////////////////////
2015 //
2016 // DocumentClass
2017 //
2018 /////////////////////////////////////////////////////////////////////////
2019
2020 DocumentClass::DocumentClass(LayoutFile const & tc)
2021         : TextClass(tc)
2022 {}
2023
2024
2025 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
2026 {
2027         for (auto const & l : layoutlist_)
2028                 if (l.latexname() == lay)
2029                         return true;
2030         return false;
2031 }
2032
2033
2034 bool DocumentClass::provides(string const & p) const
2035 {
2036         return provides_.find(p) != provides_.end();
2037 }
2038
2039
2040 bool DocumentClass::hasTocLevels() const
2041 {
2042         return min_toclevel_ != Layout::NOT_IN_TOC;
2043 }
2044
2045
2046 Layout const & DocumentClass::getTOCLayout() const
2047 {
2048         // we're going to look for the layout with the minimum toclevel
2049         int minlevel = 1000;
2050         Layout const * lay = nullptr;
2051         for (auto const & l : *this) {
2052                 int const level = l.toclevel;
2053                 // we don't want Part or unnumbered sections
2054                 if (level == Layout::NOT_IN_TOC || level < 0
2055                         || level >= minlevel || l.counter.empty())
2056                         continue;
2057                 lay = &l;
2058                 minlevel = level;
2059         }
2060         if (lay)
2061                 return *lay;
2062         // hmm. that is very odd, so we'll do our best.
2063         return operator[](defaultLayoutName());
2064 }
2065
2066
2067 Layout const & DocumentClass::htmlTOCLayout() const
2068 {
2069         if (html_toc_section_.empty())
2070                 html_toc_section_ = getTOCLayout().name();
2071         return operator[](html_toc_section_);
2072 }
2073
2074
2075 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
2076         string const & entry, bool const punct, string const & fallback) const
2077 {
2078         string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
2079                                 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
2080                                 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
2081         if (punct)
2082                 default_format += ".";
2083
2084         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2085         if (itype == cite_formats_.end())
2086                 return default_format;
2087         map<string, string>::const_iterator it = itype->second.find(entry);
2088         if (it == itype->second.end() && !fallback.empty())
2089                 it = itype->second.find(fallback);
2090         if (it == itype->second.end())
2091                 return default_format;
2092         if (punct)
2093                 return it->second + ".";
2094         return it->second;
2095 }
2096
2097
2098 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2099         string const & macro) const
2100 {
2101         static string empty;
2102         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2103         if (itype == cite_macros_.end())
2104                 return empty;
2105         map<string, string>::const_iterator it = itype->second.find(macro);
2106         if (it == itype->second.end())
2107                 return empty;
2108         return it->second;
2109 }
2110
2111
2112 vector<string> const DocumentClass::citeCommands(
2113         CiteEngineType const & type) const
2114 {
2115         vector<CitationStyle> const styles = citeStyles(type);
2116         vector<string> cmds;
2117         cmds.reserve(styles.size());
2118         for (auto const & cs : styles)
2119                 cmds.push_back(cs.name);
2120
2121         return cmds;
2122 }
2123
2124
2125 vector<CitationStyle> const & DocumentClass::citeStyles(
2126         CiteEngineType const & type) const
2127 {
2128         static vector<CitationStyle> empty;
2129         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2130         if (it == cite_styles_.end())
2131                 return empty;
2132         return it->second;
2133 }
2134
2135
2136 /////////////////////////////////////////////////////////////////////////
2137 //
2138 // PageSides
2139 //
2140 /////////////////////////////////////////////////////////////////////////
2141
2142 ostream & operator<<(ostream & os, PageSides p)
2143 {
2144         switch (p) {
2145         case OneSide:
2146                 os << '1';
2147                 break;
2148         case TwoSides:
2149                 os << '2';
2150                 break;
2151         }
2152         return os;
2153 }
2154
2155
2156 } // namespace lyx