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