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 "LayoutFile.h"
20 #include "CiteEnginesList.h"
24 #include "FloatList.h"
28 #include "ModuleList.h"
30 #include "frontends/alert.h"
32 #include "support/lassert.h"
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/FileName.h"
36 #include "support/filetools.h"
37 #include "support/gettext.h"
38 #include "support/lstrings.h"
39 #include "support/os.h"
40 #include "support/TempFile.h"
51 using namespace lyx::support;
55 // Keep the changes documented in the Customization manual.
57 // If you change this format, then you MUST also make sure that
58 // your changes do not invalidate the hardcoded layout file in
59 // LayoutFile.cpp. Additions will never do so, but syntax changes
60 // could. See LayoutFileList::addEmptyClass() and, especially, the
61 // definition of the layoutpost string.
62 // You should also run the development/tools/updatelayouts.py script,
63 // to update the format of all of our layout files.
65 int const LAYOUT_FORMAT = 81; // rikiheck: GuiName for counters
68 // Layout format for the current lyx file format. Controls which format is
69 // targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
70 int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
75 bool layout2layout(FileName const & filename, FileName const & tempfile,
76 int const format = LAYOUT_FORMAT)
78 FileName const script = libFileSearch("scripts", "layout2layout.py");
80 LYXERR0("Could not find layout conversion "
81 "script layout2layout.py.");
85 ostringstream command;
86 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
88 << ' ' << quoteName(filename.toFilesystemEncoding())
89 << ' ' << quoteName(tempfile.toFilesystemEncoding());
90 string const command_str = command.str();
92 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
94 cmd_ret const ret = runCommand(command_str);
96 if (format == LAYOUT_FORMAT)
97 LYXERR0("Conversion of layout with layout2layout.py has failed.");
104 string translateReadType(TextClass::ReadType rt)
107 case TextClass::BASECLASS:
109 case TextClass::MERGE:
111 case TextClass::MODULE:
112 return "module file";
113 case TextClass::CITE_ENGINE:
114 return "cite engine";
115 case TextClass::VALIDATION:
125 // This string should not be translated here,
126 // because it is a layout identifier.
127 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
130 /////////////////////////////////////////////////////////////////////////
134 /////////////////////////////////////////////////////////////////////////
136 TextClass::TextClass()
137 : loaded_(false), tex_class_avail_(false),
138 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
139 opt_pagesize_("default|a4|a5|b5|letter|legal|executive"),
140 opt_pagestyle_("empty|plain|headings|fancy"), fontsize_format_("$$spt"), pagesize_("default"),
141 pagesize_format_("$$spaper"), pagestyle_("default"), tablestyle_("default"),
142 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
143 outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font),
144 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
145 min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
146 cite_full_author_list_(true), bibintoc_(false)
151 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
153 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
154 if (!lay.read(lexrc, *this)) {
155 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
159 lay.resfont = lay.font;
160 lay.resfont.realize(defaultfont_);
161 lay.reslabelfont = lay.labelfont;
162 lay.reslabelfont.realize(defaultfont_);
163 return true; // no errors
203 TC_ADDTOHTMLPREAMBLE,
225 LexerKeyword textClassTags[] = {
226 { "addtociteengine", TC_ADDTOCITEENGINE },
227 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
228 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
229 { "addtopreamble", TC_ADDTOPREAMBLE },
230 { "bibintoc", TC_BIBINTOC },
231 { "citeengine", TC_CITEENGINE },
232 { "citeenginetype", TC_CITEENGINETYPE },
233 { "citeformat", TC_CITEFORMAT },
234 { "citeframework", TC_CITEFRAMEWORK },
235 { "classoptions", TC_CLASSOPTIONS },
236 { "columns", TC_COLUMNS },
237 { "counter", TC_COUNTER },
238 { "defaultbiblio", TC_DEFAULTBIBLIO },
239 { "defaultfont", TC_DEFAULTFONT },
240 { "defaultmodule", TC_DEFAULTMODULE },
241 { "defaultstyle", TC_DEFAULTSTYLE },
242 { "excludesmodule", TC_EXCLUDESMODULE },
243 { "float", TC_FLOAT },
244 { "format", TC_FORMAT },
245 { "fullauthorlist", TC_FULLAUTHORLIST },
246 { "htmlpreamble", TC_HTMLPREAMBLE },
247 { "htmlstyles", TC_HTMLSTYLES },
248 { "htmltocsection", TC_HTMLTOCSECTION },
249 { "ifcounter", TC_IFCOUNTER },
250 { "input", TC_INPUT },
251 { "insetlayout", TC_INSETLAYOUT },
252 { "leftmargin", TC_LEFTMARGIN },
253 { "maxcitenames", TC_MAXCITENAMES },
254 { "modifystyle", TC_MODIFYSTYLE },
255 { "nocounter", TC_NOCOUNTER },
256 { "nofloat", TC_NOFLOAT },
257 { "noinsetlayout", TC_NOINSETLAYOUT },
258 { "nostyle", TC_NOSTYLE },
259 { "outlinername", TC_OUTLINERNAME },
260 { "outputformat", TC_OUTPUTFORMAT },
261 { "outputtype", TC_OUTPUTTYPE },
262 { "packageoptions", TC_PKGOPTS },
263 { "pagesize", TC_PAGESIZE },
264 { "pagestyle", TC_PAGESTYLE },
265 { "preamble", TC_PREAMBLE },
266 { "provides", TC_PROVIDES },
267 { "providesmodule", TC_PROVIDESMODULE },
268 { "providestyle", TC_PROVIDESTYLE },
269 { "requires", TC_REQUIRES },
270 { "rightmargin", TC_RIGHTMARGIN },
271 { "secnumdepth", TC_SECNUMDEPTH },
272 { "sides", TC_SIDES },
273 { "style", TC_STYLE },
274 { "tablestyle", TC_TABLESTYLE },
275 { "titlelatexname", TC_TITLELATEXNAME },
276 { "titlelatextype", TC_TITLELATEXTYPE },
277 { "tocdepth", TC_TOCDEPTH }
283 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
285 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
286 TempFile tmp("convertXXXXXX.layout");
287 FileName const tempfile = tmp.name();
288 bool success = layout2layout(filename, tempfile);
290 success = readWithoutConv(tempfile, rt) == OK;
295 std::string TextClass::convert(std::string const & str)
297 TempFile tmp1("localXXXXXX.layout");
298 FileName const fn = tmp1.name();
299 ofstream os(fn.toFilesystemEncoding().c_str());
302 TempFile tmp2("convert_localXXXXXX.layout");
303 FileName const tempfile = tmp2.name();
304 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
307 ifstream is(tempfile.toFilesystemEncoding().c_str());
319 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
321 if (!filename.isReadableFile()) {
322 lyxerr << "Cannot read layout file `" << filename << "'."
327 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
328 to_utf8(makeDisplayPath(filename.absFileName())));
330 // Define the plain layout used in table cells, ert, etc. Note that
331 // we do this before loading any layout file, so that classes can
332 // override features of this layout if they should choose to do so.
333 if (rt == BASECLASS && !hasLayout(plain_layout_))
334 layoutlist_.push_back(createBasicLayout(plain_layout_));
336 Lexer lexrc(textClassTags);
337 lexrc.setFile(filename);
338 ReturnValues retval = read(lexrc, rt);
340 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
341 to_utf8(makeDisplayPath(filename.absFileName())));
347 bool TextClass::read(FileName const & filename, ReadType rt)
349 ReturnValues const retval = readWithoutConv(filename, rt);
350 if (retval != FORMAT_MISMATCH)
353 bool const worx = convertLayoutFormat(filename, rt);
355 LYXERR0 ("Unable to convert " << filename <<
356 " to format " << LAYOUT_FORMAT);
361 TextClass::ReturnValues TextClass::validate(std::string const & str)
364 return tc.read(str, VALIDATION);
368 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
370 Lexer lexrc(textClassTags);
371 istringstream is(str);
373 ReturnValues retval = read(lexrc, rt);
375 if (retval != FORMAT_MISMATCH)
378 // write the layout string to a temporary file
379 TempFile tmp("TextClass_read");
380 FileName const tempfile = tmp.name();
381 ofstream os(tempfile.toFilesystemEncoding().c_str());
383 LYXERR0("Unable to create temporary file");
389 // now try to convert it to LAYOUT_FORMAT
390 if (!convertLayoutFormat(tempfile, rt)) {
391 LYXERR0("Unable to convert internal layout information to format "
400 // Reads a textclass structure from file.
401 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
406 // The first usable line should be
407 // Format LAYOUT_FORMAT
408 if (lexrc.lex() != TC_FORMAT || !lexrc.next()
409 || lexrc.getInteger() != LAYOUT_FORMAT)
410 return FORMAT_MISMATCH;
414 while (lexrc.isOK() && !error) {
415 int le = lexrc.lex();
418 case Lexer::LEX_FEOF:
421 case Lexer::LEX_UNDEF:
422 lexrc.printError("Unknown TextClass tag `$$Token'");
430 // used below to track whether we are in an IfStyle or IfCounter tag.
431 bool modifystyle = false;
432 bool providestyle = false;
433 bool ifcounter = false;
435 switch (static_cast<TextClassTags>(le)) {
439 lexrc.printError("Duplicate Format directive");
442 case TC_OUTPUTFORMAT:
444 outputFormat_ = lexrc.getString();
445 has_output_format_ = true;
450 readOutputType(lexrc);
451 switch(outputType_) {
453 outputFormat_ = "latex";
456 outputFormat_ = "docbook";
459 outputFormat_ = "literate";
464 case TC_INPUT: // Include file
467 string const inc = lexrc.getString();
468 if (!path().empty() && (prefixIs(inc, "./") ||
469 prefixIs(inc, "../")))
470 tmp = fileSearch(path(), inc, "layout");
472 tmp = libFileSearch("layouts", inc,
476 lexrc.printError("Could not find input file: " + inc);
478 } else if (!read(tmp, MERGE)) {
479 lexrc.printError("Error reading input file: " + tmp.absFileName());
485 case TC_DEFAULTSTYLE:
487 docstring const name = from_utf8(subst(lexrc.getString(),
489 defaultlayout_ = name;
496 case TC_PROVIDESTYLE:
497 // if modifystyle is true, then we got here by falling through
498 // so we are not in an ProvideStyle block
504 lexrc.printError("No name given for style: `$$Token'.");
508 docstring const name = from_utf8(subst(lexrc.getString(),
511 string s = "Could not read name for style: `$$Token' "
512 + lexrc.getString() + " is probably not valid UTF-8!";
515 // Since we couldn't read the name, we just scan the rest
516 // of the style and discard it.
517 error = !readStyle(lexrc, lay);
521 bool const have_layout = hasLayout(name);
523 // If the layout already exists, then we want to add it to
524 // the existing layout, as long as we are not in an ProvideStyle
526 if (have_layout && !providestyle) {
527 Layout & lay = operator[](name);
528 error = !readStyle(lexrc, lay);
530 // If the layout does not exist, then we want to create a new
531 // one, but not if we are in a ModifyStyle block.
532 else if (!have_layout && !modifystyle) {
534 layout.setName(name);
535 error = !readStyle(lexrc, layout);
537 layoutlist_.push_back(layout);
539 if (defaultlayout_.empty()) {
540 // We do not have a default layout yet, so we choose
541 // the first layout we encounter.
542 defaultlayout_ = name;
545 // There are two ways to get here:
546 // (i) The layout exists but we are in an ProvideStyle block
547 // (ii) The layout doesn't exist, but we are in an ModifyStyle
549 // Either way, we just scan the rest and discard it
552 // signal to coverity that we do not care about the result
553 (void)readStyle(lexrc, lay);
560 docstring const style = from_utf8(subst(lexrc.getString(),
562 if (!deleteLayout(style))
563 lyxerr << "Cannot delete style `"
564 << to_utf8(style) << '\'' << endl;
568 case TC_NOINSETLAYOUT:
570 docstring const style = from_utf8(subst(lexrc.getString(),
572 if (!deleteInsetLayout(style))
573 LYXERR0("Style `" << style << "' cannot be removed\n"
574 "because it was not found!");
580 columns_ = lexrc.getInteger();
585 switch (lexrc.getInteger()) {
586 case 1: sides_ = OneSide; break;
587 case 2: sides_ = TwoSides; break;
589 lyxerr << "Impossible number of page"
590 " sides, setting to one."
600 pagesize_ = rtrim(lexrc.getString());
605 pagestyle_ = rtrim(lexrc.getString());
609 defaultfont_ = lyxRead(lexrc);
610 if (!defaultfont_.resolved()) {
611 lexrc.printError("Warning: defaultfont should "
612 "be fully instantiated!");
613 defaultfont_.realize(sane_font);
619 secnumdepth_ = lexrc.getInteger();
624 tocdepth_ = lexrc.getInteger();
627 // First step to support options
628 case TC_CLASSOPTIONS:
629 readClassOptions(lexrc);
633 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
636 case TC_HTMLPREAMBLE:
637 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
641 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
644 case TC_HTMLTOCSECTION:
645 html_toc_section_ = from_utf8(trim(lexrc.getString()));
648 case TC_ADDTOPREAMBLE:
649 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
652 case TC_ADDTOHTMLPREAMBLE:
653 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
656 case TC_ADDTOHTMLSTYLES:
657 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
662 string const feature = lexrc.getString();
664 if (lexrc.getInteger())
665 provides_.insert(feature);
667 provides_.erase(feature);
673 vector<string> const req
674 = getVectorFromString(lexrc.getString());
675 required_.insert(req.begin(), req.end());
681 string const pkg = lexrc.getString();
683 string const options = lexrc.getString();
684 package_options_[pkg] = options;
688 case TC_DEFAULTMODULE: {
690 string const module = lexrc.getString();
691 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
692 default_modules_.push_back(module);
696 case TC_PROVIDESMODULE: {
698 string const module = lexrc.getString();
699 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
700 provided_modules_.push_back(module);
704 case TC_EXCLUDESMODULE: {
706 string const module = lexrc.getString();
707 // modules already have their own way to exclude other modules
709 LYXERR0("ExcludesModule tag cannot be used in a module!");
712 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
713 excluded_modules_.push_back(module);
717 case TC_LEFTMARGIN: // left margin type
719 leftmargin_ = lexrc.getDocString();
722 case TC_RIGHTMARGIN: // right margin type
724 rightmargin_ = lexrc.getDocString();
727 case TC_INSETLAYOUT: {
729 lexrc.printError("No name given for InsetLayout: `$$Token'.");
733 docstring const name = subst(lexrc.getDocString(), '_', ' ');
734 bool const validating = (rt == VALIDATION);
736 string s = "Could not read name for InsetLayout: `$$Token' "
737 + lexrc.getString() + " is probably not valid UTF-8!";
740 // Since we couldn't read the name, we just scan the rest
741 // of the style and discard it.
742 il.read(lexrc, *this);
743 // Let's try to continue rather than abort, unless we're validating
744 // in which case we want to report the error
747 } else if (hasInsetLayout(name)) {
748 InsetLayout & il = insetlayoutlist_[name];
749 error = !il.read(lexrc, *this, validating);
753 error = !il.read(lexrc, *this, validating);
755 insetlayoutlist_[name] = il;
761 error = !readFloat(lexrc);
765 error = !readCiteEngine(lexrc, rt);
768 case TC_ADDTOCITEENGINE:
769 error = !readCiteEngine(lexrc, rt, true);
772 case TC_CITEENGINETYPE:
774 opt_enginetype_ = rtrim(lexrc.getString());
778 error = !readCiteFormat(lexrc, rt);
781 case TC_CITEFRAMEWORK:
783 citeframework_ = rtrim(lexrc.getString());
786 case TC_MAXCITENAMES:
788 maxcitenames_ = size_t(lexrc.getInteger());
791 case TC_DEFAULTBIBLIO:
793 vector<string> const dbs =
794 getVectorFromString(rtrim(lexrc.getString()), "|");
795 for (auto const & dbase : dbs) {
796 if (!contains(dbase, ':')) {
797 vector<string> const enginetypes =
798 getVectorFromString(opt_enginetype_, "|");
799 for (string const & s: enginetypes)
800 cite_default_biblio_style_[s] = dbase;
803 string const db = split(dbase, eng, ':');
804 cite_default_biblio_style_[eng] = db;
812 bibintoc_ = lexrc.getBool();
815 case TC_FULLAUTHORLIST:
817 cite_full_author_list_ &= lexrc.getBool();
822 docstring const cnt = lexrc.getDocString();
823 if (!counters_.remove(cnt))
824 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
833 docstring const name = lexrc.getDocString();
835 string s = "Could not read name for counter: `$$Token' "
836 + lexrc.getString() + " is probably not valid UTF-8!";
837 lexrc.printError(s.c_str());
839 // Since we couldn't read the name, we just scan the rest
843 error = !counters_.read(lexrc, name, !ifcounter);
846 lexrc.printError("No name given for style: `$$Token'.");
851 case TC_TITLELATEXTYPE:
852 readTitleType(lexrc);
855 case TC_TITLELATEXNAME:
857 titlename_ = lexrc.getString();
862 string const nofloat = lexrc.getString();
863 floatlist_.erase(nofloat);
867 case TC_OUTLINERNAME:
868 error = !readOutlinerName(lexrc);
873 tablestyle_ = rtrim(lexrc.getString());
878 // at present, we abort if we encounter an error,
879 // so there is no point continuing.
886 if (defaultlayout_.empty()) {
887 LYXERR0("Error: Textclass '" << name_
888 << "' is missing a defaultstyle.");
892 // Try to erase "stdinsets" from the provides_ set.
894 // Provides stdinsets 1
895 // declaration simply tells us that the standard insets have been
896 // defined. (It's found in stdinsets.inc but could also be used in
897 // user-defined files.) There isn't really any such package. So we
898 // might as well go ahead and erase it.
899 // If we do not succeed, then it was not there, which means that
900 // the textclass did not provide the definitions of the standard
901 // insets. So we need to try to load them.
902 size_type const erased = provides_.erase("stdinsets");
904 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
907 frontend::Alert::warning(_("Missing File"),
908 _("Could not find stdinsets.inc! This may lead to data loss!"));
910 } else if (!read(tmp, MERGE)) {
911 frontend::Alert::warning(_("Corrupt File"),
912 _("Could not read stdinsets.inc! This may lead to data loss!"));
917 min_toclevel_ = Layout::NOT_IN_TOC;
918 max_toclevel_ = Layout::NOT_IN_TOC;
919 for (auto const & lay : *this) {
920 int const toclevel = lay.toclevel;
921 if (toclevel != Layout::NOT_IN_TOC) {
922 if (min_toclevel_ == Layout::NOT_IN_TOC)
923 min_toclevel_ = toclevel;
925 min_toclevel_ = min(min_toclevel_, toclevel);
926 max_toclevel_ = max(max_toclevel_, toclevel);
929 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
930 << ", maximum is " << max_toclevel_);
932 return (error ? ERROR : OK);
936 void TextClass::readTitleType(Lexer & lexrc)
938 LexerKeyword titleTypeTags[] = {
939 { "commandafter", TITLE_COMMAND_AFTER },
940 { "environment", TITLE_ENVIRONMENT }
943 PushPopHelper pph(lexrc, titleTypeTags);
945 int le = lexrc.lex();
947 case Lexer::LEX_UNDEF:
948 lexrc.printError("Unknown output type `$$Token'");
950 case TITLE_COMMAND_AFTER:
951 case TITLE_ENVIRONMENT:
952 titletype_ = static_cast<TitleLatexType>(le);
955 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
961 void TextClass::readOutputType(Lexer & lexrc)
963 LexerKeyword outputTypeTags[] = {
964 { "docbook", DOCBOOK },
966 { "literate", LITERATE }
969 PushPopHelper pph(lexrc, outputTypeTags);
971 int le = lexrc.lex();
973 case Lexer::LEX_UNDEF:
974 lexrc.printError("Unknown output type `$$Token'");
979 outputType_ = static_cast<OutputType>(le);
982 LYXERR0("Unhandled value " << le);
988 void TextClass::readClassOptions(Lexer & lexrc)
1001 LexerKeyword classOptionsTags[] = {
1003 {"fontsize", CO_FONTSIZE },
1004 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1005 {"header", CO_HEADER },
1006 {"other", CO_OTHER },
1007 {"pagesize", CO_PAGESIZE },
1008 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1009 {"pagestyle", CO_PAGESTYLE }
1012 lexrc.pushTable(classOptionsTags);
1013 bool getout = false;
1014 while (!getout && lexrc.isOK()) {
1015 int le = lexrc.lex();
1017 case Lexer::LEX_UNDEF:
1018 lexrc.printError("Unknown ClassOption tag `$$Token'");
1026 opt_fontsize_ = rtrim(lexrc.getString());
1028 case CO_FONTSIZE_FORMAT:
1030 fontsize_format_ = rtrim(lexrc.getString());
1034 opt_pagesize_ = rtrim(lexrc.getString());
1036 case CO_PAGESIZE_FORMAT:
1038 pagesize_format_ = rtrim(lexrc.getString());
1042 opt_pagestyle_ = rtrim(lexrc.getString());
1046 if (options_.empty())
1047 options_ = lexrc.getString();
1049 options_ += ',' + lexrc.getString();
1053 class_header_ = subst(lexrc.getString(), """, "\"");
1064 vector<CitationStyle> const & TextClass::getCiteStyles(
1065 CiteEngineType const & type) const
1067 static vector<CitationStyle> empty;
1068 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1069 if (it == cite_styles_.end())
1075 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1077 int const type = readCiteEngineType(lexrc);
1078 bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1079 bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1080 bool defce = (type & ENGINE_TYPE_DEFAULT);
1082 if (rt == CITE_ENGINE) {
1083 // The cite engines are not supposed to overwrite
1084 // CiteStyle defined by the class or a module.
1086 authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1088 numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1090 defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1093 if (rt != CITE_ENGINE && !add) {
1094 // Reset if we defined CiteStyle
1095 // from the class or a module
1097 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1099 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1101 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1105 bool getout = false;
1106 while (!getout && lexrc.isOK()) {
1108 def = lexrc.getString();
1109 def = subst(def, " ", "");
1110 def = subst(def, "\t", "");
1111 if (compare_ascii_no_case(def, "end") == 0) {
1116 char ichar = def[0];
1119 if (isUpperCase(ichar)) {
1120 cs.forceUpperCase = true;
1121 def[0] = lowercase(ichar);
1124 /** For portability reasons (between different
1125 * cite engines such as natbib and biblatex),
1126 * we distinguish between:
1127 * 1. The LyX name as output in the LyX file
1128 * 2. Possible aliases that might fall back to
1129 * the given LyX name in the current engine
1130 * 3. The actual LaTeX command that is output
1131 * (2) and (3) are optional.
1132 * Also, the GUI string for the starred version can
1135 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1144 ScanMode mode = LyXName;
1145 ScanMode oldmode = LyXName;
1150 size_t const n = def.size();
1151 for (size_t i = 0; i != n; ++i) {
1155 else if (ichar == '=')
1157 else if (ichar == '<') {
1160 } else if (ichar == '>')
1162 else if (mode == LaTeXCmd)
1164 else if (mode == StarDesc)
1166 else if (ichar == '$')
1167 cs.hasQualifiedList = true;
1168 else if (ichar == '*')
1169 cs.hasStarredVersion = true;
1170 else if (ichar == '[' && cs.textAfter)
1171 cs.textBefore = true;
1172 else if (ichar == '[')
1173 cs.textAfter = true;
1174 else if (ichar != ']') {
1182 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1183 if (!alias.empty()) {
1184 vector<string> const aliases = getVectorFromString(alias);
1185 for (string const & s: aliases)
1186 cite_command_aliases_[s] = lyx_cmd;
1188 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1189 int size = int(stardesc.size());
1191 cs.stardesc = stardescs[0];
1193 cs.startooltip = stardescs[1];
1196 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1198 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1200 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1203 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1205 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1207 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1210 // If we do AddToCiteEngine, do not apply yet,
1211 // except if we have already a style to add something to
1212 bool apply_ay = !add;
1213 bool apply_num = !add;
1214 bool apply_def = !add;
1216 if (type & ENGINE_TYPE_AUTHORYEAR)
1217 apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1218 if (type & ENGINE_TYPE_NUMERICAL)
1219 apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1220 if (type & ENGINE_TYPE_DEFAULT)
1221 apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1224 // Add the styles from AddToCiteEngine to the class' styles
1225 // (but only if they are not yet defined)
1226 for (auto const & cis : class_cite_styles_) {
1227 // Only consider the current CiteEngineType
1228 if (!(type & cis.first))
1230 for (auto const & ciss : cis.second) {
1231 bool defined = false;
1232 // Check if the style "name" is already def'ed
1233 for (auto const & av : getCiteStyles(cis.first))
1234 if (av.name == ciss.name)
1237 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1238 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1239 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1240 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1241 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1242 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1246 if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1247 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1248 if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1249 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1250 if (type & ENGINE_TYPE_DEFAULT && apply_def)
1251 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1256 int TextClass::readCiteEngineType(Lexer & lexrc) const
1258 static_assert(ENGINE_TYPE_DEFAULT ==
1259 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1260 "Incorrect default engine type");
1261 if (!lexrc.next()) {
1262 lexrc.printError("No cite engine type given for token: `$$Token'.");
1263 return ENGINE_TYPE_DEFAULT;
1265 string const type = rtrim(lexrc.getString());
1266 if (compare_ascii_no_case(type, "authoryear") == 0)
1267 return ENGINE_TYPE_AUTHORYEAR;
1268 else if (compare_ascii_no_case(type, "numerical") == 0)
1269 return ENGINE_TYPE_NUMERICAL;
1270 else if (compare_ascii_no_case(type, "default") != 0) {
1271 string const s = "Unknown cite engine type `" + type
1272 + "' given for token: `$$Token',";
1273 lexrc.printError(s);
1275 return ENGINE_TYPE_DEFAULT;
1279 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1281 int const type = readCiteEngineType(lexrc);
1284 // Cite engine definitions do not overwrite existing
1285 // definitions from the class or a module
1286 bool const overwrite = rt != CITE_ENGINE;
1287 while (lexrc.isOK()) {
1289 etype = lexrc.getString();
1290 if (compare_ascii_no_case(etype, "end") == 0)
1295 definition = lexrc.getString();
1296 char initchar = etype[0];
1297 if (initchar == '#')
1299 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1300 bool defined = false;
1301 bool aydefined = false;
1302 bool numdefined = false;
1303 // Check if the macro is already def'ed
1304 for (auto const & cm : cite_macros_) {
1305 if (!(type & cm.first))
1307 if (cm.second.find(etype) != cm.second.end()) {
1308 if (type == cm.first)
1309 // defined as default or specific type
1311 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1312 // defined for author-year
1314 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1315 // defined for numerical
1319 if (!defined || overwrite) {
1320 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1321 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1322 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1323 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1324 if (type == ENGINE_TYPE_DEFAULT)
1325 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1328 bool defined = false;
1329 bool aydefined = false;
1330 bool numdefined = false;
1331 // Check if the format is already def'ed
1332 for (auto const & cm : cite_formats_) {
1333 if (!(type & cm.first))
1335 if (cm.second.find(etype) != cm.second.end()) {
1336 if (type == cm.first)
1337 // defined as default or specific type
1339 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1340 // defined for author-year
1342 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1343 // defined for numerical
1347 if (!defined || overwrite){
1348 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1349 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1350 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1351 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1352 if (type == ENGINE_TYPE_DEFAULT)
1353 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1361 bool TextClass::readFloat(Lexer & lexrc)
1378 FT_ALLOWED_PLACEMENT,
1385 LexerKeyword floatTags[] = {
1386 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1387 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1388 { "allowswide", FT_ALLOWS_WIDE },
1390 { "extension", FT_EXT },
1391 { "guiname", FT_NAME },
1392 { "htmlattr", FT_HTMLATTR },
1393 { "htmlstyle", FT_HTMLSTYLE },
1394 { "htmltag", FT_HTMLTAG },
1395 { "ispredefined", FT_PREDEFINED },
1396 { "listcommand", FT_LISTCOMMAND },
1397 { "listname", FT_LISTNAME },
1398 { "numberwithin", FT_WITHIN },
1399 { "placement", FT_PLACEMENT },
1400 { "refprefix", FT_REFPREFIX },
1401 { "requires", FT_REQUIRES },
1402 { "style", FT_STYLE },
1403 { "type", FT_TYPE },
1404 { "usesfloatpkg", FT_USESFLOAT }
1407 lexrc.pushTable(floatTags);
1411 docstring htmlstyle;
1417 string allowed_placement = "!htbpH";
1423 bool usesfloat = true;
1424 bool ispredefined = false;
1425 bool allowswide = true;
1426 bool allowssideways = true;
1428 bool getout = false;
1429 while (!getout && lexrc.isOK()) {
1430 int le = lexrc.lex();
1432 case Lexer::LEX_UNDEF:
1433 lexrc.printError("Unknown float tag `$$Token'");
1441 type = lexrc.getString();
1442 if (floatlist_.typeExist(type)) {
1443 Floating const & fl = floatlist_.getType(type);
1444 placement = fl.placement();
1446 within = fl.within();
1449 listname = fl.listName();
1450 usesfloat = fl.usesFloatPkg();
1451 ispredefined = fl.isPredefined();
1452 listcommand = fl.listCommand();
1453 refprefix = fl.refPrefix();
1458 name = lexrc.getString();
1462 placement = lexrc.getString();
1464 case FT_ALLOWED_PLACEMENT:
1466 allowed_placement = lexrc.getString();
1470 ext = lexrc.getString();
1474 within = lexrc.getString();
1475 if (within == "none")
1480 style = lexrc.getString();
1482 case FT_LISTCOMMAND:
1484 listcommand = lexrc.getString();
1488 refprefix = lexrc.getString();
1492 listname = lexrc.getString();
1496 usesfloat = lexrc.getBool();
1500 required = lexrc.getString();
1504 ispredefined = lexrc.getBool();
1506 case FT_ALLOWS_SIDEWAYS:
1508 allowssideways = lexrc.getBool();
1510 case FT_ALLOWS_WIDE:
1512 allowswide = lexrc.getBool();
1516 htmlattr = lexrc.getString();
1520 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1524 htmltag = lexrc.getString();
1534 // Here we have a full float if getout == true
1536 if (!usesfloat && listcommand.empty()) {
1537 // if this float uses the same auxfile as an existing one,
1538 // there is no need for it to provide a list command.
1539 bool found_ext = false;
1540 for (auto const & f : floatlist_) {
1541 if (f.second.ext() == ext) {
1547 LYXERR0("The layout does not provide a list command " <<
1548 "for the float `" << type << "'. LyX will " <<
1549 "not be able to produce a float list.");
1551 Floating fl(type, placement, ext, within, style, name,
1552 listname, listcommand, refprefix, allowed_placement,
1553 htmltag, htmlattr, htmlstyle, required, usesfloat,
1554 ispredefined, allowswide, allowssideways);
1555 floatlist_.newFloat(fl);
1556 // each float has its own counter
1557 counters_.newCounter(from_ascii(type), from_ascii(within),
1558 docstring(), docstring(),
1559 bformat(_("%1$s (Float)"), _(name)));
1560 // also define sub-float counters
1561 docstring const subtype = "sub-" + from_ascii(type);
1562 counters_.newCounter(subtype, from_ascii(type),
1563 "\\alph{" + subtype + "}", docstring(),
1564 bformat(_("Sub-%1$s (Float)"), _(name)));
1570 bool TextClass::readOutlinerName(Lexer & lexrc)
1575 type = lexrc.getString();
1577 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1581 name = lexrc.getDocString();
1583 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1586 outliner_names_[type] = name;
1591 string const & TextClass::prerequisites(string const & sep) const
1593 if (contains(prerequisites_, ',')) {
1594 vector<string> const pres = getVectorFromString(prerequisites_);
1595 prerequisites_ = getStringFromVector(pres, sep);
1597 return prerequisites_;
1601 bool TextClass::hasLayout(docstring const & n) const
1603 docstring const name = n.empty() ? defaultLayoutName() : n;
1604 return getLayout(name) != nullptr;
1608 bool TextClass::hasInsetLayout(docstring const & n) const
1612 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1613 return it != insetlayoutlist_.end();
1617 Layout const & TextClass::operator[](docstring const & name) const
1619 LATTEST(!name.empty());
1621 Layout const * c = getLayout(name);
1623 LYXERR0("We failed to find the layout '" << name
1624 << "' in the layout list. You MUST investigate!");
1625 for (auto const & lay : *this)
1626 lyxerr << " " << to_utf8(lay.name()) << endl;
1628 // We require the name to exist
1629 static const Layout dummy;
1630 LASSERT(false, return dummy);
1637 Layout & TextClass::operator[](docstring const & name)
1639 LATTEST(!name.empty());
1640 // Safe to continue, given what we do below.
1642 Layout * c = getLayout(name);
1644 LYXERR0("We failed to find the layout '" << to_utf8(name)
1645 << "' in the layout list. You MUST investigate!");
1646 for (auto const & lay : *this)
1647 LYXERR0(" " << to_utf8(lay.name()));
1649 // we require the name to exist
1651 // we are here only in release mode
1652 layoutlist_.push_back(createBasicLayout(name, true));
1653 c = getLayout(name);
1660 bool TextClass::deleteLayout(docstring const & name)
1662 if (name == defaultLayoutName() || name == plainLayoutName())
1665 LayoutList::iterator it =
1666 remove_if(layoutlist_.begin(), layoutlist_.end(),
1667 [name](const Layout &c) { return c.name() == name; });
1669 LayoutList::iterator const end = layoutlist_.end();
1670 bool const ret = (it != end);
1671 layoutlist_.erase(it, end);
1676 bool TextClass::deleteInsetLayout(docstring const & name)
1678 return insetlayoutlist_.erase(name);
1682 // Load textclass info if not loaded yet
1683 bool TextClass::load(string const & path) const
1688 // Read style-file, provided path is searched before the system ones
1689 // If path is a file, it is loaded directly.
1690 FileName layout_file(path);
1691 if (!path.empty() && !layout_file.isReadableFile())
1692 layout_file = FileName(addName(path, name_ + ".layout"));
1693 if (layout_file.empty() || !layout_file.exists())
1694 layout_file = libFileSearch("layouts", name_, "layout");
1695 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1698 lyxerr << "Error reading `"
1699 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1700 << "'\n(Check `" << name_
1701 << "')\nCheck your installation and "
1702 "try Options/Reconfigure..."
1710 Layout const * TextClass::getLayout(docstring const & name) const
1712 LayoutList::const_iterator cit =
1713 find_if(begin(), end(),
1714 [name](const Layout &c) { return c.name() == name; });
1715 if (cit == layoutlist_.end())
1722 Layout * TextClass::getLayout(docstring const & name)
1724 LayoutList::iterator it =
1725 find_if(layoutlist_.begin(), layoutlist_.end(),
1726 [name](const Layout &c) { return c.name() == name; });
1727 if (it == layoutlist_.end())
1734 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1739 layoutlist_.push_back(createBasicLayout(n, true));
1744 string DocumentClass::forcedLayouts() const
1748 for (auto const & lay : *this) {
1749 if (lay.forcelocal > 0) {
1751 os << "Format " << LAYOUT_FORMAT << '\n';
1761 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1763 // FIXME The fix for the InsetLayout part of 4812 would be here:
1764 // Add the InsetLayout to the document class if it is not found.
1766 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1767 while (!n.empty()) {
1768 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1769 if (cit != cen && cit->first == n) {
1770 if (cit->second.obsoleted_by().empty())
1772 n = cit->second.obsoleted_by();
1773 return insetLayout(n);
1775 // If we have a generic prefix (e.g., "Note:"),
1776 // try if this one alone is found.
1777 size_t i = n.find(':');
1778 if (i == string::npos)
1782 // Layout "name" not found.
1783 return plainInsetLayout();
1787 InsetLayout const & DocumentClass::plainInsetLayout() {
1788 static const InsetLayout plain_insetlayout_;
1789 return plain_insetlayout_;
1793 docstring const & TextClass::defaultLayoutName() const
1795 return defaultlayout_;
1799 Layout const & TextClass::defaultLayout() const
1801 return operator[](defaultLayoutName());
1805 bool TextClass::isDefaultLayout(Layout const & layout) const
1807 return layout.name() == defaultLayoutName();
1811 bool TextClass::isPlainLayout(Layout const & layout) const
1813 return layout.name() == plainLayoutName();
1817 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1819 static Layout * defaultLayout = nullptr;
1821 if (defaultLayout) {
1822 defaultLayout->setUnknown(unknown);
1823 defaultLayout->setName(name);
1824 return *defaultLayout;
1827 static char const * s = "Margin Static\n"
1828 "LatexType Paragraph\n"
1831 "AlignPossible Left, Right, Center\n"
1832 "LabelType No_Label\n"
1834 istringstream ss(s);
1835 Lexer lex(textClassTags);
1837 defaultLayout = new Layout;
1838 defaultLayout->setUnknown(unknown);
1839 defaultLayout->setName(name);
1840 if (!readStyle(lex, *defaultLayout)) {
1841 // The only way this happens is because the hardcoded layout above
1845 return *defaultLayout;
1849 DocumentClassPtr getDocumentClass(
1850 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1851 string const & cengine, bool const clone)
1853 DocumentClassPtr doc_class =
1854 DocumentClassPtr(new DocumentClass(baseClass));
1855 for (auto const & mod : modlist) {
1856 LyXModule * lm = theModuleList[mod];
1858 docstring const msg =
1859 bformat(_("The module %1$s has been requested by\n"
1860 "this document but has not been found in the list of\n"
1861 "available modules. If you recently installed it, you\n"
1862 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1864 frontend::Alert::warning(_("Module not available"), msg);
1867 if (!lm->isAvailable() && !clone) {
1868 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1869 docstring const msg =
1870 bformat(_("The module %1$s requires a package that is not\n"
1871 "available in your LaTeX installation, or a converter that\n"
1872 "you have not installed. LaTeX output may not be possible.\n"
1873 "Missing prerequisites:\n"
1875 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1876 from_utf8(mod), prereqs);
1877 frontend::Alert::warning(_("Package not available"), msg, true);
1879 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1880 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1881 docstring const msg =
1882 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1883 frontend::Alert::warning(_("Read Error"), msg);
1887 if (cengine.empty())
1890 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1892 docstring const msg =
1893 bformat(_("The cite engine %1$s has been requested by\n"
1894 "this document but has not been found in the list of\n"
1895 "available engines. If you recently installed it, you\n"
1896 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1898 frontend::Alert::warning(_("Cite Engine not available"), msg);
1899 } else if (!ce->isAvailable() && !clone) {
1900 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1901 docstring const msg =
1902 bformat(_("The cite engine %1$s requires a package that is not\n"
1903 "available in your LaTeX installation, or a converter that\n"
1904 "you have not installed. LaTeX output may not be possible.\n"
1905 "Missing prerequisites:\n"
1907 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1908 from_utf8(cengine), prereqs);
1909 frontend::Alert::warning(_("Package not available"), msg, true);
1911 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1912 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1913 docstring const msg =
1914 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1915 frontend::Alert::warning(_("Read Error"), msg);
1923 /////////////////////////////////////////////////////////////////////////
1927 /////////////////////////////////////////////////////////////////////////
1929 DocumentClass::DocumentClass(LayoutFile const & tc)
1934 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1936 for (auto const & l : layoutlist_)
1937 if (l.latexname() == lay)
1943 bool DocumentClass::provides(string const & p) const
1945 return provides_.find(p) != provides_.end();
1949 bool DocumentClass::hasTocLevels() const
1951 return min_toclevel_ != Layout::NOT_IN_TOC;
1955 Layout const & DocumentClass::getTOCLayout() const
1957 // we're going to look for the layout with the minimum toclevel
1958 int minlevel = 1000;
1959 Layout const * lay = nullptr;
1960 for (auto const & l : *this) {
1961 int const level = l.toclevel;
1962 // we don't want Part or unnumbered sections
1963 if (level == Layout::NOT_IN_TOC || level < 0
1964 || level >= minlevel || l.counter.empty())
1971 // hmm. that is very odd, so we'll do our best.
1972 return operator[](defaultLayoutName());
1976 Layout const & DocumentClass::htmlTOCLayout() const
1978 if (html_toc_section_.empty())
1979 html_toc_section_ = getTOCLayout().name();
1980 return operator[](html_toc_section_);
1984 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1985 string const & entry, bool const punct, string const & fallback) const
1987 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
1988 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
1989 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1991 default_format += ".";
1993 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1994 if (itype == cite_formats_.end())
1995 return default_format;
1996 map<string, string>::const_iterator it = itype->second.find(entry);
1997 if (it == itype->second.end() && !fallback.empty())
1998 it = itype->second.find(fallback);
1999 if (it == itype->second.end())
2000 return default_format;
2002 return it->second + ".";
2007 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2008 string const & macro) const
2010 static string empty;
2011 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2012 if (itype == cite_macros_.end())
2014 map<string, string>::const_iterator it = itype->second.find(macro);
2015 if (it == itype->second.end())
2021 vector<string> const DocumentClass::citeCommands(
2022 CiteEngineType const & type) const
2024 vector<CitationStyle> const styles = citeStyles(type);
2025 vector<string> cmds;
2026 for (auto const & cs : styles)
2027 cmds.push_back(cs.name);
2033 vector<CitationStyle> const & DocumentClass::citeStyles(
2034 CiteEngineType const & type) const
2036 static vector<CitationStyle> empty;
2037 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2038 if (it == cite_styles_.end())
2044 /////////////////////////////////////////////////////////////////////////
2048 /////////////////////////////////////////////////////////////////////////
2050 ostream & operator<<(ostream & os, PageSides p)