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 = 82; // dourouc05: DocBook additions.
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), docbookroot_("article"), docbookforceabstract_(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,
221 TC_DOCBOOKFORCEABSTRACT
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 { "insetlayout", TC_INSETLAYOUT },
256 { "leftmargin", TC_LEFTMARGIN },
257 { "maxcitenames", TC_MAXCITENAMES },
258 { "modifystyle", TC_MODIFYSTYLE },
259 { "nocounter", TC_NOCOUNTER },
260 { "nofloat", TC_NOFLOAT },
261 { "noinsetlayout", TC_NOINSETLAYOUT },
262 { "nostyle", TC_NOSTYLE },
263 { "outlinername", TC_OUTLINERNAME },
264 { "outputformat", TC_OUTPUTFORMAT },
265 { "outputtype", TC_OUTPUTTYPE },
266 { "packageoptions", TC_PKGOPTS },
267 { "pagesize", TC_PAGESIZE },
268 { "pagestyle", TC_PAGESTYLE },
269 { "preamble", TC_PREAMBLE },
270 { "provides", TC_PROVIDES },
271 { "providesmodule", TC_PROVIDESMODULE },
272 { "providestyle", TC_PROVIDESTYLE },
273 { "requires", TC_REQUIRES },
274 { "rightmargin", TC_RIGHTMARGIN },
275 { "secnumdepth", TC_SECNUMDEPTH },
276 { "sides", TC_SIDES },
277 { "style", TC_STYLE },
278 { "tablestyle", TC_TABLESTYLE },
279 { "titlelatexname", TC_TITLELATEXNAME },
280 { "titlelatextype", TC_TITLELATEXTYPE },
281 { "tocdepth", TC_TOCDEPTH }
287 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
289 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
290 TempFile tmp("convertXXXXXX.layout");
291 FileName const tempfile = tmp.name();
292 bool success = layout2layout(filename, tempfile);
294 success = readWithoutConv(tempfile, rt) == OK;
299 std::string TextClass::convert(std::string const & str)
301 TempFile tmp1("localXXXXXX.layout");
302 FileName const fn = tmp1.name();
303 ofstream os(fn.toFilesystemEncoding().c_str());
306 TempFile tmp2("convert_localXXXXXX.layout");
307 FileName const tempfile = tmp2.name();
308 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
311 ifstream is(tempfile.toFilesystemEncoding().c_str());
323 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
325 if (!filename.isReadableFile()) {
326 lyxerr << "Cannot read layout file `" << filename << "'."
331 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
332 to_utf8(makeDisplayPath(filename.absFileName())));
334 // Define the plain layout used in table cells, ert, etc. Note that
335 // we do this before loading any layout file, so that classes can
336 // override features of this layout if they should choose to do so.
337 if (rt == BASECLASS && !hasLayout(plain_layout_))
338 layoutlist_.push_back(createBasicLayout(plain_layout_));
340 Lexer lexrc(textClassTags);
341 lexrc.setFile(filename);
342 ReturnValues retval = read(lexrc, rt);
344 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
345 to_utf8(makeDisplayPath(filename.absFileName())));
351 bool TextClass::read(FileName const & filename, ReadType rt)
353 ReturnValues const retval = readWithoutConv(filename, rt);
354 if (retval != FORMAT_MISMATCH)
357 bool const worx = convertLayoutFormat(filename, rt);
359 LYXERR0 ("Unable to convert " << filename <<
360 " to format " << LAYOUT_FORMAT);
365 TextClass::ReturnValues TextClass::validate(std::string const & str)
368 return tc.read(str, VALIDATION);
372 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
374 Lexer lexrc(textClassTags);
375 istringstream is(str);
377 ReturnValues retval = read(lexrc, rt);
379 if (retval != FORMAT_MISMATCH)
382 // write the layout string to a temporary file
383 TempFile tmp("TextClass_read");
384 FileName const tempfile = tmp.name();
385 ofstream os(tempfile.toFilesystemEncoding().c_str());
387 LYXERR0("Unable to create temporary file");
393 // now try to convert it to LAYOUT_FORMAT
394 if (!convertLayoutFormat(tempfile, rt)) {
395 LYXERR0("Unable to convert internal layout information to format "
404 // Reads a textclass structure from file.
405 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
410 // The first usable line should be
411 // Format LAYOUT_FORMAT
412 if (lexrc.lex() != TC_FORMAT || !lexrc.next()
413 || lexrc.getInteger() != LAYOUT_FORMAT)
414 return FORMAT_MISMATCH;
418 while (lexrc.isOK() && !error) {
419 int le = lexrc.lex();
422 case Lexer::LEX_FEOF:
425 case Lexer::LEX_UNDEF:
426 lexrc.printError("Unknown TextClass tag `$$Token'");
434 // used below to track whether we are in an IfStyle or IfCounter tag.
435 bool modifystyle = false;
436 bool providestyle = false;
437 bool ifcounter = 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_ = "docbook";
463 outputFormat_ = "literate";
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 tmp = libFileSearch("layouts", inc,
480 lexrc.printError("Could not find input file: " + inc);
482 } else if (!read(tmp, MERGE)) {
483 lexrc.printError("Error reading input file: " + tmp.absFileName());
489 case TC_DEFAULTSTYLE:
491 docstring const name = from_utf8(subst(lexrc.getString(),
493 defaultlayout_ = name;
500 case TC_PROVIDESTYLE:
501 // if modifystyle is true, then we got here by falling through
502 // so we are not in an ProvideStyle block
508 lexrc.printError("No name given for style: `$$Token'.");
512 docstring const name = from_utf8(subst(lexrc.getString(),
515 string s = "Could not read name for style: `$$Token' "
516 + lexrc.getString() + " is probably not valid UTF-8!";
519 // Since we couldn't read the name, we just scan the rest
520 // of the style and discard it.
521 error = !readStyle(lexrc, lay);
525 bool const have_layout = hasLayout(name);
527 // If the layout already exists, then we want to add it to
528 // the existing layout, as long as we are not in an ProvideStyle
530 if (have_layout && !providestyle) {
531 Layout & lay = operator[](name);
532 error = !readStyle(lexrc, lay);
534 // If the layout does not exist, then we want to create a new
535 // one, but not if we are in a ModifyStyle block.
536 else if (!have_layout && !modifystyle) {
538 layout.setName(name);
539 error = !readStyle(lexrc, layout);
541 layoutlist_.push_back(layout);
543 if (defaultlayout_.empty()) {
544 // We do not have a default layout yet, so we choose
545 // the first layout we encounter.
546 defaultlayout_ = name;
549 // There are two ways to get here:
550 // (i) The layout exists but we are in an ProvideStyle block
551 // (ii) The layout doesn't exist, but we are in an ModifyStyle
553 // Either way, we just scan the rest and discard it
556 // signal to coverity that we do not care about the result
557 (void)readStyle(lexrc, lay);
564 docstring const style = from_utf8(subst(lexrc.getString(),
566 if (!deleteLayout(style))
567 lyxerr << "Cannot delete style `"
568 << to_utf8(style) << '\'' << endl;
572 case TC_NOINSETLAYOUT:
574 docstring const style = from_utf8(subst(lexrc.getString(),
576 if (!deleteInsetLayout(style))
577 LYXERR0("Style `" << style << "' cannot be removed\n"
578 "because it was not found!");
584 columns_ = lexrc.getInteger();
589 switch (lexrc.getInteger()) {
590 case 1: sides_ = OneSide; break;
591 case 2: sides_ = TwoSides; break;
593 lyxerr << "Impossible number of page"
594 " sides, setting to one."
604 pagesize_ = rtrim(lexrc.getString());
609 pagestyle_ = rtrim(lexrc.getString());
613 defaultfont_ = lyxRead(lexrc);
614 if (!defaultfont_.resolved()) {
615 lexrc.printError("Warning: defaultfont should "
616 "be fully instantiated!");
617 defaultfont_.realize(sane_font);
623 secnumdepth_ = lexrc.getInteger();
628 tocdepth_ = lexrc.getInteger();
631 // First step to support options
632 case TC_CLASSOPTIONS:
633 readClassOptions(lexrc);
637 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
640 case TC_HTMLPREAMBLE:
641 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
645 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
648 case TC_HTMLTOCSECTION:
649 html_toc_section_ = from_utf8(trim(lexrc.getString()));
652 case TC_ADDTOPREAMBLE:
653 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
656 case TC_ADDTOHTMLPREAMBLE:
657 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
660 case TC_ADDTOHTMLSTYLES:
661 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
666 string const feature = lexrc.getString();
668 if (lexrc.getInteger())
669 provides_.insert(feature);
671 provides_.erase(feature);
677 vector<string> const req
678 = getVectorFromString(lexrc.getString());
679 required_.insert(req.begin(), req.end());
685 string const pkg = lexrc.getString();
687 string const options = lexrc.getString();
688 package_options_[pkg] = options;
692 case TC_DEFAULTMODULE: {
694 string const module = lexrc.getString();
695 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
696 default_modules_.push_back(module);
700 case TC_PROVIDESMODULE: {
702 string const module = lexrc.getString();
703 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
704 provided_modules_.push_back(module);
708 case TC_EXCLUDESMODULE: {
710 string const module = lexrc.getString();
711 // modules already have their own way to exclude other modules
713 LYXERR0("ExcludesModule tag cannot be used in a module!");
716 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
717 excluded_modules_.push_back(module);
721 case TC_LEFTMARGIN: // left margin type
723 leftmargin_ = lexrc.getDocString();
726 case TC_RIGHTMARGIN: // right margin type
728 rightmargin_ = lexrc.getDocString();
731 case TC_INSETLAYOUT: {
733 lexrc.printError("No name given for InsetLayout: `$$Token'.");
737 docstring const name = subst(lexrc.getDocString(), '_', ' ');
738 bool const validating = (rt == VALIDATION);
740 string s = "Could not read name for InsetLayout: `$$Token' "
741 + lexrc.getString() + " is probably not valid UTF-8!";
744 // Since we couldn't read the name, we just scan the rest
745 // of the style and discard it.
746 il.read(lexrc, *this);
747 // Let's try to continue rather than abort, unless we're validating
748 // in which case we want to report the error
751 } else if (hasInsetLayout(name)) {
752 InsetLayout & il = insetlayoutlist_[name];
753 error = !il.read(lexrc, *this, validating);
757 error = !il.read(lexrc, *this, validating);
759 insetlayoutlist_[name] = il;
765 error = !readFloat(lexrc);
769 error = !readCiteEngine(lexrc, rt);
772 case TC_ADDTOCITEENGINE:
773 error = !readCiteEngine(lexrc, rt, true);
776 case TC_CITEENGINETYPE:
778 opt_enginetype_ = rtrim(lexrc.getString());
782 error = !readCiteFormat(lexrc, rt);
785 case TC_CITEFRAMEWORK:
787 citeframework_ = rtrim(lexrc.getString());
790 case TC_MAXCITENAMES:
792 maxcitenames_ = size_t(lexrc.getInteger());
795 case TC_DEFAULTBIBLIO:
797 vector<string> const dbs =
798 getVectorFromString(rtrim(lexrc.getString()), "|");
799 for (auto const & dbase : dbs) {
800 if (!contains(dbase, ':')) {
801 vector<string> const enginetypes =
802 getVectorFromString(opt_enginetype_, "|");
803 for (string const & s: enginetypes)
804 cite_default_biblio_style_[s] = dbase;
807 string const db = split(dbase, eng, ':');
808 cite_default_biblio_style_[eng] = db;
816 bibintoc_ = lexrc.getBool();
819 case TC_FULLAUTHORLIST:
821 cite_full_author_list_ &= lexrc.getBool();
826 docstring const cnt = lexrc.getDocString();
827 if (!counters_.remove(cnt))
828 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
837 docstring const name = lexrc.getDocString();
839 string s = "Could not read name for counter: `$$Token' "
840 + lexrc.getString() + " is probably not valid UTF-8!";
841 lexrc.printError(s.c_str());
843 // Since we couldn't read the name, we just scan the rest
847 error = !counters_.read(lexrc, name, !ifcounter);
850 lexrc.printError("No name given for style: `$$Token'.");
855 case TC_TITLELATEXTYPE:
856 readTitleType(lexrc);
859 case TC_TITLELATEXNAME:
861 titlename_ = lexrc.getString();
866 string const nofloat = lexrc.getString();
867 floatlist_.erase(nofloat);
871 case TC_OUTLINERNAME:
872 error = !readOutlinerName(lexrc);
877 tablestyle_ = rtrim(lexrc.getString());
882 docbookroot_ = lexrc.getString();
885 case TC_DOCBOOKFORCEABSTRACT:
887 docbookforceabstract_ = lexrc.getBool();
892 // at present, we abort if we encounter an error,
893 // so there is no point continuing.
900 if (defaultlayout_.empty()) {
901 LYXERR0("Error: Textclass '" << name_
902 << "' is missing a defaultstyle.");
906 // Try to erase "stdinsets" from the provides_ set.
908 // Provides stdinsets 1
909 // declaration simply tells us that the standard insets have been
910 // defined. (It's found in stdinsets.inc but could also be used in
911 // user-defined files.) There isn't really any such package. So we
912 // might as well go ahead and erase it.
913 // If we do not succeed, then it was not there, which means that
914 // the textclass did not provide the definitions of the standard
915 // insets. So we need to try to load them.
916 size_type const erased = provides_.erase("stdinsets");
918 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
921 frontend::Alert::warning(_("Missing File"),
922 _("Could not find stdinsets.inc! This may lead to data loss!"));
924 } else if (!read(tmp, MERGE)) {
925 frontend::Alert::warning(_("Corrupt File"),
926 _("Could not read stdinsets.inc! This may lead to data loss!"));
931 min_toclevel_ = Layout::NOT_IN_TOC;
932 max_toclevel_ = Layout::NOT_IN_TOC;
933 for (auto const & lay : *this) {
934 int const toclevel = lay.toclevel;
935 if (toclevel != Layout::NOT_IN_TOC) {
936 if (min_toclevel_ == Layout::NOT_IN_TOC)
937 min_toclevel_ = toclevel;
939 min_toclevel_ = min(min_toclevel_, toclevel);
940 max_toclevel_ = max(max_toclevel_, toclevel);
943 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
944 << ", maximum is " << max_toclevel_);
946 return (error ? ERROR : OK);
950 void TextClass::readTitleType(Lexer & lexrc)
952 LexerKeyword titleTypeTags[] = {
953 { "commandafter", TITLE_COMMAND_AFTER },
954 { "environment", TITLE_ENVIRONMENT }
957 PushPopHelper pph(lexrc, titleTypeTags);
959 int le = lexrc.lex();
961 case Lexer::LEX_UNDEF:
962 lexrc.printError("Unknown output type `$$Token'");
964 case TITLE_COMMAND_AFTER:
965 case TITLE_ENVIRONMENT:
966 titletype_ = static_cast<TitleLatexType>(le);
969 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
975 void TextClass::readOutputType(Lexer & lexrc)
977 LexerKeyword outputTypeTags[] = {
978 { "docbook", DOCBOOK },
980 { "literate", LITERATE }
983 PushPopHelper pph(lexrc, outputTypeTags);
985 int le = lexrc.lex();
987 case Lexer::LEX_UNDEF:
988 lexrc.printError("Unknown output type `$$Token'");
993 outputType_ = static_cast<OutputType>(le);
996 LYXERR0("Unhandled value " << le);
1002 void TextClass::readClassOptions(Lexer & lexrc)
1014 LexerKeyword classOptionsTags[] = {
1016 {"fontsize", CO_FONTSIZE },
1017 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1018 {"other", CO_OTHER },
1019 {"pagesize", CO_PAGESIZE },
1020 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1021 {"pagestyle", CO_PAGESTYLE }
1024 lexrc.pushTable(classOptionsTags);
1025 bool getout = false;
1026 while (!getout && lexrc.isOK()) {
1027 int le = lexrc.lex();
1029 case Lexer::LEX_UNDEF:
1030 lexrc.printError("Unknown ClassOption tag `$$Token'");
1038 opt_fontsize_ = rtrim(lexrc.getString());
1040 case CO_FONTSIZE_FORMAT:
1042 fontsize_format_ = rtrim(lexrc.getString());
1046 opt_pagesize_ = rtrim(lexrc.getString());
1048 case CO_PAGESIZE_FORMAT:
1050 pagesize_format_ = rtrim(lexrc.getString());
1054 opt_pagestyle_ = rtrim(lexrc.getString());
1058 if (options_.empty())
1059 options_ = lexrc.getString();
1061 options_ += ',' + lexrc.getString();
1072 vector<CitationStyle> const & TextClass::getCiteStyles(
1073 CiteEngineType const & type) const
1075 static vector<CitationStyle> empty;
1076 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1077 if (it == cite_styles_.end())
1083 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1085 int const type = readCiteEngineType(lexrc);
1086 bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1087 bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1088 bool defce = (type & ENGINE_TYPE_DEFAULT);
1090 if (rt == CITE_ENGINE) {
1091 // The cite engines are not supposed to overwrite
1092 // CiteStyle defined by the class or a module.
1094 authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1096 numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1098 defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1101 if (rt != CITE_ENGINE && !add) {
1102 // Reset if we defined CiteStyle
1103 // from the class or a module
1105 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1107 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1109 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1113 bool getout = false;
1114 while (!getout && lexrc.isOK()) {
1116 def = lexrc.getString();
1117 def = subst(def, " ", "");
1118 def = subst(def, "\t", "");
1119 if (compare_ascii_no_case(def, "end") == 0) {
1124 char ichar = def[0];
1127 if (isUpperCase(ichar)) {
1128 cs.forceUpperCase = true;
1129 def[0] = lowercase(ichar);
1132 /** For portability reasons (between different
1133 * cite engines such as natbib and biblatex),
1134 * we distinguish between:
1135 * 1. The LyX name as output in the LyX file
1136 * 2. Possible aliases that might fall back to
1137 * the given LyX name in the current engine
1138 * 3. The actual LaTeX command that is output
1139 * (2) and (3) are optional.
1140 * Also, the GUI string for the starred version can
1143 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1152 ScanMode mode = LyXName;
1153 ScanMode oldmode = LyXName;
1158 size_t const n = def.size();
1159 for (size_t i = 0; i != n; ++i) {
1163 else if (ichar == '=')
1165 else if (ichar == '<') {
1168 } else if (ichar == '>')
1170 else if (mode == LaTeXCmd)
1172 else if (mode == StarDesc)
1174 else if (ichar == '$')
1175 cs.hasQualifiedList = true;
1176 else if (ichar == '*')
1177 cs.hasStarredVersion = true;
1178 else if (ichar == '[' && cs.textAfter)
1179 cs.textBefore = true;
1180 else if (ichar == '[')
1181 cs.textAfter = true;
1182 else if (ichar != ']') {
1190 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1191 if (!alias.empty()) {
1192 vector<string> const aliases = getVectorFromString(alias);
1193 for (string const & s: aliases)
1194 cite_command_aliases_[s] = lyx_cmd;
1196 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1197 int size = int(stardesc.size());
1199 cs.stardesc = stardescs[0];
1201 cs.startooltip = stardescs[1];
1204 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1206 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1208 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1211 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1213 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1215 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1218 // If we do AddToCiteEngine, do not apply yet,
1219 // except if we have already a style to add something to
1220 bool apply_ay = !add;
1221 bool apply_num = !add;
1222 bool apply_def = !add;
1224 if (type & ENGINE_TYPE_AUTHORYEAR)
1225 apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1226 if (type & ENGINE_TYPE_NUMERICAL)
1227 apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1228 if (type & ENGINE_TYPE_DEFAULT)
1229 apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1232 // Add the styles from AddToCiteEngine to the class' styles
1233 // (but only if they are not yet defined)
1234 for (auto const & cis : class_cite_styles_) {
1235 // Only consider the current CiteEngineType
1236 if (!(type & cis.first))
1238 for (auto const & ciss : cis.second) {
1239 bool defined = false;
1240 // Check if the style "name" is already def'ed
1241 for (auto const & av : getCiteStyles(cis.first))
1242 if (av.name == ciss.name)
1245 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1246 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1247 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1248 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1249 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1250 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1254 if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1255 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1256 if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1257 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1258 if (type & ENGINE_TYPE_DEFAULT && apply_def)
1259 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1264 int TextClass::readCiteEngineType(Lexer & lexrc) const
1266 static_assert(ENGINE_TYPE_DEFAULT ==
1267 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1268 "Incorrect default engine type");
1269 if (!lexrc.next()) {
1270 lexrc.printError("No cite engine type given for token: `$$Token'.");
1271 return ENGINE_TYPE_DEFAULT;
1273 string const type = rtrim(lexrc.getString());
1274 if (compare_ascii_no_case(type, "authoryear") == 0)
1275 return ENGINE_TYPE_AUTHORYEAR;
1276 else if (compare_ascii_no_case(type, "numerical") == 0)
1277 return ENGINE_TYPE_NUMERICAL;
1278 else if (compare_ascii_no_case(type, "default") != 0) {
1279 string const s = "Unknown cite engine type `" + type
1280 + "' given for token: `$$Token',";
1281 lexrc.printError(s);
1283 return ENGINE_TYPE_DEFAULT;
1287 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1289 int const type = readCiteEngineType(lexrc);
1292 // Cite engine definitions do not overwrite existing
1293 // definitions from the class or a module
1294 bool const overwrite = rt != CITE_ENGINE;
1295 while (lexrc.isOK()) {
1297 etype = lexrc.getString();
1298 if (compare_ascii_no_case(etype, "end") == 0)
1303 definition = lexrc.getString();
1304 char initchar = etype[0];
1305 if (initchar == '#')
1307 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1308 bool defined = false;
1309 bool aydefined = false;
1310 bool numdefined = false;
1311 // Check if the macro is already def'ed
1312 for (auto const & cm : cite_macros_) {
1313 if (!(type & cm.first))
1315 if (cm.second.find(etype) != cm.second.end()) {
1316 if (type == cm.first)
1317 // defined as default or specific type
1319 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1320 // defined for author-year
1322 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1323 // defined for numerical
1327 if (!defined || overwrite) {
1328 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1329 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1330 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1331 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1332 if (type == ENGINE_TYPE_DEFAULT)
1333 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1336 bool defined = false;
1337 bool aydefined = false;
1338 bool numdefined = false;
1339 // Check if the format is already def'ed
1340 for (auto const & cm : cite_formats_) {
1341 if (!(type & cm.first))
1343 if (cm.second.find(etype) != cm.second.end()) {
1344 if (type == cm.first)
1345 // defined as default or specific type
1347 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1348 // defined for author-year
1350 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1351 // defined for numerical
1355 if (!defined || overwrite){
1356 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1357 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1358 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1359 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1360 if (type == ENGINE_TYPE_DEFAULT)
1361 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1369 bool TextClass::readFloat(Lexer & lexrc)
1388 FT_ALLOWED_PLACEMENT,
1395 LexerKeyword floatTags[] = {
1396 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1397 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1398 { "allowswide", FT_ALLOWS_WIDE },
1399 { "docbookattr", FT_DOCBOOKATTR },
1400 { "docbooktag", FT_DOCBOOKTAG },
1402 { "extension", FT_EXT },
1403 { "guiname", FT_NAME },
1404 { "htmlattr", FT_HTMLATTR },
1405 { "htmlstyle", FT_HTMLSTYLE },
1406 { "htmltag", FT_HTMLTAG },
1407 { "ispredefined", FT_PREDEFINED },
1408 { "listcommand", FT_LISTCOMMAND },
1409 { "listname", FT_LISTNAME },
1410 { "numberwithin", FT_WITHIN },
1411 { "placement", FT_PLACEMENT },
1412 { "refprefix", FT_REFPREFIX },
1413 { "requires", FT_REQUIRES },
1414 { "style", FT_STYLE },
1415 { "type", FT_TYPE },
1416 { "usesfloatpkg", FT_USESFLOAT }
1419 lexrc.pushTable(floatTags);
1423 docstring htmlstyle;
1431 string allowed_placement = "!htbpH";
1437 bool usesfloat = true;
1438 bool ispredefined = false;
1439 bool allowswide = true;
1440 bool allowssideways = true;
1442 bool getout = false;
1443 while (!getout && lexrc.isOK()) {
1444 int le = lexrc.lex();
1446 case Lexer::LEX_UNDEF:
1447 lexrc.printError("Unknown float tag `$$Token'");
1455 type = lexrc.getString();
1456 if (floatlist_.typeExist(type)) {
1457 Floating const & fl = floatlist_.getType(type);
1458 placement = fl.placement();
1460 within = fl.within();
1463 listname = fl.listName();
1464 usesfloat = fl.usesFloatPkg();
1465 ispredefined = fl.isPredefined();
1466 listcommand = fl.listCommand();
1467 refprefix = fl.refPrefix();
1472 name = lexrc.getString();
1476 placement = lexrc.getString();
1478 case FT_ALLOWED_PLACEMENT:
1480 allowed_placement = lexrc.getString();
1484 ext = lexrc.getString();
1488 within = lexrc.getString();
1489 if (within == "none")
1494 style = lexrc.getString();
1496 case FT_LISTCOMMAND:
1498 listcommand = lexrc.getString();
1502 refprefix = lexrc.getString();
1506 listname = lexrc.getString();
1510 usesfloat = lexrc.getBool();
1514 required = lexrc.getString();
1518 ispredefined = lexrc.getBool();
1520 case FT_ALLOWS_SIDEWAYS:
1522 allowssideways = lexrc.getBool();
1524 case FT_ALLOWS_WIDE:
1526 allowswide = lexrc.getBool();
1530 htmlattr = lexrc.getString();
1534 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1538 htmltag = lexrc.getString();
1540 case FT_DOCBOOKATTR:
1542 docbookattr = lexrc.getString();
1546 docbooktag = lexrc.getString();
1556 // Here we have a full float if getout == true
1558 if (!usesfloat && listcommand.empty()) {
1559 // if this float uses the same auxfile as an existing one,
1560 // there is no need for it to provide a list command.
1561 bool found_ext = false;
1562 for (auto const & f : floatlist_) {
1563 if (f.second.ext() == ext) {
1569 LYXERR0("The layout does not provide a list command " <<
1570 "for the float `" << type << "'. LyX will " <<
1571 "not be able to produce a float list.");
1573 Floating fl(type, placement, ext, within, style, name,
1574 listname, listcommand, refprefix, allowed_placement,
1575 htmltag, htmlattr, htmlstyle, docbooktag, docbookattr,
1576 required, usesfloat, ispredefined, allowswide,
1578 floatlist_.newFloat(fl);
1579 // each float has its own counter
1580 counters_.newCounter(from_ascii(type), from_ascii(within),
1581 docstring(), docstring(),
1582 bformat(_("%1$s (Float)"), _(name)));
1583 // also define sub-float counters
1584 docstring const subtype = "sub-" + from_ascii(type);
1585 counters_.newCounter(subtype, from_ascii(type),
1586 "\\alph{" + subtype + "}", docstring(),
1587 bformat(_("Sub-%1$s (Float)"), _(name)));
1593 bool TextClass::readOutlinerName(Lexer & lexrc)
1598 type = lexrc.getString();
1600 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1604 name = lexrc.getDocString();
1606 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1609 outliner_names_[type] = name;
1614 string const & TextClass::prerequisites(string const & sep) const
1616 if (contains(prerequisites_, ',')) {
1617 vector<string> const pres = getVectorFromString(prerequisites_);
1618 prerequisites_ = getStringFromVector(pres, sep);
1620 return prerequisites_;
1624 bool TextClass::hasLayout(docstring const & n) const
1626 docstring const name = n.empty() ? defaultLayoutName() : n;
1627 return getLayout(name) != nullptr;
1631 bool TextClass::hasInsetLayout(docstring const & n) const
1635 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1636 return it != insetlayoutlist_.end();
1640 Layout const & TextClass::operator[](docstring const & name) const
1642 LATTEST(!name.empty());
1644 Layout const * c = getLayout(name);
1646 LYXERR0("We failed to find the layout '" << name
1647 << "' in the layout list. You MUST investigate!");
1648 for (auto const & lay : *this)
1649 lyxerr << " " << to_utf8(lay.name()) << endl;
1651 // We require the name to exist
1652 static const Layout dummy;
1653 LASSERT(false, return dummy);
1660 Layout & TextClass::operator[](docstring const & name)
1662 LATTEST(!name.empty());
1663 // Safe to continue, given what we do below.
1665 Layout * c = getLayout(name);
1667 LYXERR0("We failed to find the layout '" << to_utf8(name)
1668 << "' in the layout list. You MUST investigate!");
1669 for (auto const & lay : *this)
1670 LYXERR0(" " << to_utf8(lay.name()));
1672 // we require the name to exist
1674 // we are here only in release mode
1675 layoutlist_.push_back(createBasicLayout(name, true));
1676 c = getLayout(name);
1683 bool TextClass::deleteLayout(docstring const & name)
1685 if (name == defaultLayoutName() || name == plainLayoutName())
1688 LayoutList::iterator it =
1689 remove_if(layoutlist_.begin(), layoutlist_.end(),
1690 [name](const Layout &c) { return c.name() == name; });
1692 LayoutList::iterator const end = layoutlist_.end();
1693 bool const ret = (it != end);
1694 layoutlist_.erase(it, end);
1699 bool TextClass::deleteInsetLayout(docstring const & name)
1701 return insetlayoutlist_.erase(name);
1705 // Load textclass info if not loaded yet
1706 bool TextClass::load(string const & path) const
1711 // Read style-file, provided path is searched before the system ones
1712 // If path is a file, it is loaded directly.
1713 FileName layout_file(path);
1714 if (!path.empty() && !layout_file.isReadableFile())
1715 layout_file = FileName(addName(path, name_ + ".layout"));
1716 if (layout_file.empty() || !layout_file.exists())
1717 layout_file = libFileSearch("layouts", name_, "layout");
1718 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1721 lyxerr << "Error reading `"
1722 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1723 << "'\n(Check `" << name_
1724 << "')\nCheck your installation and "
1725 "try Options/Reconfigure..."
1733 Layout const * TextClass::getLayout(docstring const & name) const
1735 LayoutList::const_iterator cit =
1736 find_if(begin(), end(),
1737 [name](const Layout &c) { return c.name() == name; });
1738 if (cit == layoutlist_.end())
1745 Layout * TextClass::getLayout(docstring const & name)
1747 LayoutList::iterator it =
1748 find_if(layoutlist_.begin(), layoutlist_.end(),
1749 [name](const Layout &c) { return c.name() == name; });
1750 if (it == layoutlist_.end())
1757 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1762 layoutlist_.push_back(createBasicLayout(n, true));
1767 string DocumentClass::forcedLayouts() const
1771 for (auto const & lay : *this) {
1772 if (lay.forcelocal > 0) {
1774 os << "Format " << LAYOUT_FORMAT << '\n';
1784 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1786 // FIXME The fix for the InsetLayout part of 4812 would be here:
1787 // Add the InsetLayout to the document class if it is not found.
1789 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1790 while (!n.empty()) {
1791 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1792 if (cit != cen && cit->first == n) {
1793 if (cit->second.obsoleted_by().empty())
1795 n = cit->second.obsoleted_by();
1796 return insetLayout(n);
1798 // If we have a generic prefix (e.g., "Note:"),
1799 // try if this one alone is found.
1800 size_t i = n.find(':');
1801 if (i == string::npos)
1805 // Layout "name" not found.
1806 return plainInsetLayout();
1810 InsetLayout const & DocumentClass::plainInsetLayout() {
1811 static const InsetLayout plain_insetlayout_;
1812 return plain_insetlayout_;
1816 docstring const & TextClass::defaultLayoutName() const
1818 return defaultlayout_;
1822 Layout const & TextClass::defaultLayout() const
1824 return operator[](defaultLayoutName());
1828 bool TextClass::isDefaultLayout(Layout const & layout) const
1830 return layout.name() == defaultLayoutName();
1834 bool TextClass::isPlainLayout(Layout const & layout) const
1836 return layout.name() == plainLayoutName();
1840 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1842 static Layout * defaultLayout = nullptr;
1844 if (defaultLayout) {
1845 defaultLayout->setUnknown(unknown);
1846 defaultLayout->setName(name);
1847 return *defaultLayout;
1850 static char const * s = "Margin Static\n"
1851 "LatexType Paragraph\n"
1854 "AlignPossible Left, Right, Center\n"
1855 "LabelType No_Label\n"
1857 istringstream ss(s);
1858 Lexer lex(textClassTags);
1860 defaultLayout = new Layout;
1861 defaultLayout->setUnknown(unknown);
1862 defaultLayout->setName(name);
1863 if (!readStyle(lex, *defaultLayout)) {
1864 // The only way this happens is because the hardcoded layout above
1868 return *defaultLayout;
1872 DocumentClassPtr getDocumentClass(
1873 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1874 string const & cengine, bool const clone)
1876 DocumentClassPtr doc_class =
1877 DocumentClassPtr(new DocumentClass(baseClass));
1878 for (auto const & mod : modlist) {
1879 LyXModule * lm = theModuleList[mod];
1881 docstring const msg =
1882 bformat(_("The module %1$s has been requested by\n"
1883 "this document but has not been found in the list of\n"
1884 "available modules. If you recently installed it, you\n"
1885 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1887 frontend::Alert::warning(_("Module not available"), msg);
1890 if (!lm->isAvailable() && !clone) {
1891 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1892 docstring const msg =
1893 bformat(_("The module %1$s requires a package that is not\n"
1894 "available in your LaTeX installation, or a converter that\n"
1895 "you have not installed. LaTeX output may not be possible.\n"
1896 "Missing prerequisites:\n"
1898 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1899 from_utf8(mod), prereqs);
1900 frontend::Alert::warning(_("Package not available"), msg, true);
1902 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1903 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1904 docstring const msg =
1905 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1906 frontend::Alert::warning(_("Read Error"), msg);
1910 if (cengine.empty())
1913 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1915 docstring const msg =
1916 bformat(_("The cite engine %1$s has been requested by\n"
1917 "this document but has not been found in the list of\n"
1918 "available engines. If you recently installed it, you\n"
1919 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1921 frontend::Alert::warning(_("Cite Engine not available"), msg);
1922 } else if (!ce->isAvailable() && !clone) {
1923 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1924 docstring const msg =
1925 bformat(_("The cite engine %1$s requires a package that is not\n"
1926 "available in your LaTeX installation, or a converter that\n"
1927 "you have not installed. LaTeX output may not be possible.\n"
1928 "Missing prerequisites:\n"
1930 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1931 from_utf8(cengine), prereqs);
1932 frontend::Alert::warning(_("Package not available"), msg, true);
1934 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1935 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1936 docstring const msg =
1937 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1938 frontend::Alert::warning(_("Read Error"), msg);
1946 /////////////////////////////////////////////////////////////////////////
1950 /////////////////////////////////////////////////////////////////////////
1952 DocumentClass::DocumentClass(LayoutFile const & tc)
1957 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1959 for (auto const & l : layoutlist_)
1960 if (l.latexname() == lay)
1966 bool DocumentClass::provides(string const & p) const
1968 return provides_.find(p) != provides_.end();
1972 bool DocumentClass::hasTocLevels() const
1974 return min_toclevel_ != Layout::NOT_IN_TOC;
1978 Layout const & DocumentClass::getTOCLayout() const
1980 // we're going to look for the layout with the minimum toclevel
1981 int minlevel = 1000;
1982 Layout const * lay = nullptr;
1983 for (auto const & l : *this) {
1984 int const level = l.toclevel;
1985 // we don't want Part or unnumbered sections
1986 if (level == Layout::NOT_IN_TOC || level < 0
1987 || level >= minlevel || l.counter.empty())
1994 // hmm. that is very odd, so we'll do our best.
1995 return operator[](defaultLayoutName());
1999 Layout const & DocumentClass::htmlTOCLayout() const
2001 if (html_toc_section_.empty())
2002 html_toc_section_ = getTOCLayout().name();
2003 return operator[](html_toc_section_);
2007 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
2008 string const & entry, bool const punct, string const & fallback) const
2010 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
2011 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
2012 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
2014 default_format += ".";
2016 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2017 if (itype == cite_formats_.end())
2018 return default_format;
2019 map<string, string>::const_iterator it = itype->second.find(entry);
2020 if (it == itype->second.end() && !fallback.empty())
2021 it = itype->second.find(fallback);
2022 if (it == itype->second.end())
2023 return default_format;
2025 return it->second + ".";
2030 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2031 string const & macro) const
2033 static string empty;
2034 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2035 if (itype == cite_macros_.end())
2037 map<string, string>::const_iterator it = itype->second.find(macro);
2038 if (it == itype->second.end())
2044 vector<string> const DocumentClass::citeCommands(
2045 CiteEngineType const & type) const
2047 vector<CitationStyle> const styles = citeStyles(type);
2048 vector<string> cmds;
2049 for (auto const & cs : styles)
2050 cmds.push_back(cs.name);
2056 vector<CitationStyle> const & DocumentClass::citeStyles(
2057 CiteEngineType const & type) const
2059 static vector<CitationStyle> empty;
2060 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2061 if (it == cite_styles_.end())
2067 /////////////////////////////////////////////////////////////////////////
2071 /////////////////////////////////////////////////////////////////////////
2073 ostream & operator<<(ostream & os, PageSides p)