3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
17 #include "TextClass.h"
19 #include "CiteEnginesList.h"
22 #include "FloatList.h"
24 #include "LayoutFile.h"
26 #include "ModuleList.h"
28 #include "frontends/alert.h"
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"
48 using namespace lyx::support;
52 // Keep the changes documented in the Customization manual.
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.
62 int const LAYOUT_FORMAT = 91; // spitz: InputGlobal method
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;
72 bool layout2layout(FileName const & filename, FileName const & tempfile,
73 int const format = LAYOUT_FORMAT)
75 FileName const script = libFileSearch("scripts", "layout2layout.py");
77 LYXERR0("Could not find layout conversion "
78 "script layout2layout.py.");
82 ostringstream command;
83 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
85 << ' ' << quoteName(filename.toFilesystemEncoding())
86 << ' ' << quoteName(tempfile.toFilesystemEncoding());
87 string const command_str = command.str();
89 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
91 cmd_ret const ret = runCommand(command_str);
93 if (format == LAYOUT_FORMAT)
94 LYXERR0("Conversion of layout with layout2layout.py has failed.");
101 string translateReadType(TextClass::ReadType rt)
104 case TextClass::BASECLASS:
106 case TextClass::MERGE:
108 case TextClass::MODULE:
109 return "module file";
110 case TextClass::CITE_ENGINE:
111 return "cite engine";
112 case TextClass::VALIDATION:
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"));
127 /////////////////////////////////////////////////////////////////////////
131 /////////////////////////////////////////////////////////////////////////
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) {
148 bool TextClass::readStyle(Lexer & lexrc, Layout & lay, ReadType rt) const
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()) << '\'');
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
201 TC_ADDTOHTMLPREAMBLE,
219 TC_DOCBOOKFORCEABSTRACT
225 LexerKeyword textClassTags[] = {
226 { "addtociteengine", TC_ADDTOCITEENGINE },
227 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
228 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
229 { "addtopreamble", TC_ADDTOPREAMBLE },
230 { "bibintoc", TC_BIBINTOC },
231 { "citeengine", TC_CITEENGINE },
232 { "citeenginetype", TC_CITEENGINETYPE },
233 { "citeformat", TC_CITEFORMAT },
234 { "citeframework", TC_CITEFRAMEWORK },
235 { "classoptions", TC_CLASSOPTIONS },
236 { "columns", TC_COLUMNS },
237 { "counter", TC_COUNTER },
238 { "defaultbiblio", TC_DEFAULTBIBLIO },
239 { "defaultfont", TC_DEFAULTFONT },
240 { "defaultmodule", TC_DEFAULTMODULE },
241 { "defaultstyle", TC_DEFAULTSTYLE },
242 { "docbookforceabstract", TC_DOCBOOKFORCEABSTRACT },
243 { "docbookroot", TC_DOCBOOKROOT },
244 { "excludesmodule", TC_EXCLUDESMODULE },
245 { "float", TC_FLOAT },
246 { "format", TC_FORMAT },
247 { "fullauthorlist", TC_FULLAUTHORLIST },
248 { "htmlpreamble", TC_HTMLPREAMBLE },
249 { "htmlstyles", TC_HTMLSTYLES },
250 { "htmltocsection", TC_HTMLTOCSECTION },
251 { "ifcounter", TC_IFCOUNTER },
252 { "input", TC_INPUT },
253 { "inputglobal", TC_INPUT_GLOBAL },
254 { "insetlayout", TC_INSETLAYOUT },
255 { "leftmargin", TC_LEFTMARGIN },
256 { "maxcitenames", TC_MAXCITENAMES },
257 { "modifystyle", TC_MODIFYSTYLE },
258 { "nocounter", TC_NOCOUNTER },
259 { "nofloat", TC_NOFLOAT },
260 { "noinsetlayout", TC_NOINSETLAYOUT },
261 { "nostyle", TC_NOSTYLE },
262 { "outlinername", TC_OUTLINERNAME },
263 { "outputformat", TC_OUTPUTFORMAT },
264 { "outputtype", TC_OUTPUTTYPE },
265 { "packageoptions", TC_PKGOPTS },
266 { "pagesize", TC_PAGESIZE },
267 { "pagestyle", TC_PAGESTYLE },
268 { "preamble", TC_PREAMBLE },
269 { "provides", TC_PROVIDES },
270 { "providesmodule", TC_PROVIDESMODULE },
271 { "providestyle", TC_PROVIDESTYLE },
272 { "requires", TC_REQUIRES },
273 { "rightmargin", TC_RIGHTMARGIN },
274 { "secnumdepth", TC_SECNUMDEPTH },
275 { "sides", TC_SIDES },
276 { "style", TC_STYLE },
277 { "tablestyle", TC_TABLESTYLE },
278 { "titlelatexname", TC_TITLELATEXNAME },
279 { "titlelatextype", TC_TITLELATEXTYPE },
280 { "tocdepth", TC_TOCDEPTH }
286 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
288 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
289 TempFile tmp("convertXXXXXX.layout");
290 FileName const tempfile = tmp.name();
291 bool success = layout2layout(filename, tempfile);
293 success = readWithoutConv(tempfile, rt) == OK;
298 std::string TextClass::convert(std::string const & str)
300 TempFile tmp1("localXXXXXX.layout");
301 FileName const fn = tmp1.name();
302 ofstream os(fn.toFilesystemEncoding().c_str());
305 TempFile tmp2("convert_localXXXXXX.layout");
306 FileName const tempfile = tmp2.name();
307 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
310 ifstream is(tempfile.toFilesystemEncoding().c_str());
322 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
324 if (!filename.isReadableFile()) {
325 lyxerr << "Cannot read layout file `" << filename << "'."
330 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
331 to_utf8(makeDisplayPath(filename.absFileName())));
333 // Define the plain layout used in table cells, ert, etc. Note that
334 // we do this before loading any layout file, so that classes can
335 // override features of this layout if they should choose to do so.
336 if (rt == BASECLASS && !hasLayout(plain_layout_))
337 layoutlist_.push_back(createBasicLayout(plain_layout_));
339 Lexer lexrc(textClassTags);
340 lexrc.setFile(filename);
341 ReturnValues retval = read(lexrc, rt);
343 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
344 to_utf8(makeDisplayPath(filename.absFileName())));
350 bool TextClass::read(FileName const & filename, ReadType rt)
352 ReturnValues const retval = readWithoutConv(filename, rt);
353 if (retval != FORMAT_MISMATCH)
356 bool const worx = convertLayoutFormat(filename, rt);
358 LYXERR0 ("Unable to convert " << filename <<
359 " to format " << LAYOUT_FORMAT);
364 TextClass::ReturnValues TextClass::validate(std::string const & str)
367 return tc.read(str, VALIDATION);
371 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
373 Lexer lexrc(textClassTags);
374 istringstream is(str);
376 ReturnValues retval = read(lexrc, rt);
378 if (retval != FORMAT_MISMATCH)
381 // write the layout string to a temporary file
382 TempFile tmp("TextClass_read");
383 FileName const tempfile = tmp.name();
384 ofstream os(tempfile.toFilesystemEncoding().c_str());
386 LYXERR0("Unable to create temporary file");
392 // now try to convert it to LAYOUT_FORMAT
393 if (!convertLayoutFormat(tempfile, rt)) {
394 LYXERR0("Unable to convert internal layout information to format "
403 // Reads a textclass structure from file.
404 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
409 // The first usable line should be
410 // Format LAYOUT_FORMAT
411 if (lexrc.lex() != TC_FORMAT || !lexrc.next()
412 || lexrc.getInteger() != LAYOUT_FORMAT)
413 return FORMAT_MISMATCH;
417 while (lexrc.isOK() && !error) {
418 int le = lexrc.lex();
421 case Lexer::LEX_FEOF:
424 case Lexer::LEX_UNDEF:
425 lexrc.printError("Unknown TextClass tag `$$Token'");
433 // used below to track whether we are in an IfStyle or IfCounter tag.
434 bool modifystyle = false;
435 bool providestyle = false;
436 bool ifcounter = false;
437 bool only_global = false;
439 switch (static_cast<TextClassTags>(le)) {
443 lexrc.printError("Duplicate Format directive");
446 case TC_OUTPUTFORMAT:
448 outputFormat_ = lexrc.getString();
449 has_output_format_ = true;
454 readOutputType(lexrc);
455 switch(outputType_) {
457 outputFormat_ = "latex";
460 outputFormat_ = "literate";
465 case TC_INPUT_GLOBAL:
468 case TC_INPUT: // Include file
471 string const inc = lexrc.getString();
472 if (!path().empty() && (prefixIs(inc, "./") ||
473 prefixIs(inc, "../")))
474 tmp = fileSearch(path(), inc, "layout");
476 // InputGlobal only searches in the system and
477 // build directories. This allows to reuse and
478 // modify files in the user directory.
479 tmp = libFileSearch("layouts", inc,
480 "layout", must_exist, only_global);
484 lexrc.printError("Could not find input file: " + inc);
486 } else if (!read(tmp, MERGE)) {
487 lexrc.printError("Error reading input file: " + tmp.absFileName());
493 case TC_DEFAULTSTYLE:
495 docstring const name = from_utf8(subst(lexrc.getString(),
497 defaultlayout_ = name;
504 case TC_PROVIDESTYLE:
505 // if modifystyle is true, then we got here by falling through
506 // so we are not in an ProvideStyle block
512 lexrc.printError("No name given for style: `$$Token'.");
516 docstring const name = from_utf8(subst(lexrc.getString(),
519 string s = "Could not read name for style: `$$Token' "
520 + lexrc.getString() + " is probably not valid UTF-8!";
523 // Since we couldn't read the name, we just scan the rest
524 // of the style and discard it.
525 error = !readStyle(lexrc, lay, rt);
529 bool const have_layout = hasLayout(name);
531 // If the layout already exists, then we want to add it to
532 // the existing layout, as long as we are not in an ProvideStyle
534 if (have_layout && !providestyle) {
535 Layout & lay = operator[](name);
536 error = !readStyle(lexrc, lay, rt);
538 // If the layout does not exist, then we want to create a new
539 // one, but not if we are in a ModifyStyle block.
540 else if (!have_layout && !modifystyle) {
542 layout.setName(name);
543 error = !readStyle(lexrc, layout, rt);
545 layoutlist_.push_back(layout);
547 if (defaultlayout_.empty()) {
548 // We do not have a default layout yet, so we choose
549 // the first layout we encounter.
550 defaultlayout_ = name;
553 // There are two ways to get here:
554 // (i) The layout exists but we are in an ProvideStyle block
555 // (ii) The layout doesn't exist, but we are in an ModifyStyle
557 // Either way, we just scan the rest and discard it
560 // signal to coverity that we do not care about the result
561 (void)readStyle(lexrc, lay, rt);
568 docstring const style = from_utf8(subst(lexrc.getString(),
570 if (!deleteLayout(style))
571 lyxerr << "Cannot delete style `"
572 << to_utf8(style) << '\'' << endl;
576 case TC_NOINSETLAYOUT:
578 docstring const style = from_utf8(subst(lexrc.getString(),
580 if (!deleteInsetLayout(style))
581 LYXERR0("Style `" << style << "' cannot be removed\n"
582 "because it was not found!");
588 columns_ = lexrc.getInteger();
593 switch (lexrc.getInteger()) {
594 case 1: sides_ = OneSide; break;
595 case 2: sides_ = TwoSides; break;
597 lyxerr << "Impossible number of page"
598 " sides, setting to one."
608 pagesize_ = rtrim(lexrc.getString());
613 pagestyle_ = rtrim(lexrc.getString());
617 defaultfont_ = lyxRead(lexrc);
618 if (!defaultfont_.resolved()) {
619 lexrc.printError("Warning: defaultfont should "
620 "be fully instantiated!");
621 defaultfont_.realize(sane_font);
627 secnumdepth_ = lexrc.getInteger();
632 tocdepth_ = lexrc.getInteger();
635 // First step to support options
636 case TC_CLASSOPTIONS:
637 readClassOptions(lexrc);
641 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
644 case TC_HTMLPREAMBLE:
645 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
649 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
652 case TC_HTMLTOCSECTION:
653 html_toc_section_ = from_utf8(trim(lexrc.getString()));
656 case TC_ADDTOPREAMBLE:
657 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
660 case TC_ADDTOHTMLPREAMBLE:
661 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
664 case TC_ADDTOHTMLSTYLES:
665 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
670 string const feature = lexrc.getString();
672 if (lexrc.getInteger())
673 provides_.insert(feature);
675 provides_.erase(feature);
681 vector<string> const req
682 = getVectorFromString(lexrc.getString());
683 required_.insert(req.begin(), req.end());
689 string const pkg = lexrc.getString();
691 string const options = lexrc.getString();
692 package_options_[pkg] = options;
696 case TC_DEFAULTMODULE: {
698 string const module = lexrc.getString();
699 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
700 default_modules_.push_back(module);
704 case TC_PROVIDESMODULE: {
706 string const module = lexrc.getString();
707 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
708 provided_modules_.push_back(module);
712 case TC_EXCLUDESMODULE: {
714 string const module = lexrc.getString();
715 // modules already have their own way to exclude other modules
717 LYXERR0("ExcludesModule tag cannot be used in a module!");
720 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
721 excluded_modules_.push_back(module);
725 case TC_LEFTMARGIN: // left margin type
727 leftmargin_ = lexrc.getDocString();
730 case TC_RIGHTMARGIN: // right margin type
732 rightmargin_ = lexrc.getDocString();
735 case TC_INSETLAYOUT: {
737 lexrc.printError("No name given for InsetLayout: `$$Token'.");
741 docstring const name = subst(lexrc.getDocString(), '_', ' ');
742 bool const validating = (rt == VALIDATION);
744 string s = "Could not read name for InsetLayout: `$$Token' "
745 + lexrc.getString() + " is probably not valid UTF-8!";
748 // Since we couldn't read the name, we just scan the rest
749 // of the style and discard it.
750 il.read(lexrc, *this);
751 // Let's try to continue rather than abort, unless we're validating
752 // in which case we want to report the error
755 } else if (hasInsetLayout(name)) {
756 InsetLayout & il = insetlayoutlist_[name];
757 error = !il.read(lexrc, *this, validating);
761 error = !il.read(lexrc, *this, validating);
763 insetlayoutlist_[name] = il;
769 error = !readFloat(lexrc);
773 error = !readCiteEngine(lexrc, rt);
776 case TC_ADDTOCITEENGINE:
777 error = !readCiteEngine(lexrc, rt, true);
780 case TC_CITEENGINETYPE:
782 opt_enginetype_ = rtrim(lexrc.getString());
786 error = !readCiteFormat(lexrc, rt);
789 case TC_CITEFRAMEWORK:
791 citeframework_ = rtrim(lexrc.getString());
794 case TC_MAXCITENAMES:
796 maxcitenames_ = size_t(lexrc.getInteger());
799 case TC_DEFAULTBIBLIO:
801 vector<string> const dbs =
802 getVectorFromString(rtrim(lexrc.getString()), "|");
803 for (auto const & dbase : dbs) {
804 if (!contains(dbase, ':')) {
805 vector<string> const enginetypes =
806 getVectorFromString(opt_enginetype_, "|");
807 for (string const & s: enginetypes)
808 cite_default_biblio_style_[s] = dbase;
811 string const db = split(dbase, eng, ':');
812 cite_default_biblio_style_[eng] = db;
820 bibintoc_ = lexrc.getBool();
823 case TC_FULLAUTHORLIST:
825 cite_full_author_list_ &= lexrc.getBool();
830 docstring const cnt = lexrc.getDocString();
831 if (!counters_.remove(cnt))
832 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
841 docstring const name = lexrc.getDocString();
843 string s = "Could not read name for counter: `$$Token' "
844 + lexrc.getString() + " is probably not valid UTF-8!";
845 lexrc.printError(s.c_str());
847 // Since we couldn't read the name, we just scan the rest
851 error = !counters_.read(lexrc, name, !ifcounter);
854 lexrc.printError("No name given for style: `$$Token'.");
859 case TC_TITLELATEXTYPE:
860 readTitleType(lexrc);
863 case TC_TITLELATEXNAME:
865 titlename_ = lexrc.getString();
870 string const nofloat = lexrc.getString();
871 floatlist_.erase(nofloat);
875 case TC_OUTLINERNAME:
876 error = !readOutlinerName(lexrc);
881 tablestyle_ = rtrim(lexrc.getString());
886 docbookroot_ = lexrc.getString();
889 case TC_DOCBOOKFORCEABSTRACT:
891 docbookforceabstract_ = lexrc.getBool();
896 // at present, we abort if we encounter an error,
897 // so there is no point continuing.
904 if (defaultlayout_.empty()) {
905 LYXERR0("Error: Textclass '" << name_
906 << "' is missing a defaultstyle.");
910 // Try to erase "stdinsets" from the provides_ set.
912 // Provides stdinsets 1
913 // declaration simply tells us that the standard insets have been
914 // defined. (It's found in stdinsets.inc but could also be used in
915 // user-defined files.) There isn't really any such package. So we
916 // might as well go ahead and erase it.
917 // If we do not succeed, then it was not there, which means that
918 // the textclass did not provide the definitions of the standard
919 // insets. So we need to try to load them.
920 size_type const erased = provides_.erase("stdinsets");
922 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
925 frontend::Alert::warning(_("Missing File"),
926 _("Could not find stdinsets.inc! This may lead to data loss!"));
928 } else if (!read(tmp, MERGE)) {
929 frontend::Alert::warning(_("Corrupt File"),
930 _("Could not read stdinsets.inc! This may lead to data loss!"));
935 min_toclevel_ = Layout::NOT_IN_TOC;
936 max_toclevel_ = Layout::NOT_IN_TOC;
937 for (auto const & lay : *this) {
938 int const toclevel = lay.toclevel;
939 if (toclevel != Layout::NOT_IN_TOC) {
940 if (min_toclevel_ == Layout::NOT_IN_TOC)
941 min_toclevel_ = toclevel;
943 min_toclevel_ = min(min_toclevel_, toclevel);
944 max_toclevel_ = max(max_toclevel_, toclevel);
947 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
948 << ", maximum is " << max_toclevel_);
950 return (error ? ERROR : OK);
954 void TextClass::readTitleType(Lexer & lexrc)
956 LexerKeyword titleTypeTags[] = {
957 { "commandafter", TITLE_COMMAND_AFTER },
958 { "environment", TITLE_ENVIRONMENT }
961 PushPopHelper pph(lexrc, titleTypeTags);
963 int le = lexrc.lex();
965 case Lexer::LEX_UNDEF:
966 lexrc.printError("Unknown output type `$$Token'");
968 case TITLE_COMMAND_AFTER:
969 case TITLE_ENVIRONMENT:
970 titletype_ = static_cast<TitleLatexType>(le);
973 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
979 void TextClass::readOutputType(Lexer & lexrc)
981 LexerKeyword outputTypeTags[] = {
983 { "literate", LITERATE }
986 PushPopHelper pph(lexrc, outputTypeTags);
988 int le = lexrc.lex();
990 case Lexer::LEX_UNDEF:
991 lexrc.printError("Unknown output type `$$Token'");
995 outputType_ = static_cast<OutputType>(le);
998 LYXERR0("Unhandled value " << le);
1004 void TextClass::readClassOptions(Lexer & lexrc)
1016 LexerKeyword classOptionsTags[] = {
1018 {"fontsize", CO_FONTSIZE },
1019 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1020 {"other", CO_OTHER },
1021 {"pagesize", CO_PAGESIZE },
1022 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1023 {"pagestyle", CO_PAGESTYLE }
1026 lexrc.pushTable(classOptionsTags);
1027 bool getout = false;
1028 while (!getout && lexrc.isOK()) {
1029 int le = lexrc.lex();
1031 case Lexer::LEX_UNDEF:
1032 lexrc.printError("Unknown ClassOption tag `$$Token'");
1040 opt_fontsize_ = rtrim(lexrc.getString());
1042 case CO_FONTSIZE_FORMAT:
1044 fontsize_format_ = rtrim(lexrc.getString());
1048 opt_pagesize_ = rtrim(lexrc.getString());
1050 case CO_PAGESIZE_FORMAT:
1052 pagesize_format_ = rtrim(lexrc.getString());
1056 opt_pagestyle_ = rtrim(lexrc.getString());
1060 if (options_.empty())
1061 options_ = lexrc.getString();
1063 options_ += ',' + lexrc.getString();
1074 vector<CitationStyle> const & TextClass::getCiteStyles(
1075 CiteEngineType const & type) const
1077 static vector<CitationStyle> empty;
1078 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1079 if (it == cite_styles_.end())
1085 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1087 int const type = readCiteEngineType(lexrc);
1088 bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1089 bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1090 bool defce = (type & ENGINE_TYPE_DEFAULT);
1092 if (rt == CITE_ENGINE) {
1093 // The cite engines are not supposed to overwrite
1094 // CiteStyle defined by the class or a module.
1096 authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1098 numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1100 defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1103 if (rt != CITE_ENGINE && !add) {
1104 // Reset if we defined CiteStyle
1105 // from the class or a module
1107 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1109 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1111 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1115 bool getout = false;
1116 while (!getout && lexrc.isOK()) {
1118 def = lexrc.getString();
1119 def = subst(def, " ", "");
1120 def = subst(def, "\t", "");
1121 if (compare_ascii_no_case(def, "end") == 0) {
1126 char ichar = def[0];
1129 if (isUpperCase(ichar)) {
1130 cs.forceUpperCase = true;
1131 def[0] = lowercase(ichar);
1134 /** For portability reasons (between different
1135 * cite engines such as natbib and biblatex),
1136 * we distinguish between:
1137 * 1. The LyX name as output in the LyX file
1138 * 2. Possible aliases that might fall back to
1139 * the given LyX name in the current engine
1140 * 3. The actual LaTeX command that is output
1141 * (2) and (3) are optional.
1142 * Also, the GUI string for the starred version can
1145 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1154 ScanMode mode = LyXName;
1155 ScanMode oldmode = LyXName;
1160 size_t const n = def.size();
1161 for (size_t i = 0; i != n; ++i) {
1165 else if (ichar == '=')
1167 else if (ichar == '<') {
1170 } else if (ichar == '>')
1172 else if (mode == LaTeXCmd)
1174 else if (mode == StarDesc)
1176 else if (ichar == '$')
1177 cs.hasQualifiedList = true;
1178 else if (ichar == '*')
1179 cs.hasStarredVersion = true;
1180 else if (ichar == '[' && cs.textAfter)
1181 cs.textBefore = true;
1182 else if (ichar == '[')
1183 cs.textAfter = true;
1184 else if (ichar != ']') {
1192 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1193 if (!alias.empty()) {
1194 vector<string> const aliases = getVectorFromString(alias);
1195 for (string const & s: aliases)
1196 cite_command_aliases_[s] = lyx_cmd;
1198 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1199 int size = int(stardesc.size());
1201 cs.stardesc = stardescs[0];
1203 cs.startooltip = stardescs[1];
1206 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1208 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1210 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1213 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1215 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1217 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1220 // If we do AddToCiteEngine, do not apply yet,
1221 // except if we have already a style to add something to
1222 bool apply_ay = !add;
1223 bool apply_num = !add;
1224 bool apply_def = !add;
1226 if (type & ENGINE_TYPE_AUTHORYEAR)
1227 apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1228 if (type & ENGINE_TYPE_NUMERICAL)
1229 apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1230 if (type & ENGINE_TYPE_DEFAULT)
1231 apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1234 // Add the styles from AddToCiteEngine to the class' styles
1235 // (but only if they are not yet defined)
1236 for (auto const & cis : class_cite_styles_) {
1237 // Only consider the current CiteEngineType
1238 if (!(type & cis.first))
1240 for (auto const & ciss : cis.second) {
1241 bool defined = false;
1242 // Check if the style "name" is already def'ed
1243 for (auto const & av : getCiteStyles(cis.first))
1244 if (av.name == ciss.name)
1247 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1248 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1249 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1250 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1251 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1252 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1256 if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1257 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1258 if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1259 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1260 if (type & ENGINE_TYPE_DEFAULT && apply_def)
1261 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1266 int TextClass::readCiteEngineType(Lexer & lexrc) const
1268 static_assert(ENGINE_TYPE_DEFAULT ==
1269 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1270 "Incorrect default engine type");
1271 if (!lexrc.next()) {
1272 lexrc.printError("No cite engine type given for token: `$$Token'.");
1273 return ENGINE_TYPE_DEFAULT;
1275 string const type = rtrim(lexrc.getString());
1276 if (compare_ascii_no_case(type, "authoryear") == 0)
1277 return ENGINE_TYPE_AUTHORYEAR;
1278 else if (compare_ascii_no_case(type, "numerical") == 0)
1279 return ENGINE_TYPE_NUMERICAL;
1280 else if (compare_ascii_no_case(type, "default") != 0) {
1281 string const s = "Unknown cite engine type `" + type
1282 + "' given for token: `$$Token',";
1283 lexrc.printError(s);
1285 return ENGINE_TYPE_DEFAULT;
1289 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1291 int const type = readCiteEngineType(lexrc);
1294 // Cite engine definitions do not overwrite existing
1295 // definitions from the class or a module
1296 bool const overwrite = rt != CITE_ENGINE;
1297 while (lexrc.isOK()) {
1299 etype = lexrc.getString();
1300 if (compare_ascii_no_case(etype, "end") == 0)
1305 definition = lexrc.getString();
1306 char initchar = etype[0];
1307 if (initchar == '#')
1309 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1310 bool defined = false;
1311 bool aydefined = false;
1312 bool numdefined = false;
1313 // Check if the macro is already def'ed
1314 for (auto const & cm : cite_macros_) {
1315 if (!(type & cm.first))
1317 if (cm.second.find(etype) != cm.second.end()) {
1318 if (type == cm.first)
1319 // defined as default or specific type
1321 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1322 // defined for author-year
1324 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1325 // defined for numerical
1329 if (!defined || overwrite) {
1330 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1331 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1332 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1333 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1334 if (type == ENGINE_TYPE_DEFAULT)
1335 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1338 bool defined = false;
1339 bool aydefined = false;
1340 bool numdefined = false;
1341 // Check if the format is already def'ed
1342 for (auto const & cm : cite_formats_) {
1343 if (!(type & cm.first))
1345 if (cm.second.find(etype) != cm.second.end()) {
1346 if (type == cm.first)
1347 // defined as default or specific type
1349 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1350 // defined for author-year
1352 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1353 // defined for numerical
1357 if (!defined || overwrite){
1358 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1359 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1360 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1361 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1362 if (type == ENGINE_TYPE_DEFAULT)
1363 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1371 bool TextClass::readFloat(Lexer & lexrc)
1391 FT_ALLOWED_PLACEMENT,
1398 LexerKeyword floatTags[] = {
1399 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1400 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1401 { "allowswide", FT_ALLOWS_WIDE },
1402 { "docbookattr", FT_DOCBOOKATTR },
1403 { "docbooktag", FT_DOCBOOKTAG },
1404 { "docbooktagtype", FT_DOCBOOKTAGTYPE },
1406 { "extension", FT_EXT },
1407 { "guiname", FT_NAME },
1408 { "htmlattr", FT_HTMLATTR },
1409 { "htmlstyle", FT_HTMLSTYLE },
1410 { "htmltag", FT_HTMLTAG },
1411 { "ispredefined", FT_PREDEFINED },
1412 { "listcommand", FT_LISTCOMMAND },
1413 { "listname", FT_LISTNAME },
1414 { "numberwithin", FT_WITHIN },
1415 { "placement", FT_PLACEMENT },
1416 { "refprefix", FT_REFPREFIX },
1417 { "requires", FT_REQUIRES },
1418 { "style", FT_STYLE },
1419 { "type", FT_TYPE },
1420 { "usesfloatpkg", FT_USESFLOAT }
1423 lexrc.pushTable(floatTags);
1427 docstring htmlstyle;
1431 string docbooktagtype;
1436 string allowed_placement = "!htbpH";
1442 bool usesfloat = true;
1443 bool ispredefined = false;
1444 bool allowswide = true;
1445 bool allowssideways = true;
1447 bool getout = false;
1448 while (!getout && lexrc.isOK()) {
1449 int le = lexrc.lex();
1451 case Lexer::LEX_UNDEF:
1452 lexrc.printError("Unknown float tag `$$Token'");
1460 type = lexrc.getString();
1461 if (floatlist_.typeExist(type)) {
1462 Floating const & fl = floatlist_.getType(type);
1463 placement = fl.placement();
1465 within = fl.within();
1468 listname = fl.listName();
1469 usesfloat = fl.usesFloatPkg();
1470 ispredefined = fl.isPredefined();
1471 listcommand = fl.listCommand();
1472 refprefix = fl.refPrefix();
1477 name = lexrc.getString();
1481 placement = lexrc.getString();
1483 case FT_ALLOWED_PLACEMENT:
1485 allowed_placement = lexrc.getString();
1489 ext = lexrc.getString();
1493 within = lexrc.getString();
1494 if (within == "none")
1499 style = lexrc.getString();
1501 case FT_LISTCOMMAND:
1503 listcommand = lexrc.getString();
1507 refprefix = lexrc.getString();
1511 listname = lexrc.getString();
1515 usesfloat = lexrc.getBool();
1519 required = lexrc.getString();
1523 ispredefined = lexrc.getBool();
1525 case FT_ALLOWS_SIDEWAYS:
1527 allowssideways = lexrc.getBool();
1529 case FT_ALLOWS_WIDE:
1531 allowswide = lexrc.getBool();
1535 htmlattr = lexrc.getString();
1539 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1543 htmltag = lexrc.getString();
1545 case FT_DOCBOOKATTR:
1547 docbookattr = lexrc.getString();
1551 docbooktag = lexrc.getString();
1553 case FT_DOCBOOKTAGTYPE:
1555 docbooktagtype = lexrc.getString();
1565 // Here we have a full float if getout == true
1567 if (!usesfloat && listcommand.empty()) {
1568 // if this float uses the same auxfile as an existing one,
1569 // there is no need for it to provide a list command.
1570 bool found_ext = false;
1571 for (auto const & f : floatlist_) {
1572 if (f.second.ext() == ext) {
1578 LYXERR0("The layout does not provide a list command " <<
1579 "for the float `" << type << "'. LyX will " <<
1580 "not be able to produce a float list.");
1582 Floating fl(type, placement, ext, within, style, name,
1583 listname, listcommand, refprefix, allowed_placement,
1584 htmltag, htmlattr, htmlstyle, docbookattr,
1585 docbooktagtype, required, usesfloat, ispredefined,
1586 allowswide, allowssideways);
1587 floatlist_.newFloat(fl);
1588 // each float has its own counter
1589 counters_.newCounter(from_ascii(type), from_ascii(within),
1590 docstring(), docstring(),
1591 bformat(_("%1$s (Float)"), _(name)));
1592 // also define sub-float counters
1593 docstring const subtype = "sub-" + from_ascii(type);
1594 counters_.newCounter(subtype, from_ascii(type),
1595 "\\alph{" + subtype + "}", docstring(),
1596 bformat(_("Sub-%1$s (Float)"), _(name)));
1602 bool TextClass::readOutlinerName(Lexer & lexrc)
1607 type = lexrc.getString();
1609 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1613 name = lexrc.getDocString();
1615 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1618 outliner_names_[type] = name;
1623 string const & TextClass::prerequisites(string const & sep) const
1625 if (contains(prerequisites_, ',')) {
1626 vector<string> const pres = getVectorFromString(prerequisites_);
1627 prerequisites_ = getStringFromVector(pres, sep);
1629 return prerequisites_;
1633 bool TextClass::hasLayout(docstring const & n) const
1635 docstring const name = n.empty() ? defaultLayoutName() : n;
1636 return getLayout(name) != nullptr;
1640 bool TextClass::hasInsetLayout(docstring const & n) const
1644 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1645 return it != insetlayoutlist_.end();
1649 Layout const & TextClass::operator[](docstring const & name) const
1651 LATTEST(!name.empty());
1653 Layout const * c = getLayout(name);
1655 LYXERR0("We failed to find the layout '" << name
1656 << "' in the layout list. You MUST investigate!");
1657 for (auto const & lay : *this)
1658 lyxerr << " " << to_utf8(lay.name()) << endl;
1660 // We require the name to exist
1661 static const Layout dummy;
1662 LASSERT(false, return dummy);
1669 Layout & TextClass::operator[](docstring const & name)
1671 LATTEST(!name.empty());
1672 // Safe to continue, given what we do below.
1674 Layout * c = getLayout(name);
1676 LYXERR0("We failed to find the layout '" << to_utf8(name)
1677 << "' in the layout list. You MUST investigate!");
1678 for (auto const & lay : *this)
1679 LYXERR0(" " << to_utf8(lay.name()));
1681 // we require the name to exist
1683 // we are here only in release mode
1684 layoutlist_.push_back(createBasicLayout(name, true));
1685 c = getLayout(name);
1692 bool TextClass::deleteLayout(docstring const & name)
1694 if (name == defaultLayoutName() || name == plainLayoutName())
1697 LayoutList::iterator it =
1698 remove_if(layoutlist_.begin(), layoutlist_.end(),
1699 [name](const Layout &c) { return c.name() == name; });
1701 LayoutList::iterator const end = layoutlist_.end();
1702 bool const ret = (it != end);
1703 layoutlist_.erase(it, end);
1708 bool TextClass::deleteInsetLayout(docstring const & name)
1710 return insetlayoutlist_.erase(name);
1714 // Load textclass info if not loaded yet
1715 bool TextClass::load(string const & path) const
1720 // Read style-file, provided path is searched before the system ones
1721 // If path is a file, it is loaded directly.
1722 FileName layout_file(path);
1723 if (!path.empty() && !layout_file.isReadableFile())
1724 layout_file = FileName(addName(path, name_ + ".layout"));
1725 if (layout_file.empty() || !layout_file.exists())
1726 layout_file = libFileSearch("layouts", name_, "layout");
1727 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1730 lyxerr << "Error reading `"
1731 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1732 << "'\n(Check `" << name_
1733 << "')\nCheck your installation and "
1734 "try Options/Reconfigure..."
1742 Layout const * TextClass::getLayout(docstring const & name) const
1744 LayoutList::const_iterator cit =
1745 find_if(begin(), end(),
1746 [name](const Layout &c) { return c.name() == name; });
1747 if (cit == layoutlist_.end())
1754 Layout * TextClass::getLayout(docstring const & name)
1756 LayoutList::iterator it =
1757 find_if(layoutlist_.begin(), layoutlist_.end(),
1758 [name](const Layout &c) { return c.name() == name; });
1759 if (it == layoutlist_.end())
1766 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1771 layoutlist_.push_back(createBasicLayout(n, true));
1776 string DocumentClass::forcedLayouts() const
1780 for (auto const & lay : *this) {
1781 if (lay.forcelocal > 0) {
1783 os << "Format " << LAYOUT_FORMAT << '\n';
1793 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1795 // FIXME The fix for the InsetLayout part of 4812 would be here:
1796 // Add the InsetLayout to the document class if it is not found.
1798 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1799 while (!n.empty()) {
1800 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1801 if (cit != cen && cit->first == n) {
1802 if (cit->second.obsoleted_by().empty())
1804 n = cit->second.obsoleted_by();
1805 return insetLayout(n);
1807 // If we have a generic prefix (e.g., "Note:"),
1808 // try if this one alone is found.
1809 size_t i = n.find(':');
1810 if (i == string::npos)
1814 // Layout "name" not found.
1815 return plainInsetLayout();
1819 InsetLayout const & DocumentClass::plainInsetLayout() {
1820 static const InsetLayout plain_insetlayout_;
1821 return plain_insetlayout_;
1825 docstring const & TextClass::defaultLayoutName() const
1827 return defaultlayout_;
1831 Layout const & TextClass::defaultLayout() const
1833 return operator[](defaultLayoutName());
1837 bool TextClass::isDefaultLayout(Layout const & layout) const
1839 return layout.name() == defaultLayoutName();
1843 bool TextClass::isPlainLayout(Layout const & layout) const
1845 return layout.name() == plainLayoutName();
1849 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1851 static Layout * defaultLayout = nullptr;
1853 if (defaultLayout) {
1854 defaultLayout->setUnknown(unknown);
1855 defaultLayout->setName(name);
1856 return *defaultLayout;
1859 static char const * s = "Margin Static\n"
1860 "LatexType Paragraph\n"
1863 "AlignPossible Left, Right, Center\n"
1864 "LabelType No_Label\n"
1866 istringstream ss(s);
1867 Lexer lex(textClassTags);
1869 defaultLayout = new Layout;
1870 defaultLayout->setUnknown(unknown);
1871 defaultLayout->setName(name);
1872 if (!readStyle(lex, *defaultLayout, BASECLASS)) {
1873 // The only way this happens is because the hardcoded layout above
1877 return *defaultLayout;
1881 DocumentClassPtr getDocumentClass(LayoutFile const & baseClass, LayoutModuleList const & modlist,
1882 string const & cengine, bool clone, bool internal)
1884 bool const show_warnings = !clone && !internal;
1885 DocumentClassPtr doc_class =
1886 DocumentClassPtr(new DocumentClass(baseClass));
1887 for (auto const & mod : modlist) {
1888 LyXModule * lm = theModuleList[mod];
1890 if (show_warnings) {
1891 docstring const msg =
1892 bformat(_("The module %1$s has been requested by\n"
1893 "this document but has not been found in the list of\n"
1894 "available modules. If you recently installed it, you\n"
1895 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1896 frontend::Alert::warning(_("Module not available"), msg);
1900 if (!lm->isAvailable() && show_warnings) {
1901 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1902 docstring const msg =
1903 bformat(_("The module %1$s requires a package that is not\n"
1904 "available in your LaTeX installation, or a converter that\n"
1905 "you have not installed. LaTeX output may not be possible.\n"
1906 "Missing prerequisites:\n"
1908 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1909 from_utf8(mod), prereqs);
1910 frontend::Alert::warning(_("Package not available"), msg, true);
1912 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1913 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1914 docstring const msg =
1915 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1916 frontend::Alert::warning(_("Read Error"), msg);
1920 if (cengine.empty())
1923 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1925 if (show_warnings) {
1926 docstring const msg =
1927 bformat(_("The cite engine %1$s has been requested by\n"
1928 "this document but has not been found in the list of\n"
1929 "available engines. If you recently installed it, you\n"
1930 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1931 frontend::Alert::warning(_("Cite Engine not available"), msg);
1933 } else if (!ce->isAvailable() && show_warnings) {
1934 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1935 docstring const msg =
1936 bformat(_("The cite engine %1$s requires a package that is not\n"
1937 "available in your LaTeX installation, or a converter that\n"
1938 "you have not installed. LaTeX output may not be possible.\n"
1939 "Missing prerequisites:\n"
1941 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1942 from_utf8(cengine), prereqs);
1943 frontend::Alert::warning(_("Package not available"), msg, true);
1945 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1946 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1947 docstring const msg =
1948 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1949 frontend::Alert::warning(_("Read Error"), msg);
1957 /////////////////////////////////////////////////////////////////////////
1961 /////////////////////////////////////////////////////////////////////////
1963 DocumentClass::DocumentClass(LayoutFile const & tc)
1968 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1970 for (auto const & l : layoutlist_)
1971 if (l.latexname() == lay)
1977 bool DocumentClass::provides(string const & p) const
1979 return provides_.find(p) != provides_.end();
1983 bool DocumentClass::hasTocLevels() const
1985 return min_toclevel_ != Layout::NOT_IN_TOC;
1989 Layout const & DocumentClass::getTOCLayout() const
1991 // we're going to look for the layout with the minimum toclevel
1992 int minlevel = 1000;
1993 Layout const * lay = nullptr;
1994 for (auto const & l : *this) {
1995 int const level = l.toclevel;
1996 // we don't want Part or unnumbered sections
1997 if (level == Layout::NOT_IN_TOC || level < 0
1998 || level >= minlevel || l.counter.empty())
2005 // hmm. that is very odd, so we'll do our best.
2006 return operator[](defaultLayoutName());
2010 Layout const & DocumentClass::htmlTOCLayout() const
2012 if (html_toc_section_.empty())
2013 html_toc_section_ = getTOCLayout().name();
2014 return operator[](html_toc_section_);
2018 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
2019 string const & entry, bool const punct, string const & fallback) const
2021 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
2022 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
2023 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
2025 default_format += ".";
2027 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2028 if (itype == cite_formats_.end())
2029 return default_format;
2030 map<string, string>::const_iterator it = itype->second.find(entry);
2031 if (it == itype->second.end() && !fallback.empty())
2032 it = itype->second.find(fallback);
2033 if (it == itype->second.end())
2034 return default_format;
2036 return it->second + ".";
2041 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2042 string const & macro) const
2044 static string empty;
2045 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2046 if (itype == cite_macros_.end())
2048 map<string, string>::const_iterator it = itype->second.find(macro);
2049 if (it == itype->second.end())
2055 vector<string> const DocumentClass::citeCommands(
2056 CiteEngineType const & type) const
2058 vector<CitationStyle> const styles = citeStyles(type);
2059 vector<string> cmds;
2060 cmds.reserve(styles.size());
2061 for (auto const & cs : styles)
2062 cmds.push_back(cs.name);
2068 vector<CitationStyle> const & DocumentClass::citeStyles(
2069 CiteEngineType const & type) const
2071 static vector<CitationStyle> empty;
2072 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2073 if (it == cite_styles_.end())
2079 /////////////////////////////////////////////////////////////////////////
2083 /////////////////////////////////////////////////////////////////////////
2085 ostream & operator<<(ostream & os, PageSides p)