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 = 85; // tcuvelier: DocBookInnerTag.
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
200 TC_ADDTOHTMLPREAMBLE,
218 TC_DOCBOOKFORCEABSTRACT
224 LexerKeyword textClassTags[] = {
225 { "addtociteengine", TC_ADDTOCITEENGINE },
226 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
227 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
228 { "addtopreamble", TC_ADDTOPREAMBLE },
229 { "bibintoc", TC_BIBINTOC },
230 { "citeengine", TC_CITEENGINE },
231 { "citeenginetype", TC_CITEENGINETYPE },
232 { "citeformat", TC_CITEFORMAT },
233 { "citeframework", TC_CITEFRAMEWORK },
234 { "classoptions", TC_CLASSOPTIONS },
235 { "columns", TC_COLUMNS },
236 { "counter", TC_COUNTER },
237 { "defaultbiblio", TC_DEFAULTBIBLIO },
238 { "defaultfont", TC_DEFAULTFONT },
239 { "defaultmodule", TC_DEFAULTMODULE },
240 { "defaultstyle", TC_DEFAULTSTYLE },
241 { "docbookforceabstract", TC_DOCBOOKFORCEABSTRACT },
242 { "docbookroot", TC_DOCBOOKROOT },
243 { "excludesmodule", TC_EXCLUDESMODULE },
244 { "float", TC_FLOAT },
245 { "format", TC_FORMAT },
246 { "fullauthorlist", TC_FULLAUTHORLIST },
247 { "htmlpreamble", TC_HTMLPREAMBLE },
248 { "htmlstyles", TC_HTMLSTYLES },
249 { "htmltocsection", TC_HTMLTOCSECTION },
250 { "ifcounter", TC_IFCOUNTER },
251 { "input", TC_INPUT },
252 { "insetlayout", TC_INSETLAYOUT },
253 { "leftmargin", TC_LEFTMARGIN },
254 { "maxcitenames", TC_MAXCITENAMES },
255 { "modifystyle", TC_MODIFYSTYLE },
256 { "nocounter", TC_NOCOUNTER },
257 { "nofloat", TC_NOFLOAT },
258 { "noinsetlayout", TC_NOINSETLAYOUT },
259 { "nostyle", TC_NOSTYLE },
260 { "outlinername", TC_OUTLINERNAME },
261 { "outputformat", TC_OUTPUTFORMAT },
262 { "outputtype", TC_OUTPUTTYPE },
263 { "packageoptions", TC_PKGOPTS },
264 { "pagesize", TC_PAGESIZE },
265 { "pagestyle", TC_PAGESTYLE },
266 { "preamble", TC_PREAMBLE },
267 { "provides", TC_PROVIDES },
268 { "providesmodule", TC_PROVIDESMODULE },
269 { "providestyle", TC_PROVIDESTYLE },
270 { "requires", TC_REQUIRES },
271 { "rightmargin", TC_RIGHTMARGIN },
272 { "secnumdepth", TC_SECNUMDEPTH },
273 { "sides", TC_SIDES },
274 { "style", TC_STYLE },
275 { "tablestyle", TC_TABLESTYLE },
276 { "titlelatexname", TC_TITLELATEXNAME },
277 { "titlelatextype", TC_TITLELATEXTYPE },
278 { "tocdepth", TC_TOCDEPTH }
284 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
286 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
287 TempFile tmp("convertXXXXXX.layout");
288 FileName const tempfile = tmp.name();
289 bool success = layout2layout(filename, tempfile);
291 success = readWithoutConv(tempfile, rt) == OK;
296 std::string TextClass::convert(std::string const & str)
298 TempFile tmp1("localXXXXXX.layout");
299 FileName const fn = tmp1.name();
300 ofstream os(fn.toFilesystemEncoding().c_str());
303 TempFile tmp2("convert_localXXXXXX.layout");
304 FileName const tempfile = tmp2.name();
305 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
308 ifstream is(tempfile.toFilesystemEncoding().c_str());
320 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
322 if (!filename.isReadableFile()) {
323 lyxerr << "Cannot read layout file `" << filename << "'."
328 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
329 to_utf8(makeDisplayPath(filename.absFileName())));
331 // Define the plain layout used in table cells, ert, etc. Note that
332 // we do this before loading any layout file, so that classes can
333 // override features of this layout if they should choose to do so.
334 if (rt == BASECLASS && !hasLayout(plain_layout_))
335 layoutlist_.push_back(createBasicLayout(plain_layout_));
337 Lexer lexrc(textClassTags);
338 lexrc.setFile(filename);
339 ReturnValues retval = read(lexrc, rt);
341 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
342 to_utf8(makeDisplayPath(filename.absFileName())));
348 bool TextClass::read(FileName const & filename, ReadType rt)
350 ReturnValues const retval = readWithoutConv(filename, rt);
351 if (retval != FORMAT_MISMATCH)
354 bool const worx = convertLayoutFormat(filename, rt);
356 LYXERR0 ("Unable to convert " << filename <<
357 " to format " << LAYOUT_FORMAT);
362 TextClass::ReturnValues TextClass::validate(std::string const & str)
365 return tc.read(str, VALIDATION);
369 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
371 Lexer lexrc(textClassTags);
372 istringstream is(str);
374 ReturnValues retval = read(lexrc, rt);
376 if (retval != FORMAT_MISMATCH)
379 // write the layout string to a temporary file
380 TempFile tmp("TextClass_read");
381 FileName const tempfile = tmp.name();
382 ofstream os(tempfile.toFilesystemEncoding().c_str());
384 LYXERR0("Unable to create temporary file");
390 // now try to convert it to LAYOUT_FORMAT
391 if (!convertLayoutFormat(tempfile, rt)) {
392 LYXERR0("Unable to convert internal layout information to format "
401 // Reads a textclass structure from file.
402 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
407 // The first usable line should be
408 // Format LAYOUT_FORMAT
409 if (lexrc.lex() != TC_FORMAT || !lexrc.next()
410 || lexrc.getInteger() != LAYOUT_FORMAT)
411 return FORMAT_MISMATCH;
415 while (lexrc.isOK() && !error) {
416 int le = lexrc.lex();
419 case Lexer::LEX_FEOF:
422 case Lexer::LEX_UNDEF:
423 lexrc.printError("Unknown TextClass tag `$$Token'");
431 // used below to track whether we are in an IfStyle or IfCounter tag.
432 bool modifystyle = false;
433 bool providestyle = false;
434 bool ifcounter = false;
436 switch (static_cast<TextClassTags>(le)) {
440 lexrc.printError("Duplicate Format directive");
443 case TC_OUTPUTFORMAT:
445 outputFormat_ = lexrc.getString();
446 has_output_format_ = true;
451 readOutputType(lexrc);
452 switch(outputType_) {
454 outputFormat_ = "latex";
457 outputFormat_ = "literate";
462 case TC_INPUT: // Include file
465 string const inc = lexrc.getString();
466 if (!path().empty() && (prefixIs(inc, "./") ||
467 prefixIs(inc, "../")))
468 tmp = fileSearch(path(), inc, "layout");
470 tmp = libFileSearch("layouts", inc,
474 lexrc.printError("Could not find input file: " + inc);
476 } else if (!read(tmp, MERGE)) {
477 lexrc.printError("Error reading input file: " + tmp.absFileName());
483 case TC_DEFAULTSTYLE:
485 docstring const name = from_utf8(subst(lexrc.getString(),
487 defaultlayout_ = name;
494 case TC_PROVIDESTYLE:
495 // if modifystyle is true, then we got here by falling through
496 // so we are not in an ProvideStyle block
502 lexrc.printError("No name given for style: `$$Token'.");
506 docstring const name = from_utf8(subst(lexrc.getString(),
509 string s = "Could not read name for style: `$$Token' "
510 + lexrc.getString() + " is probably not valid UTF-8!";
513 // Since we couldn't read the name, we just scan the rest
514 // of the style and discard it.
515 error = !readStyle(lexrc, lay, rt);
519 bool const have_layout = hasLayout(name);
521 // If the layout already exists, then we want to add it to
522 // the existing layout, as long as we are not in an ProvideStyle
524 if (have_layout && !providestyle) {
525 Layout & lay = operator[](name);
526 error = !readStyle(lexrc, lay, rt);
528 // If the layout does not exist, then we want to create a new
529 // one, but not if we are in a ModifyStyle block.
530 else if (!have_layout && !modifystyle) {
532 layout.setName(name);
533 error = !readStyle(lexrc, layout, rt);
535 layoutlist_.push_back(layout);
537 if (defaultlayout_.empty()) {
538 // We do not have a default layout yet, so we choose
539 // the first layout we encounter.
540 defaultlayout_ = name;
543 // There are two ways to get here:
544 // (i) The layout exists but we are in an ProvideStyle block
545 // (ii) The layout doesn't exist, but we are in an ModifyStyle
547 // Either way, we just scan the rest and discard it
550 // signal to coverity that we do not care about the result
551 (void)readStyle(lexrc, lay, rt);
558 docstring const style = from_utf8(subst(lexrc.getString(),
560 if (!deleteLayout(style))
561 lyxerr << "Cannot delete style `"
562 << to_utf8(style) << '\'' << endl;
566 case TC_NOINSETLAYOUT:
568 docstring const style = from_utf8(subst(lexrc.getString(),
570 if (!deleteInsetLayout(style))
571 LYXERR0("Style `" << style << "' cannot be removed\n"
572 "because it was not found!");
578 columns_ = lexrc.getInteger();
583 switch (lexrc.getInteger()) {
584 case 1: sides_ = OneSide; break;
585 case 2: sides_ = TwoSides; break;
587 lyxerr << "Impossible number of page"
588 " sides, setting to one."
598 pagesize_ = rtrim(lexrc.getString());
603 pagestyle_ = rtrim(lexrc.getString());
607 defaultfont_ = lyxRead(lexrc);
608 if (!defaultfont_.resolved()) {
609 lexrc.printError("Warning: defaultfont should "
610 "be fully instantiated!");
611 defaultfont_.realize(sane_font);
617 secnumdepth_ = lexrc.getInteger();
622 tocdepth_ = lexrc.getInteger();
625 // First step to support options
626 case TC_CLASSOPTIONS:
627 readClassOptions(lexrc);
631 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
634 case TC_HTMLPREAMBLE:
635 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
639 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
642 case TC_HTMLTOCSECTION:
643 html_toc_section_ = from_utf8(trim(lexrc.getString()));
646 case TC_ADDTOPREAMBLE:
647 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
650 case TC_ADDTOHTMLPREAMBLE:
651 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
654 case TC_ADDTOHTMLSTYLES:
655 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
660 string const feature = lexrc.getString();
662 if (lexrc.getInteger())
663 provides_.insert(feature);
665 provides_.erase(feature);
671 vector<string> const req
672 = getVectorFromString(lexrc.getString());
673 required_.insert(req.begin(), req.end());
679 string const pkg = lexrc.getString();
681 string const options = lexrc.getString();
682 package_options_[pkg] = options;
686 case TC_DEFAULTMODULE: {
688 string const module = lexrc.getString();
689 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
690 default_modules_.push_back(module);
694 case TC_PROVIDESMODULE: {
696 string const module = lexrc.getString();
697 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
698 provided_modules_.push_back(module);
702 case TC_EXCLUDESMODULE: {
704 string const module = lexrc.getString();
705 // modules already have their own way to exclude other modules
707 LYXERR0("ExcludesModule tag cannot be used in a module!");
710 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
711 excluded_modules_.push_back(module);
715 case TC_LEFTMARGIN: // left margin type
717 leftmargin_ = lexrc.getDocString();
720 case TC_RIGHTMARGIN: // right margin type
722 rightmargin_ = lexrc.getDocString();
725 case TC_INSETLAYOUT: {
727 lexrc.printError("No name given for InsetLayout: `$$Token'.");
731 docstring const name = subst(lexrc.getDocString(), '_', ' ');
732 bool const validating = (rt == VALIDATION);
734 string s = "Could not read name for InsetLayout: `$$Token' "
735 + lexrc.getString() + " is probably not valid UTF-8!";
738 // Since we couldn't read the name, we just scan the rest
739 // of the style and discard it.
740 il.read(lexrc, *this);
741 // Let's try to continue rather than abort, unless we're validating
742 // in which case we want to report the error
745 } else if (hasInsetLayout(name)) {
746 InsetLayout & il = insetlayoutlist_[name];
747 error = !il.read(lexrc, *this, validating);
751 error = !il.read(lexrc, *this, validating);
753 insetlayoutlist_[name] = il;
759 error = !readFloat(lexrc);
763 error = !readCiteEngine(lexrc, rt);
766 case TC_ADDTOCITEENGINE:
767 error = !readCiteEngine(lexrc, rt, true);
770 case TC_CITEENGINETYPE:
772 opt_enginetype_ = rtrim(lexrc.getString());
776 error = !readCiteFormat(lexrc, rt);
779 case TC_CITEFRAMEWORK:
781 citeframework_ = rtrim(lexrc.getString());
784 case TC_MAXCITENAMES:
786 maxcitenames_ = size_t(lexrc.getInteger());
789 case TC_DEFAULTBIBLIO:
791 vector<string> const dbs =
792 getVectorFromString(rtrim(lexrc.getString()), "|");
793 for (auto const & dbase : dbs) {
794 if (!contains(dbase, ':')) {
795 vector<string> const enginetypes =
796 getVectorFromString(opt_enginetype_, "|");
797 for (string const & s: enginetypes)
798 cite_default_biblio_style_[s] = dbase;
801 string const db = split(dbase, eng, ':');
802 cite_default_biblio_style_[eng] = db;
810 bibintoc_ = lexrc.getBool();
813 case TC_FULLAUTHORLIST:
815 cite_full_author_list_ &= lexrc.getBool();
820 docstring const cnt = lexrc.getDocString();
821 if (!counters_.remove(cnt))
822 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
831 docstring const name = lexrc.getDocString();
833 string s = "Could not read name for counter: `$$Token' "
834 + lexrc.getString() + " is probably not valid UTF-8!";
835 lexrc.printError(s.c_str());
837 // Since we couldn't read the name, we just scan the rest
841 error = !counters_.read(lexrc, name, !ifcounter);
844 lexrc.printError("No name given for style: `$$Token'.");
849 case TC_TITLELATEXTYPE:
850 readTitleType(lexrc);
853 case TC_TITLELATEXNAME:
855 titlename_ = lexrc.getString();
860 string const nofloat = lexrc.getString();
861 floatlist_.erase(nofloat);
865 case TC_OUTLINERNAME:
866 error = !readOutlinerName(lexrc);
871 tablestyle_ = rtrim(lexrc.getString());
876 docbookroot_ = lexrc.getString();
879 case TC_DOCBOOKFORCEABSTRACT:
881 docbookforceabstract_ = lexrc.getBool();
886 // at present, we abort if we encounter an error,
887 // so there is no point continuing.
894 if (defaultlayout_.empty()) {
895 LYXERR0("Error: Textclass '" << name_
896 << "' is missing a defaultstyle.");
900 // Try to erase "stdinsets" from the provides_ set.
902 // Provides stdinsets 1
903 // declaration simply tells us that the standard insets have been
904 // defined. (It's found in stdinsets.inc but could also be used in
905 // user-defined files.) There isn't really any such package. So we
906 // might as well go ahead and erase it.
907 // If we do not succeed, then it was not there, which means that
908 // the textclass did not provide the definitions of the standard
909 // insets. So we need to try to load them.
910 size_type const erased = provides_.erase("stdinsets");
912 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
915 frontend::Alert::warning(_("Missing File"),
916 _("Could not find stdinsets.inc! This may lead to data loss!"));
918 } else if (!read(tmp, MERGE)) {
919 frontend::Alert::warning(_("Corrupt File"),
920 _("Could not read stdinsets.inc! This may lead to data loss!"));
925 min_toclevel_ = Layout::NOT_IN_TOC;
926 max_toclevel_ = Layout::NOT_IN_TOC;
927 for (auto const & lay : *this) {
928 int const toclevel = lay.toclevel;
929 if (toclevel != Layout::NOT_IN_TOC) {
930 if (min_toclevel_ == Layout::NOT_IN_TOC)
931 min_toclevel_ = toclevel;
933 min_toclevel_ = min(min_toclevel_, toclevel);
934 max_toclevel_ = max(max_toclevel_, toclevel);
937 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
938 << ", maximum is " << max_toclevel_);
940 return (error ? ERROR : OK);
944 void TextClass::readTitleType(Lexer & lexrc)
946 LexerKeyword titleTypeTags[] = {
947 { "commandafter", TITLE_COMMAND_AFTER },
948 { "environment", TITLE_ENVIRONMENT }
951 PushPopHelper pph(lexrc, titleTypeTags);
953 int le = lexrc.lex();
955 case Lexer::LEX_UNDEF:
956 lexrc.printError("Unknown output type `$$Token'");
958 case TITLE_COMMAND_AFTER:
959 case TITLE_ENVIRONMENT:
960 titletype_ = static_cast<TitleLatexType>(le);
963 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
969 void TextClass::readOutputType(Lexer & lexrc)
971 LexerKeyword outputTypeTags[] = {
973 { "literate", LITERATE }
976 PushPopHelper pph(lexrc, outputTypeTags);
978 int le = lexrc.lex();
980 case Lexer::LEX_UNDEF:
981 lexrc.printError("Unknown output type `$$Token'");
985 outputType_ = static_cast<OutputType>(le);
988 LYXERR0("Unhandled value " << le);
994 void TextClass::readClassOptions(Lexer & lexrc)
1006 LexerKeyword classOptionsTags[] = {
1008 {"fontsize", CO_FONTSIZE },
1009 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1010 {"other", CO_OTHER },
1011 {"pagesize", CO_PAGESIZE },
1012 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1013 {"pagestyle", CO_PAGESTYLE }
1016 lexrc.pushTable(classOptionsTags);
1017 bool getout = false;
1018 while (!getout && lexrc.isOK()) {
1019 int le = lexrc.lex();
1021 case Lexer::LEX_UNDEF:
1022 lexrc.printError("Unknown ClassOption tag `$$Token'");
1030 opt_fontsize_ = rtrim(lexrc.getString());
1032 case CO_FONTSIZE_FORMAT:
1034 fontsize_format_ = rtrim(lexrc.getString());
1038 opt_pagesize_ = rtrim(lexrc.getString());
1040 case CO_PAGESIZE_FORMAT:
1042 pagesize_format_ = rtrim(lexrc.getString());
1046 opt_pagestyle_ = rtrim(lexrc.getString());
1050 if (options_.empty())
1051 options_ = lexrc.getString();
1053 options_ += ',' + 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)
1381 FT_ALLOWED_PLACEMENT,
1388 LexerKeyword floatTags[] = {
1389 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1390 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1391 { "allowswide", FT_ALLOWS_WIDE },
1392 { "docbookattr", FT_DOCBOOKATTR },
1393 { "docbooktag", FT_DOCBOOKTAG },
1394 { "docbooktagtype", FT_DOCBOOKTAGTYPE },
1396 { "extension", FT_EXT },
1397 { "guiname", FT_NAME },
1398 { "htmlattr", FT_HTMLATTR },
1399 { "htmlstyle", FT_HTMLSTYLE },
1400 { "htmltag", FT_HTMLTAG },
1401 { "ispredefined", FT_PREDEFINED },
1402 { "listcommand", FT_LISTCOMMAND },
1403 { "listname", FT_LISTNAME },
1404 { "numberwithin", FT_WITHIN },
1405 { "placement", FT_PLACEMENT },
1406 { "refprefix", FT_REFPREFIX },
1407 { "requires", FT_REQUIRES },
1408 { "style", FT_STYLE },
1409 { "type", FT_TYPE },
1410 { "usesfloatpkg", FT_USESFLOAT }
1413 lexrc.pushTable(floatTags);
1417 docstring htmlstyle;
1421 string docbooktagtype;
1426 string allowed_placement = "!htbpH";
1432 bool usesfloat = true;
1433 bool ispredefined = false;
1434 bool allowswide = true;
1435 bool allowssideways = true;
1437 bool getout = false;
1438 while (!getout && lexrc.isOK()) {
1439 int le = lexrc.lex();
1441 case Lexer::LEX_UNDEF:
1442 lexrc.printError("Unknown float tag `$$Token'");
1450 type = lexrc.getString();
1451 if (floatlist_.typeExist(type)) {
1452 Floating const & fl = floatlist_.getType(type);
1453 placement = fl.placement();
1455 within = fl.within();
1458 listname = fl.listName();
1459 usesfloat = fl.usesFloatPkg();
1460 ispredefined = fl.isPredefined();
1461 listcommand = fl.listCommand();
1462 refprefix = fl.refPrefix();
1467 name = lexrc.getString();
1471 placement = lexrc.getString();
1473 case FT_ALLOWED_PLACEMENT:
1475 allowed_placement = lexrc.getString();
1479 ext = lexrc.getString();
1483 within = lexrc.getString();
1484 if (within == "none")
1489 style = lexrc.getString();
1491 case FT_LISTCOMMAND:
1493 listcommand = lexrc.getString();
1497 refprefix = lexrc.getString();
1501 listname = lexrc.getString();
1505 usesfloat = lexrc.getBool();
1509 required = lexrc.getString();
1513 ispredefined = lexrc.getBool();
1515 case FT_ALLOWS_SIDEWAYS:
1517 allowssideways = lexrc.getBool();
1519 case FT_ALLOWS_WIDE:
1521 allowswide = lexrc.getBool();
1525 htmlattr = lexrc.getString();
1529 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1533 htmltag = lexrc.getString();
1535 case FT_DOCBOOKATTR:
1537 docbookattr = lexrc.getString();
1541 docbooktag = lexrc.getString();
1543 case FT_DOCBOOKTAGTYPE:
1545 docbooktagtype = lexrc.getString();
1555 // Here we have a full float if getout == true
1557 if (!usesfloat && listcommand.empty()) {
1558 // if this float uses the same auxfile as an existing one,
1559 // there is no need for it to provide a list command.
1560 bool found_ext = false;
1561 for (auto const & f : floatlist_) {
1562 if (f.second.ext() == ext) {
1568 LYXERR0("The layout does not provide a list command " <<
1569 "for the float `" << type << "'. LyX will " <<
1570 "not be able to produce a float list.");
1572 Floating fl(type, placement, ext, within, style, name,
1573 listname, listcommand, refprefix, allowed_placement,
1574 htmltag, htmlattr, htmlstyle, docbookattr,
1575 docbooktagtype, required, usesfloat, ispredefined,
1576 allowswide, allowssideways);
1577 floatlist_.newFloat(fl);
1578 // each float has its own counter
1579 counters_.newCounter(from_ascii(type), from_ascii(within),
1580 docstring(), docstring(),
1581 bformat(_("%1$s (Float)"), _(name)));
1582 // also define sub-float counters
1583 docstring const subtype = "sub-" + from_ascii(type);
1584 counters_.newCounter(subtype, from_ascii(type),
1585 "\\alph{" + subtype + "}", docstring(),
1586 bformat(_("Sub-%1$s (Float)"), _(name)));
1592 bool TextClass::readOutlinerName(Lexer & lexrc)
1597 type = lexrc.getString();
1599 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1603 name = lexrc.getDocString();
1605 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1608 outliner_names_[type] = name;
1613 string const & TextClass::prerequisites(string const & sep) const
1615 if (contains(prerequisites_, ',')) {
1616 vector<string> const pres = getVectorFromString(prerequisites_);
1617 prerequisites_ = getStringFromVector(pres, sep);
1619 return prerequisites_;
1623 bool TextClass::hasLayout(docstring const & n) const
1625 docstring const name = n.empty() ? defaultLayoutName() : n;
1626 return getLayout(name) != nullptr;
1630 bool TextClass::hasInsetLayout(docstring const & n) const
1634 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1635 return it != insetlayoutlist_.end();
1639 Layout const & TextClass::operator[](docstring const & name) const
1641 LATTEST(!name.empty());
1643 Layout const * c = getLayout(name);
1645 LYXERR0("We failed to find the layout '" << name
1646 << "' in the layout list. You MUST investigate!");
1647 for (auto const & lay : *this)
1648 lyxerr << " " << to_utf8(lay.name()) << endl;
1650 // We require the name to exist
1651 static const Layout dummy;
1652 LASSERT(false, return dummy);
1659 Layout & TextClass::operator[](docstring const & name)
1661 LATTEST(!name.empty());
1662 // Safe to continue, given what we do below.
1664 Layout * c = getLayout(name);
1666 LYXERR0("We failed to find the layout '" << to_utf8(name)
1667 << "' in the layout list. You MUST investigate!");
1668 for (auto const & lay : *this)
1669 LYXERR0(" " << to_utf8(lay.name()));
1671 // we require the name to exist
1673 // we are here only in release mode
1674 layoutlist_.push_back(createBasicLayout(name, true));
1675 c = getLayout(name);
1682 bool TextClass::deleteLayout(docstring const & name)
1684 if (name == defaultLayoutName() || name == plainLayoutName())
1687 LayoutList::iterator it =
1688 remove_if(layoutlist_.begin(), layoutlist_.end(),
1689 [name](const Layout &c) { return c.name() == name; });
1691 LayoutList::iterator const end = layoutlist_.end();
1692 bool const ret = (it != end);
1693 layoutlist_.erase(it, end);
1698 bool TextClass::deleteInsetLayout(docstring const & name)
1700 return insetlayoutlist_.erase(name);
1704 // Load textclass info if not loaded yet
1705 bool TextClass::load(string const & path) const
1710 // Read style-file, provided path is searched before the system ones
1711 // If path is a file, it is loaded directly.
1712 FileName layout_file(path);
1713 if (!path.empty() && !layout_file.isReadableFile())
1714 layout_file = FileName(addName(path, name_ + ".layout"));
1715 if (layout_file.empty() || !layout_file.exists())
1716 layout_file = libFileSearch("layouts", name_, "layout");
1717 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1720 lyxerr << "Error reading `"
1721 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1722 << "'\n(Check `" << name_
1723 << "')\nCheck your installation and "
1724 "try Options/Reconfigure..."
1732 Layout const * TextClass::getLayout(docstring const & name) const
1734 LayoutList::const_iterator cit =
1735 find_if(begin(), end(),
1736 [name](const Layout &c) { return c.name() == name; });
1737 if (cit == layoutlist_.end())
1744 Layout * TextClass::getLayout(docstring const & name)
1746 LayoutList::iterator it =
1747 find_if(layoutlist_.begin(), layoutlist_.end(),
1748 [name](const Layout &c) { return c.name() == name; });
1749 if (it == layoutlist_.end())
1756 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1761 layoutlist_.push_back(createBasicLayout(n, true));
1766 string DocumentClass::forcedLayouts() const
1770 for (auto const & lay : *this) {
1771 if (lay.forcelocal > 0) {
1773 os << "Format " << LAYOUT_FORMAT << '\n';
1783 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1785 // FIXME The fix for the InsetLayout part of 4812 would be here:
1786 // Add the InsetLayout to the document class if it is not found.
1788 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1789 while (!n.empty()) {
1790 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1791 if (cit != cen && cit->first == n) {
1792 if (cit->second.obsoleted_by().empty())
1794 n = cit->second.obsoleted_by();
1795 return insetLayout(n);
1797 // If we have a generic prefix (e.g., "Note:"),
1798 // try if this one alone is found.
1799 size_t i = n.find(':');
1800 if (i == string::npos)
1804 // Layout "name" not found.
1805 return plainInsetLayout();
1809 InsetLayout const & DocumentClass::plainInsetLayout() {
1810 static const InsetLayout plain_insetlayout_;
1811 return plain_insetlayout_;
1815 docstring const & TextClass::defaultLayoutName() const
1817 return defaultlayout_;
1821 Layout const & TextClass::defaultLayout() const
1823 return operator[](defaultLayoutName());
1827 bool TextClass::isDefaultLayout(Layout const & layout) const
1829 return layout.name() == defaultLayoutName();
1833 bool TextClass::isPlainLayout(Layout const & layout) const
1835 return layout.name() == plainLayoutName();
1839 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1841 static Layout * defaultLayout = nullptr;
1843 if (defaultLayout) {
1844 defaultLayout->setUnknown(unknown);
1845 defaultLayout->setName(name);
1846 return *defaultLayout;
1849 static char const * s = "Margin Static\n"
1850 "LatexType Paragraph\n"
1853 "AlignPossible Left, Right, Center\n"
1854 "LabelType No_Label\n"
1856 istringstream ss(s);
1857 Lexer lex(textClassTags);
1859 defaultLayout = new Layout;
1860 defaultLayout->setUnknown(unknown);
1861 defaultLayout->setName(name);
1862 if (!readStyle(lex, *defaultLayout, BASECLASS)) {
1863 // The only way this happens is because the hardcoded layout above
1867 return *defaultLayout;
1871 DocumentClassPtr getDocumentClass(
1872 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1873 string const & cengine, bool const clone)
1875 DocumentClassPtr doc_class =
1876 DocumentClassPtr(new DocumentClass(baseClass));
1877 for (auto const & mod : modlist) {
1878 LyXModule * lm = theModuleList[mod];
1880 docstring const msg =
1881 bformat(_("The module %1$s has been requested by\n"
1882 "this document but has not been found in the list of\n"
1883 "available modules. If you recently installed it, you\n"
1884 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1886 frontend::Alert::warning(_("Module not available"), msg);
1889 if (!lm->isAvailable() && !clone) {
1890 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1891 docstring const msg =
1892 bformat(_("The module %1$s requires a package that is not\n"
1893 "available in your LaTeX installation, or a converter that\n"
1894 "you have not installed. LaTeX output may not be possible.\n"
1895 "Missing prerequisites:\n"
1897 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1898 from_utf8(mod), prereqs);
1899 frontend::Alert::warning(_("Package not available"), msg, true);
1901 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1902 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1903 docstring const msg =
1904 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1905 frontend::Alert::warning(_("Read Error"), msg);
1909 if (cengine.empty())
1912 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1914 docstring const msg =
1915 bformat(_("The cite engine %1$s has been requested by\n"
1916 "this document but has not been found in the list of\n"
1917 "available engines. If you recently installed it, you\n"
1918 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1920 frontend::Alert::warning(_("Cite Engine not available"), msg);
1921 } else if (!ce->isAvailable() && !clone) {
1922 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1923 docstring const msg =
1924 bformat(_("The cite engine %1$s requires a package that is not\n"
1925 "available in your LaTeX installation, or a converter that\n"
1926 "you have not installed. LaTeX output may not be possible.\n"
1927 "Missing prerequisites:\n"
1929 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1930 from_utf8(cengine), prereqs);
1931 frontend::Alert::warning(_("Package not available"), msg, true);
1933 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1934 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1935 docstring const msg =
1936 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1937 frontend::Alert::warning(_("Read Error"), msg);
1945 /////////////////////////////////////////////////////////////////////////
1949 /////////////////////////////////////////////////////////////////////////
1951 DocumentClass::DocumentClass(LayoutFile const & tc)
1956 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1958 for (auto const & l : layoutlist_)
1959 if (l.latexname() == lay)
1965 bool DocumentClass::provides(string const & p) const
1967 return provides_.find(p) != provides_.end();
1971 bool DocumentClass::hasTocLevels() const
1973 return min_toclevel_ != Layout::NOT_IN_TOC;
1977 Layout const & DocumentClass::getTOCLayout() const
1979 // we're going to look for the layout with the minimum toclevel
1980 int minlevel = 1000;
1981 Layout const * lay = nullptr;
1982 for (auto const & l : *this) {
1983 int const level = l.toclevel;
1984 // we don't want Part or unnumbered sections
1985 if (level == Layout::NOT_IN_TOC || level < 0
1986 || level >= minlevel || l.counter.empty())
1993 // hmm. that is very odd, so we'll do our best.
1994 return operator[](defaultLayoutName());
1998 Layout const & DocumentClass::htmlTOCLayout() const
2000 if (html_toc_section_.empty())
2001 html_toc_section_ = getTOCLayout().name();
2002 return operator[](html_toc_section_);
2006 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
2007 string const & entry, bool const punct, string const & fallback) const
2009 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
2010 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
2011 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
2013 default_format += ".";
2015 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2016 if (itype == cite_formats_.end())
2017 return default_format;
2018 map<string, string>::const_iterator it = itype->second.find(entry);
2019 if (it == itype->second.end() && !fallback.empty())
2020 it = itype->second.find(fallback);
2021 if (it == itype->second.end())
2022 return default_format;
2024 return it->second + ".";
2029 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2030 string const & macro) const
2032 static string empty;
2033 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2034 if (itype == cite_macros_.end())
2036 map<string, string>::const_iterator it = itype->second.find(macro);
2037 if (it == itype->second.end())
2043 vector<string> const DocumentClass::citeCommands(
2044 CiteEngineType const & type) const
2046 vector<CitationStyle> const styles = citeStyles(type);
2047 vector<string> cmds;
2048 for (auto const & cs : styles)
2049 cmds.push_back(cs.name);
2055 vector<CitationStyle> const & DocumentClass::citeStyles(
2056 CiteEngineType const & type) const
2058 static vector<CitationStyle> empty;
2059 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2060 if (it == cite_styles_.end())
2066 /////////////////////////////////////////////////////////////////////////
2070 /////////////////////////////////////////////////////////////////////////
2072 ostream & operator<<(ostream & os, PageSides p)