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 = 84; // tcuvelier: DocBook*TagType.
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 docbookroot_("article"), docbookforceabstract_(false),
143 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
144 outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font),
145 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
146 min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
147 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,
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_ = "literate";
465 case TC_INPUT: // Include file
468 string const inc = lexrc.getString();
469 if (!path().empty() && (prefixIs(inc, "./") ||
470 prefixIs(inc, "../")))
471 tmp = fileSearch(path(), inc, "layout");
473 tmp = libFileSearch("layouts", inc,
477 lexrc.printError("Could not find input file: " + inc);
479 } else if (!read(tmp, MERGE)) {
480 lexrc.printError("Error reading input file: " + tmp.absFileName());
486 case TC_DEFAULTSTYLE:
488 docstring const name = from_utf8(subst(lexrc.getString(),
490 defaultlayout_ = name;
497 case TC_PROVIDESTYLE:
498 // if modifystyle is true, then we got here by falling through
499 // so we are not in an ProvideStyle block
505 lexrc.printError("No name given for style: `$$Token'.");
509 docstring const name = from_utf8(subst(lexrc.getString(),
512 string s = "Could not read name for style: `$$Token' "
513 + lexrc.getString() + " is probably not valid UTF-8!";
516 // Since we couldn't read the name, we just scan the rest
517 // of the style and discard it.
518 error = !readStyle(lexrc, lay);
522 bool const have_layout = hasLayout(name);
524 // If the layout already exists, then we want to add it to
525 // the existing layout, as long as we are not in an ProvideStyle
527 if (have_layout && !providestyle) {
528 Layout & lay = operator[](name);
529 error = !readStyle(lexrc, lay);
531 // If the layout does not exist, then we want to create a new
532 // one, but not if we are in a ModifyStyle block.
533 else if (!have_layout && !modifystyle) {
535 layout.setName(name);
536 error = !readStyle(lexrc, layout);
538 layoutlist_.push_back(layout);
540 if (defaultlayout_.empty()) {
541 // We do not have a default layout yet, so we choose
542 // the first layout we encounter.
543 defaultlayout_ = name;
546 // There are two ways to get here:
547 // (i) The layout exists but we are in an ProvideStyle block
548 // (ii) The layout doesn't exist, but we are in an ModifyStyle
550 // Either way, we just scan the rest and discard it
553 // signal to coverity that we do not care about the result
554 (void)readStyle(lexrc, lay);
561 docstring const style = from_utf8(subst(lexrc.getString(),
563 if (!deleteLayout(style))
564 lyxerr << "Cannot delete style `"
565 << to_utf8(style) << '\'' << endl;
569 case TC_NOINSETLAYOUT:
571 docstring const style = from_utf8(subst(lexrc.getString(),
573 if (!deleteInsetLayout(style))
574 LYXERR0("Style `" << style << "' cannot be removed\n"
575 "because it was not found!");
581 columns_ = lexrc.getInteger();
586 switch (lexrc.getInteger()) {
587 case 1: sides_ = OneSide; break;
588 case 2: sides_ = TwoSides; break;
590 lyxerr << "Impossible number of page"
591 " sides, setting to one."
601 pagesize_ = rtrim(lexrc.getString());
606 pagestyle_ = rtrim(lexrc.getString());
610 defaultfont_ = lyxRead(lexrc);
611 if (!defaultfont_.resolved()) {
612 lexrc.printError("Warning: defaultfont should "
613 "be fully instantiated!");
614 defaultfont_.realize(sane_font);
620 secnumdepth_ = lexrc.getInteger();
625 tocdepth_ = lexrc.getInteger();
628 // First step to support options
629 case TC_CLASSOPTIONS:
630 readClassOptions(lexrc);
634 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
637 case TC_HTMLPREAMBLE:
638 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
642 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
645 case TC_HTMLTOCSECTION:
646 html_toc_section_ = from_utf8(trim(lexrc.getString()));
649 case TC_ADDTOPREAMBLE:
650 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
653 case TC_ADDTOHTMLPREAMBLE:
654 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
657 case TC_ADDTOHTMLSTYLES:
658 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
663 string const feature = lexrc.getString();
665 if (lexrc.getInteger())
666 provides_.insert(feature);
668 provides_.erase(feature);
674 vector<string> const req
675 = getVectorFromString(lexrc.getString());
676 required_.insert(req.begin(), req.end());
682 string const pkg = lexrc.getString();
684 string const options = lexrc.getString();
685 package_options_[pkg] = options;
689 case TC_DEFAULTMODULE: {
691 string const module = lexrc.getString();
692 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
693 default_modules_.push_back(module);
697 case TC_PROVIDESMODULE: {
699 string const module = lexrc.getString();
700 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
701 provided_modules_.push_back(module);
705 case TC_EXCLUDESMODULE: {
707 string const module = lexrc.getString();
708 // modules already have their own way to exclude other modules
710 LYXERR0("ExcludesModule tag cannot be used in a module!");
713 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
714 excluded_modules_.push_back(module);
718 case TC_LEFTMARGIN: // left margin type
720 leftmargin_ = lexrc.getDocString();
723 case TC_RIGHTMARGIN: // right margin type
725 rightmargin_ = lexrc.getDocString();
728 case TC_INSETLAYOUT: {
730 lexrc.printError("No name given for InsetLayout: `$$Token'.");
734 docstring const name = subst(lexrc.getDocString(), '_', ' ');
735 bool const validating = (rt == VALIDATION);
737 string s = "Could not read name for InsetLayout: `$$Token' "
738 + lexrc.getString() + " is probably not valid UTF-8!";
741 // Since we couldn't read the name, we just scan the rest
742 // of the style and discard it.
743 il.read(lexrc, *this);
744 // Let's try to continue rather than abort, unless we're validating
745 // in which case we want to report the error
748 } else if (hasInsetLayout(name)) {
749 InsetLayout & il = insetlayoutlist_[name];
750 error = !il.read(lexrc, *this, validating);
754 error = !il.read(lexrc, *this, validating);
756 insetlayoutlist_[name] = il;
762 error = !readFloat(lexrc);
766 error = !readCiteEngine(lexrc, rt);
769 case TC_ADDTOCITEENGINE:
770 error = !readCiteEngine(lexrc, rt, true);
773 case TC_CITEENGINETYPE:
775 opt_enginetype_ = rtrim(lexrc.getString());
779 error = !readCiteFormat(lexrc, rt);
782 case TC_CITEFRAMEWORK:
784 citeframework_ = rtrim(lexrc.getString());
787 case TC_MAXCITENAMES:
789 maxcitenames_ = size_t(lexrc.getInteger());
792 case TC_DEFAULTBIBLIO:
794 vector<string> const dbs =
795 getVectorFromString(rtrim(lexrc.getString()), "|");
796 for (auto const & dbase : dbs) {
797 if (!contains(dbase, ':')) {
798 vector<string> const enginetypes =
799 getVectorFromString(opt_enginetype_, "|");
800 for (string const & s: enginetypes)
801 cite_default_biblio_style_[s] = dbase;
804 string const db = split(dbase, eng, ':');
805 cite_default_biblio_style_[eng] = db;
813 bibintoc_ = lexrc.getBool();
816 case TC_FULLAUTHORLIST:
818 cite_full_author_list_ &= lexrc.getBool();
823 docstring const cnt = lexrc.getDocString();
824 if (!counters_.remove(cnt))
825 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
834 docstring const name = lexrc.getDocString();
836 string s = "Could not read name for counter: `$$Token' "
837 + lexrc.getString() + " is probably not valid UTF-8!";
838 lexrc.printError(s.c_str());
840 // Since we couldn't read the name, we just scan the rest
844 error = !counters_.read(lexrc, name, !ifcounter);
847 lexrc.printError("No name given for style: `$$Token'.");
852 case TC_TITLELATEXTYPE:
853 readTitleType(lexrc);
856 case TC_TITLELATEXNAME:
858 titlename_ = lexrc.getString();
863 string const nofloat = lexrc.getString();
864 floatlist_.erase(nofloat);
868 case TC_OUTLINERNAME:
869 error = !readOutlinerName(lexrc);
874 tablestyle_ = rtrim(lexrc.getString());
879 docbookroot_ = lexrc.getString();
882 case TC_DOCBOOKFORCEABSTRACT:
884 docbookforceabstract_ = lexrc.getBool();
889 // at present, we abort if we encounter an error,
890 // so there is no point continuing.
897 if (defaultlayout_.empty()) {
898 LYXERR0("Error: Textclass '" << name_
899 << "' is missing a defaultstyle.");
903 // Try to erase "stdinsets" from the provides_ set.
905 // Provides stdinsets 1
906 // declaration simply tells us that the standard insets have been
907 // defined. (It's found in stdinsets.inc but could also be used in
908 // user-defined files.) There isn't really any such package. So we
909 // might as well go ahead and erase it.
910 // If we do not succeed, then it was not there, which means that
911 // the textclass did not provide the definitions of the standard
912 // insets. So we need to try to load them.
913 size_type const erased = provides_.erase("stdinsets");
915 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
918 frontend::Alert::warning(_("Missing File"),
919 _("Could not find stdinsets.inc! This may lead to data loss!"));
921 } else if (!read(tmp, MERGE)) {
922 frontend::Alert::warning(_("Corrupt File"),
923 _("Could not read stdinsets.inc! This may lead to data loss!"));
928 min_toclevel_ = Layout::NOT_IN_TOC;
929 max_toclevel_ = Layout::NOT_IN_TOC;
930 for (auto const & lay : *this) {
931 int const toclevel = lay.toclevel;
932 if (toclevel != Layout::NOT_IN_TOC) {
933 if (min_toclevel_ == Layout::NOT_IN_TOC)
934 min_toclevel_ = toclevel;
936 min_toclevel_ = min(min_toclevel_, toclevel);
937 max_toclevel_ = max(max_toclevel_, toclevel);
940 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
941 << ", maximum is " << max_toclevel_);
943 return (error ? ERROR : OK);
947 void TextClass::readTitleType(Lexer & lexrc)
949 LexerKeyword titleTypeTags[] = {
950 { "commandafter", TITLE_COMMAND_AFTER },
951 { "environment", TITLE_ENVIRONMENT }
954 PushPopHelper pph(lexrc, titleTypeTags);
956 int le = lexrc.lex();
958 case Lexer::LEX_UNDEF:
959 lexrc.printError("Unknown output type `$$Token'");
961 case TITLE_COMMAND_AFTER:
962 case TITLE_ENVIRONMENT:
963 titletype_ = static_cast<TitleLatexType>(le);
966 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
972 void TextClass::readOutputType(Lexer & lexrc)
974 LexerKeyword outputTypeTags[] = {
976 { "literate", LITERATE }
979 PushPopHelper pph(lexrc, outputTypeTags);
981 int le = lexrc.lex();
983 case Lexer::LEX_UNDEF:
984 lexrc.printError("Unknown output type `$$Token'");
988 outputType_ = static_cast<OutputType>(le);
991 LYXERR0("Unhandled value " << le);
997 void TextClass::readClassOptions(Lexer & lexrc)
1009 LexerKeyword classOptionsTags[] = {
1011 {"fontsize", CO_FONTSIZE },
1012 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1013 {"other", CO_OTHER },
1014 {"pagesize", CO_PAGESIZE },
1015 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1016 {"pagestyle", CO_PAGESTYLE }
1019 lexrc.pushTable(classOptionsTags);
1020 bool getout = false;
1021 while (!getout && lexrc.isOK()) {
1022 int le = lexrc.lex();
1024 case Lexer::LEX_UNDEF:
1025 lexrc.printError("Unknown ClassOption tag `$$Token'");
1033 opt_fontsize_ = rtrim(lexrc.getString());
1035 case CO_FONTSIZE_FORMAT:
1037 fontsize_format_ = rtrim(lexrc.getString());
1041 opt_pagesize_ = rtrim(lexrc.getString());
1043 case CO_PAGESIZE_FORMAT:
1045 pagesize_format_ = rtrim(lexrc.getString());
1049 opt_pagestyle_ = rtrim(lexrc.getString());
1053 if (options_.empty())
1054 options_ = lexrc.getString();
1056 options_ += ',' + lexrc.getString();
1067 vector<CitationStyle> const & TextClass::getCiteStyles(
1068 CiteEngineType const & type) const
1070 static vector<CitationStyle> empty;
1071 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1072 if (it == cite_styles_.end())
1078 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1080 int const type = readCiteEngineType(lexrc);
1081 bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1082 bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1083 bool defce = (type & ENGINE_TYPE_DEFAULT);
1085 if (rt == CITE_ENGINE) {
1086 // The cite engines are not supposed to overwrite
1087 // CiteStyle defined by the class or a module.
1089 authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1091 numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1093 defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1096 if (rt != CITE_ENGINE && !add) {
1097 // Reset if we defined CiteStyle
1098 // from the class or a module
1100 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1102 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1104 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1108 bool getout = false;
1109 while (!getout && lexrc.isOK()) {
1111 def = lexrc.getString();
1112 def = subst(def, " ", "");
1113 def = subst(def, "\t", "");
1114 if (compare_ascii_no_case(def, "end") == 0) {
1119 char ichar = def[0];
1122 if (isUpperCase(ichar)) {
1123 cs.forceUpperCase = true;
1124 def[0] = lowercase(ichar);
1127 /** For portability reasons (between different
1128 * cite engines such as natbib and biblatex),
1129 * we distinguish between:
1130 * 1. The LyX name as output in the LyX file
1131 * 2. Possible aliases that might fall back to
1132 * the given LyX name in the current engine
1133 * 3. The actual LaTeX command that is output
1134 * (2) and (3) are optional.
1135 * Also, the GUI string for the starred version can
1138 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1147 ScanMode mode = LyXName;
1148 ScanMode oldmode = LyXName;
1153 size_t const n = def.size();
1154 for (size_t i = 0; i != n; ++i) {
1158 else if (ichar == '=')
1160 else if (ichar == '<') {
1163 } else if (ichar == '>')
1165 else if (mode == LaTeXCmd)
1167 else if (mode == StarDesc)
1169 else if (ichar == '$')
1170 cs.hasQualifiedList = true;
1171 else if (ichar == '*')
1172 cs.hasStarredVersion = true;
1173 else if (ichar == '[' && cs.textAfter)
1174 cs.textBefore = true;
1175 else if (ichar == '[')
1176 cs.textAfter = true;
1177 else if (ichar != ']') {
1185 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1186 if (!alias.empty()) {
1187 vector<string> const aliases = getVectorFromString(alias);
1188 for (string const & s: aliases)
1189 cite_command_aliases_[s] = lyx_cmd;
1191 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1192 int size = int(stardesc.size());
1194 cs.stardesc = stardescs[0];
1196 cs.startooltip = stardescs[1];
1199 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1201 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1203 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1206 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1208 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1210 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1213 // If we do AddToCiteEngine, do not apply yet,
1214 // except if we have already a style to add something to
1215 bool apply_ay = !add;
1216 bool apply_num = !add;
1217 bool apply_def = !add;
1219 if (type & ENGINE_TYPE_AUTHORYEAR)
1220 apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1221 if (type & ENGINE_TYPE_NUMERICAL)
1222 apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1223 if (type & ENGINE_TYPE_DEFAULT)
1224 apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1227 // Add the styles from AddToCiteEngine to the class' styles
1228 // (but only if they are not yet defined)
1229 for (auto const & cis : class_cite_styles_) {
1230 // Only consider the current CiteEngineType
1231 if (!(type & cis.first))
1233 for (auto const & ciss : cis.second) {
1234 bool defined = false;
1235 // Check if the style "name" is already def'ed
1236 for (auto const & av : getCiteStyles(cis.first))
1237 if (av.name == ciss.name)
1240 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1241 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1242 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1243 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1244 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1245 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1249 if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1250 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1251 if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1252 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1253 if (type & ENGINE_TYPE_DEFAULT && apply_def)
1254 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1259 int TextClass::readCiteEngineType(Lexer & lexrc) const
1261 static_assert(ENGINE_TYPE_DEFAULT ==
1262 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1263 "Incorrect default engine type");
1264 if (!lexrc.next()) {
1265 lexrc.printError("No cite engine type given for token: `$$Token'.");
1266 return ENGINE_TYPE_DEFAULT;
1268 string const type = rtrim(lexrc.getString());
1269 if (compare_ascii_no_case(type, "authoryear") == 0)
1270 return ENGINE_TYPE_AUTHORYEAR;
1271 else if (compare_ascii_no_case(type, "numerical") == 0)
1272 return ENGINE_TYPE_NUMERICAL;
1273 else if (compare_ascii_no_case(type, "default") != 0) {
1274 string const s = "Unknown cite engine type `" + type
1275 + "' given for token: `$$Token',";
1276 lexrc.printError(s);
1278 return ENGINE_TYPE_DEFAULT;
1282 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1284 int const type = readCiteEngineType(lexrc);
1287 // Cite engine definitions do not overwrite existing
1288 // definitions from the class or a module
1289 bool const overwrite = rt != CITE_ENGINE;
1290 while (lexrc.isOK()) {
1292 etype = lexrc.getString();
1293 if (compare_ascii_no_case(etype, "end") == 0)
1298 definition = lexrc.getString();
1299 char initchar = etype[0];
1300 if (initchar == '#')
1302 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1303 bool defined = false;
1304 bool aydefined = false;
1305 bool numdefined = false;
1306 // Check if the macro is already def'ed
1307 for (auto const & cm : cite_macros_) {
1308 if (!(type & cm.first))
1310 if (cm.second.find(etype) != cm.second.end()) {
1311 if (type == cm.first)
1312 // defined as default or specific type
1314 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1315 // defined for author-year
1317 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1318 // defined for numerical
1322 if (!defined || overwrite) {
1323 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1324 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1325 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1326 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1327 if (type == ENGINE_TYPE_DEFAULT)
1328 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1331 bool defined = false;
1332 bool aydefined = false;
1333 bool numdefined = false;
1334 // Check if the format is already def'ed
1335 for (auto const & cm : cite_formats_) {
1336 if (!(type & cm.first))
1338 if (cm.second.find(etype) != cm.second.end()) {
1339 if (type == cm.first)
1340 // defined as default or specific type
1342 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1343 // defined for author-year
1345 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1346 // defined for numerical
1350 if (!defined || overwrite){
1351 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1352 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1353 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1354 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1355 if (type == ENGINE_TYPE_DEFAULT)
1356 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1364 bool TextClass::readFloat(Lexer & lexrc)
1384 FT_ALLOWED_PLACEMENT,
1391 LexerKeyword floatTags[] = {
1392 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1393 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1394 { "allowswide", FT_ALLOWS_WIDE },
1395 { "docbookattr", FT_DOCBOOKATTR },
1396 { "docbooktag", FT_DOCBOOKTAG },
1397 { "docbooktagtype", FT_DOCBOOKTAGTYPE },
1399 { "extension", FT_EXT },
1400 { "guiname", FT_NAME },
1401 { "htmlattr", FT_HTMLATTR },
1402 { "htmlstyle", FT_HTMLSTYLE },
1403 { "htmltag", FT_HTMLTAG },
1404 { "ispredefined", FT_PREDEFINED },
1405 { "listcommand", FT_LISTCOMMAND },
1406 { "listname", FT_LISTNAME },
1407 { "numberwithin", FT_WITHIN },
1408 { "placement", FT_PLACEMENT },
1409 { "refprefix", FT_REFPREFIX },
1410 { "requires", FT_REQUIRES },
1411 { "style", FT_STYLE },
1412 { "type", FT_TYPE },
1413 { "usesfloatpkg", FT_USESFLOAT }
1416 lexrc.pushTable(floatTags);
1420 docstring htmlstyle;
1424 string docbooktagtype;
1429 string allowed_placement = "!htbpH";
1435 bool usesfloat = true;
1436 bool ispredefined = false;
1437 bool allowswide = true;
1438 bool allowssideways = true;
1440 bool getout = false;
1441 while (!getout && lexrc.isOK()) {
1442 int le = lexrc.lex();
1444 case Lexer::LEX_UNDEF:
1445 lexrc.printError("Unknown float tag `$$Token'");
1453 type = lexrc.getString();
1454 if (floatlist_.typeExist(type)) {
1455 Floating const & fl = floatlist_.getType(type);
1456 placement = fl.placement();
1458 within = fl.within();
1461 listname = fl.listName();
1462 usesfloat = fl.usesFloatPkg();
1463 ispredefined = fl.isPredefined();
1464 listcommand = fl.listCommand();
1465 refprefix = fl.refPrefix();
1470 name = lexrc.getString();
1474 placement = lexrc.getString();
1476 case FT_ALLOWED_PLACEMENT:
1478 allowed_placement = lexrc.getString();
1482 ext = lexrc.getString();
1486 within = lexrc.getString();
1487 if (within == "none")
1492 style = lexrc.getString();
1494 case FT_LISTCOMMAND:
1496 listcommand = lexrc.getString();
1500 refprefix = lexrc.getString();
1504 listname = lexrc.getString();
1508 usesfloat = lexrc.getBool();
1512 required = lexrc.getString();
1516 ispredefined = lexrc.getBool();
1518 case FT_ALLOWS_SIDEWAYS:
1520 allowssideways = lexrc.getBool();
1522 case FT_ALLOWS_WIDE:
1524 allowswide = lexrc.getBool();
1528 htmlattr = lexrc.getString();
1532 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1536 htmltag = lexrc.getString();
1538 case FT_DOCBOOKATTR:
1540 docbookattr = lexrc.getString();
1544 docbooktag = lexrc.getString();
1546 case FT_DOCBOOKTAGTYPE:
1548 docbooktagtype = lexrc.getString();
1558 // Here we have a full float if getout == true
1560 if (!usesfloat && listcommand.empty()) {
1561 // if this float uses the same auxfile as an existing one,
1562 // there is no need for it to provide a list command.
1563 bool found_ext = false;
1564 for (auto const & f : floatlist_) {
1565 if (f.second.ext() == ext) {
1571 LYXERR0("The layout does not provide a list command " <<
1572 "for the float `" << type << "'. LyX will " <<
1573 "not be able to produce a float list.");
1575 Floating fl(type, placement, ext, within, style, name,
1576 listname, listcommand, refprefix, allowed_placement,
1577 htmltag, htmlattr, htmlstyle, docbookattr,
1578 docbooktagtype, required, usesfloat, ispredefined,
1579 allowswide, allowssideways);
1580 floatlist_.newFloat(fl);
1581 // each float has its own counter
1582 counters_.newCounter(from_ascii(type), from_ascii(within),
1583 docstring(), docstring(),
1584 bformat(_("%1$s (Float)"), _(name)));
1585 // also define sub-float counters
1586 docstring const subtype = "sub-" + from_ascii(type);
1587 counters_.newCounter(subtype, from_ascii(type),
1588 "\\alph{" + subtype + "}", docstring(),
1589 bformat(_("Sub-%1$s (Float)"), _(name)));
1595 bool TextClass::readOutlinerName(Lexer & lexrc)
1600 type = lexrc.getString();
1602 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1606 name = lexrc.getDocString();
1608 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1611 outliner_names_[type] = name;
1616 string const & TextClass::prerequisites(string const & sep) const
1618 if (contains(prerequisites_, ',')) {
1619 vector<string> const pres = getVectorFromString(prerequisites_);
1620 prerequisites_ = getStringFromVector(pres, sep);
1622 return prerequisites_;
1626 bool TextClass::hasLayout(docstring const & n) const
1628 docstring const name = n.empty() ? defaultLayoutName() : n;
1629 return getLayout(name) != nullptr;
1633 bool TextClass::hasInsetLayout(docstring const & n) const
1637 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1638 return it != insetlayoutlist_.end();
1642 Layout const & TextClass::operator[](docstring const & name) const
1644 LATTEST(!name.empty());
1646 Layout const * c = getLayout(name);
1648 LYXERR0("We failed to find the layout '" << name
1649 << "' in the layout list. You MUST investigate!");
1650 for (auto const & lay : *this)
1651 lyxerr << " " << to_utf8(lay.name()) << endl;
1653 // We require the name to exist
1654 static const Layout dummy;
1655 LASSERT(false, return dummy);
1662 Layout & TextClass::operator[](docstring const & name)
1664 LATTEST(!name.empty());
1665 // Safe to continue, given what we do below.
1667 Layout * c = getLayout(name);
1669 LYXERR0("We failed to find the layout '" << to_utf8(name)
1670 << "' in the layout list. You MUST investigate!");
1671 for (auto const & lay : *this)
1672 LYXERR0(" " << to_utf8(lay.name()));
1674 // we require the name to exist
1676 // we are here only in release mode
1677 layoutlist_.push_back(createBasicLayout(name, true));
1678 c = getLayout(name);
1685 bool TextClass::deleteLayout(docstring const & name)
1687 if (name == defaultLayoutName() || name == plainLayoutName())
1690 LayoutList::iterator it =
1691 remove_if(layoutlist_.begin(), layoutlist_.end(),
1692 [name](const Layout &c) { return c.name() == name; });
1694 LayoutList::iterator const end = layoutlist_.end();
1695 bool const ret = (it != end);
1696 layoutlist_.erase(it, end);
1701 bool TextClass::deleteInsetLayout(docstring const & name)
1703 return insetlayoutlist_.erase(name);
1707 // Load textclass info if not loaded yet
1708 bool TextClass::load(string const & path) const
1713 // Read style-file, provided path is searched before the system ones
1714 // If path is a file, it is loaded directly.
1715 FileName layout_file(path);
1716 if (!path.empty() && !layout_file.isReadableFile())
1717 layout_file = FileName(addName(path, name_ + ".layout"));
1718 if (layout_file.empty() || !layout_file.exists())
1719 layout_file = libFileSearch("layouts", name_, "layout");
1720 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1723 lyxerr << "Error reading `"
1724 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1725 << "'\n(Check `" << name_
1726 << "')\nCheck your installation and "
1727 "try Options/Reconfigure..."
1735 Layout const * TextClass::getLayout(docstring const & name) const
1737 LayoutList::const_iterator cit =
1738 find_if(begin(), end(),
1739 [name](const Layout &c) { return c.name() == name; });
1740 if (cit == layoutlist_.end())
1747 Layout * TextClass::getLayout(docstring const & name)
1749 LayoutList::iterator it =
1750 find_if(layoutlist_.begin(), layoutlist_.end(),
1751 [name](const Layout &c) { return c.name() == name; });
1752 if (it == layoutlist_.end())
1759 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1764 layoutlist_.push_back(createBasicLayout(n, true));
1769 string DocumentClass::forcedLayouts() const
1773 for (auto const & lay : *this) {
1774 if (lay.forcelocal > 0) {
1776 os << "Format " << LAYOUT_FORMAT << '\n';
1786 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1788 // FIXME The fix for the InsetLayout part of 4812 would be here:
1789 // Add the InsetLayout to the document class if it is not found.
1791 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1792 while (!n.empty()) {
1793 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1794 if (cit != cen && cit->first == n) {
1795 if (cit->second.obsoleted_by().empty())
1797 n = cit->second.obsoleted_by();
1798 return insetLayout(n);
1800 // If we have a generic prefix (e.g., "Note:"),
1801 // try if this one alone is found.
1802 size_t i = n.find(':');
1803 if (i == string::npos)
1807 // Layout "name" not found.
1808 return plainInsetLayout();
1812 InsetLayout const & DocumentClass::plainInsetLayout() {
1813 static const InsetLayout plain_insetlayout_;
1814 return plain_insetlayout_;
1818 docstring const & TextClass::defaultLayoutName() const
1820 return defaultlayout_;
1824 Layout const & TextClass::defaultLayout() const
1826 return operator[](defaultLayoutName());
1830 bool TextClass::isDefaultLayout(Layout const & layout) const
1832 return layout.name() == defaultLayoutName();
1836 bool TextClass::isPlainLayout(Layout const & layout) const
1838 return layout.name() == plainLayoutName();
1842 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1844 static Layout * defaultLayout = nullptr;
1846 if (defaultLayout) {
1847 defaultLayout->setUnknown(unknown);
1848 defaultLayout->setName(name);
1849 return *defaultLayout;
1852 static char const * s = "Margin Static\n"
1853 "LatexType Paragraph\n"
1856 "AlignPossible Left, Right, Center\n"
1857 "LabelType No_Label\n"
1859 istringstream ss(s);
1860 Lexer lex(textClassTags);
1862 defaultLayout = new Layout;
1863 defaultLayout->setUnknown(unknown);
1864 defaultLayout->setName(name);
1865 if (!readStyle(lex, *defaultLayout)) {
1866 // The only way this happens is because the hardcoded layout above
1870 return *defaultLayout;
1874 DocumentClassPtr getDocumentClass(
1875 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1876 string const & cengine, bool const clone)
1878 DocumentClassPtr doc_class =
1879 DocumentClassPtr(new DocumentClass(baseClass));
1880 for (auto const & mod : modlist) {
1881 LyXModule * lm = theModuleList[mod];
1883 docstring const msg =
1884 bformat(_("The module %1$s has been requested by\n"
1885 "this document but has not been found in the list of\n"
1886 "available modules. If you recently installed it, you\n"
1887 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1889 frontend::Alert::warning(_("Module not available"), msg);
1892 if (!lm->isAvailable() && !clone) {
1893 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1894 docstring const msg =
1895 bformat(_("The module %1$s requires a package that is not\n"
1896 "available in your LaTeX installation, or a converter that\n"
1897 "you have not installed. LaTeX output may not be possible.\n"
1898 "Missing prerequisites:\n"
1900 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1901 from_utf8(mod), prereqs);
1902 frontend::Alert::warning(_("Package not available"), msg, true);
1904 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1905 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1906 docstring const msg =
1907 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1908 frontend::Alert::warning(_("Read Error"), msg);
1912 if (cengine.empty())
1915 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1917 docstring const msg =
1918 bformat(_("The cite engine %1$s has been requested by\n"
1919 "this document but has not been found in the list of\n"
1920 "available engines. If you recently installed it, you\n"
1921 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1923 frontend::Alert::warning(_("Cite Engine not available"), msg);
1924 } else if (!ce->isAvailable() && !clone) {
1925 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1926 docstring const msg =
1927 bformat(_("The cite engine %1$s requires a package that is not\n"
1928 "available in your LaTeX installation, or a converter that\n"
1929 "you have not installed. LaTeX output may not be possible.\n"
1930 "Missing prerequisites:\n"
1932 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1933 from_utf8(cengine), prereqs);
1934 frontend::Alert::warning(_("Package not available"), msg, true);
1936 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1937 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1938 docstring const msg =
1939 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1940 frontend::Alert::warning(_("Read Error"), msg);
1948 /////////////////////////////////////////////////////////////////////////
1952 /////////////////////////////////////////////////////////////////////////
1954 DocumentClass::DocumentClass(LayoutFile const & tc)
1959 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1961 for (auto const & l : layoutlist_)
1962 if (l.latexname() == lay)
1968 bool DocumentClass::provides(string const & p) const
1970 return provides_.find(p) != provides_.end();
1974 bool DocumentClass::hasTocLevels() const
1976 return min_toclevel_ != Layout::NOT_IN_TOC;
1980 Layout const & DocumentClass::getTOCLayout() const
1982 // we're going to look for the layout with the minimum toclevel
1983 int minlevel = 1000;
1984 Layout const * lay = nullptr;
1985 for (auto const & l : *this) {
1986 int const level = l.toclevel;
1987 // we don't want Part or unnumbered sections
1988 if (level == Layout::NOT_IN_TOC || level < 0
1989 || level >= minlevel || l.counter.empty())
1996 // hmm. that is very odd, so we'll do our best.
1997 return operator[](defaultLayoutName());
2001 Layout const & DocumentClass::htmlTOCLayout() const
2003 if (html_toc_section_.empty())
2004 html_toc_section_ = getTOCLayout().name();
2005 return operator[](html_toc_section_);
2009 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
2010 string const & entry, bool const punct, string const & fallback) const
2012 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
2013 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
2014 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
2016 default_format += ".";
2018 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2019 if (itype == cite_formats_.end())
2020 return default_format;
2021 map<string, string>::const_iterator it = itype->second.find(entry);
2022 if (it == itype->second.end() && !fallback.empty())
2023 it = itype->second.find(fallback);
2024 if (it == itype->second.end())
2025 return default_format;
2027 return it->second + ".";
2032 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2033 string const & macro) const
2035 static string empty;
2036 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2037 if (itype == cite_macros_.end())
2039 map<string, string>::const_iterator it = itype->second.find(macro);
2040 if (it == itype->second.end())
2046 vector<string> const DocumentClass::citeCommands(
2047 CiteEngineType const & type) const
2049 vector<CitationStyle> const styles = citeStyles(type);
2050 vector<string> cmds;
2051 for (auto const & cs : styles)
2052 cmds.push_back(cs.name);
2058 vector<CitationStyle> const & DocumentClass::citeStyles(
2059 CiteEngineType const & type) const
2061 static vector<CitationStyle> empty;
2062 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2063 if (it == cite_styles_.end())
2069 /////////////////////////////////////////////////////////////////////////
2073 /////////////////////////////////////////////////////////////////////////
2075 ostream & operator<<(ostream & os, PageSides p)