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"
25 #include "ModuleList.h"
27 #include "frontends/alert.h"
29 #include "support/lassert.h"
30 #include "support/debug.h"
31 #include "support/FileName.h"
32 #include "support/filetools.h"
33 #include "support/gettext.h"
34 #include "support/Lexer.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 = 105; // spitz: ParskipHalf and ParskipFull class options
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"), parskip_full_(""), parskip_half_(""),
139 tablestyle_("default"), 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
174 TC_MODIFYINSETLAYOUT,
175 TC_PROVIDEINSETLAYOUT,
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 { "inputglobal", TC_INPUT_GLOBAL },
256 { "insetlayout", TC_INSETLAYOUT },
257 { "leftmargin", TC_LEFTMARGIN },
258 { "maxcitenames", TC_MAXCITENAMES },
259 { "modifyinsetlayout", TC_MODIFYINSETLAYOUT },
260 { "modifystyle", TC_MODIFYSTYLE },
261 { "nocounter", TC_NOCOUNTER },
262 { "nofloat", TC_NOFLOAT },
263 { "noinsetlayout", TC_NOINSETLAYOUT },
264 { "nostyle", TC_NOSTYLE },
265 { "outlinername", TC_OUTLINERNAME },
266 { "outputformat", TC_OUTPUTFORMAT },
267 { "outputtype", TC_OUTPUTTYPE },
268 { "packageoptions", TC_PKGOPTS },
269 { "pagesize", TC_PAGESIZE },
270 { "pagestyle", TC_PAGESTYLE },
271 { "preamble", TC_PREAMBLE },
272 { "provideinsetlayout", TC_PROVIDEINSETLAYOUT },
273 { "provides", TC_PROVIDES },
274 { "providesmodule", TC_PROVIDESMODULE },
275 { "providestyle", TC_PROVIDESTYLE },
276 { "requires", TC_REQUIRES },
277 { "rightmargin", TC_RIGHTMARGIN },
278 { "secnumdepth", TC_SECNUMDEPTH },
279 { "sides", TC_SIDES },
280 { "style", TC_STYLE },
281 { "tablestyle", TC_TABLESTYLE },
282 { "titlelatexname", TC_TITLELATEXNAME },
283 { "titlelatextype", TC_TITLELATEXTYPE },
284 { "tocdepth", TC_TOCDEPTH }
290 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
292 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
293 TempFile tmp("convertXXXXXX.layout");
294 FileName const tempfile = tmp.name();
295 bool success = layout2layout(filename, tempfile);
297 success = readWithoutConv(tempfile, rt) == OK;
302 std::string TextClass::convert(std::string const & str)
304 TempFile tmp1("localXXXXXX.layout");
305 FileName const fn = tmp1.name();
306 ofstream os(fn.toFilesystemEncoding().c_str());
309 TempFile tmp2("convert_localXXXXXX.layout");
310 FileName const tempfile = tmp2.name();
311 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
314 ifstream is(tempfile.toFilesystemEncoding().c_str());
326 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
328 if (!filename.isReadableFile()) {
329 lyxerr << "Cannot read layout file `" << filename << "'."
334 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
335 to_utf8(makeDisplayPath(filename.absFileName())));
337 // Define the plain layout used in table cells, ert, etc. Note that
338 // we do this before loading any layout file, so that classes can
339 // override features of this layout if they should choose to do so.
340 if (rt == BASECLASS && !hasLayout(plain_layout_))
341 layoutlist_.push_back(createBasicLayout(plain_layout_));
343 Lexer lexrc(textClassTags);
344 lexrc.setFile(filename);
345 ReturnValues retval = read(lexrc, rt);
347 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
348 to_utf8(makeDisplayPath(filename.absFileName())));
354 bool TextClass::read(FileName const & filename, ReadType rt)
356 ReturnValues const retval = readWithoutConv(filename, rt);
357 if (retval != FORMAT_MISMATCH)
360 bool const worx = convertLayoutFormat(filename, rt);
362 LYXERR0 ("Unable to convert " << filename <<
363 " to format " << LAYOUT_FORMAT);
368 TextClass::ReturnValues TextClass::validate(std::string const & str)
371 return tc.read(str, VALIDATION);
375 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
377 Lexer lexrc(textClassTags);
378 istringstream is(str);
380 ReturnValues retval = read(lexrc, rt);
382 if (retval != FORMAT_MISMATCH)
385 // write the layout string to a temporary file
386 TempFile tmp("TextClass_read");
387 FileName const tempfile = tmp.name();
388 ofstream os(tempfile.toFilesystemEncoding().c_str());
390 LYXERR0("Unable to create temporary file");
396 // now try to convert it to LAYOUT_FORMAT
397 if (!convertLayoutFormat(tempfile, rt)) {
398 LYXERR0("Unable to convert internal layout information to format "
407 // Reads a textclass structure from file.
408 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
413 // The first usable line should be
414 // Format LAYOUT_FORMAT
415 if (lexrc.lex() != TC_FORMAT || !lexrc.next()
416 || lexrc.getInteger() != LAYOUT_FORMAT)
417 return FORMAT_MISMATCH;
421 while (lexrc.isOK() && !error) {
422 int le = lexrc.lex();
425 case Lexer::LEX_FEOF:
428 case Lexer::LEX_UNDEF:
429 lexrc.printError("Unknown TextClass tag `$$Token'");
437 // used below to track whether we are in an IfStyle or IfCounter tag.
439 bool provide = false;
440 bool ifcounter = false;
441 bool only_global = false;
443 switch (static_cast<TextClassTags>(le)) {
447 lexrc.printError("Duplicate Format directive");
450 case TC_OUTPUTFORMAT:
452 outputFormat_ = lexrc.getString();
453 has_output_format_ = true;
458 readOutputType(lexrc);
459 switch(outputType_) {
461 outputFormat_ = "latex";
464 outputFormat_ = "literate";
469 case TC_INPUT_GLOBAL:
472 case TC_INPUT: // Include file
475 string const inc = lexrc.getString();
476 if (!path().empty() && (prefixIs(inc, "./") ||
477 prefixIs(inc, "../")))
478 tmp = fileSearch(path(), inc, "layout");
480 // InputGlobal only searches in the system and
481 // build directories. This allows to reuse and
482 // modify files in the user directory.
483 tmp = libFileSearch("layouts", inc,
484 "layout", must_exist, only_global);
488 lexrc.printError("Could not find input file: " + inc);
490 } else if (!read(tmp, MERGE)) {
491 lexrc.printError("Error reading input file: " + tmp.absFileName());
497 case TC_DEFAULTSTYLE:
499 docstring const name = from_utf8(subst(lexrc.getString(),
501 defaultlayout_ = name;
508 case TC_PROVIDESTYLE:
509 // if modifystyle is true, then we got here by falling through
510 // so we are not in an ProvideStyle block
516 lexrc.printError("No name given for style: `$$Token'.");
520 docstring const name = from_utf8(subst(lexrc.getString(),
523 string s = "Could not read name for style: `$$Token' "
524 + lexrc.getString() + " is probably not valid UTF-8!";
527 // Since we couldn't read the name, we just scan the rest
528 // of the style and discard it.
529 error = !readStyle(lexrc, lay, rt);
533 bool const have_layout = hasLayout(name);
535 // If the layout already exists, then we want to add it to
536 // the existing layout, as long as we are not in an ProvideStyle
538 if (have_layout && !provide) {
539 Layout & lay = operator[](name);
540 error = !readStyle(lexrc, lay, rt);
542 // If the layout does not exist, then we want to create a new
543 // one, but not if we are in a ModifyStyle block.
544 else if (!have_layout && !modify) {
546 layout.setName(name);
547 error = !readStyle(lexrc, layout, rt);
549 layoutlist_.push_back(layout);
551 if (defaultlayout_.empty()) {
552 // We do not have a default layout yet, so we choose
553 // the first layout we encounter.
554 defaultlayout_ = name;
557 // There are two ways to get here:
558 // (i) The layout exists but we are in an ProvideStyle block
559 // (ii) The layout doesn't exist, but we are in an ModifyStyle
561 // Either way, we just scan the rest and discard it
564 // signal to coverity that we do not care about the result
565 (void)readStyle(lexrc, lay, rt);
572 docstring const style = from_utf8(subst(lexrc.getString(),
574 if (!deleteLayout(style))
575 lyxerr << "Cannot delete style `"
576 << to_utf8(style) << '\'' << endl;
580 case TC_NOINSETLAYOUT:
582 docstring const style = from_utf8(subst(lexrc.getString(),
584 if (!deleteInsetLayout(style))
585 LYXERR0("Style `" << style << "' cannot be removed\n"
586 "because it was not found!");
592 columns_ = lexrc.getInteger();
597 switch (lexrc.getInteger()) {
598 case 1: sides_ = OneSide; break;
599 case 2: sides_ = TwoSides; break;
601 lyxerr << "Impossible number of page"
602 " sides, setting to one."
612 pagesize_ = rtrim(lexrc.getString());
617 pagestyle_ = rtrim(lexrc.getString());
621 defaultfont_ = lyxRead(lexrc);
622 if (!defaultfont_.resolved()) {
623 lexrc.printError("Warning: defaultfont should "
624 "be fully instantiated!");
625 defaultfont_.realize(sane_font);
631 secnumdepth_ = lexrc.getInteger();
636 tocdepth_ = lexrc.getInteger();
639 // First step to support options
640 case TC_CLASSOPTIONS:
641 readClassOptions(lexrc);
645 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
648 case TC_HTMLPREAMBLE:
649 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
653 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
656 case TC_HTMLTOCSECTION:
657 html_toc_section_ = from_utf8(trim(lexrc.getString()));
660 case TC_ADDTOPREAMBLE:
661 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
664 case TC_ADDTOHTMLPREAMBLE:
665 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
668 case TC_ADDTOHTMLSTYLES:
669 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
674 string const feature = lexrc.getString();
676 if (lexrc.getInteger())
677 provides_.insert(feature);
679 provides_.erase(feature);
685 vector<string> const req
686 = getVectorFromString(lexrc.getString());
687 required_.insert(req.begin(), req.end());
693 string const pkg = lexrc.getString();
695 string const options = lexrc.getString();
696 package_options_[pkg] = trim(options, "\"");
700 case TC_DEFAULTMODULE: {
702 string const module = lexrc.getString();
703 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
704 default_modules_.push_back(module);
708 case TC_PROVIDESMODULE: {
710 string const module = lexrc.getString();
711 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
712 provided_modules_.push_back(module);
716 case TC_EXCLUDESMODULE: {
718 string const module = lexrc.getString();
719 // modules already have their own way to exclude other modules
721 LYXERR0("ExcludesModule tag cannot be used in a module!");
724 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
725 excluded_modules_.push_back(module);
729 case TC_LEFTMARGIN: // left margin type
731 leftmargin_ = lexrc.getDocString();
734 case TC_RIGHTMARGIN: // right margin type
736 rightmargin_ = lexrc.getDocString();
739 case TC_MODIFYINSETLAYOUT:
742 case TC_PROVIDEINSETLAYOUT:
743 // if modifyinsetlayout is true, then we got here by falling through
744 // so we are not in an ProvideInsetLayout block
748 case TC_INSETLAYOUT: {
750 lexrc.printError("No name given for InsetLayout: `$$Token'.");
754 docstring const name = subst(lexrc.getDocString(), '_', ' ');
755 bool const validating = (rt == VALIDATION);
756 bool const have_layout = name.empty() ? false : hasInsetLayout(name);
758 string s = "Could not read name for InsetLayout: `$$Token' "
759 + lexrc.getString() + " is probably not valid UTF-8!";
762 // Since we couldn't read the name, we just scan the rest
763 // of the style and discard it.
764 il.read(lexrc, *this);
765 // Let's try to continue rather than abort, unless we're validating
766 // in which case we want to report the error
769 } else if (have_layout && !provide) {
770 InsetLayout & il = insetlayoutlist_[name];
771 error = !il.read(lexrc, *this, validating);
772 } else if (!modify && !have_layout) {
775 error = !il.read(lexrc, *this, validating);
777 insetlayoutlist_[name] = il;
780 // We just scan the rest of the style and discard it.
781 il.read(lexrc, *this);
787 error = !readFloat(lexrc);
791 error = !readCiteEngine(lexrc, rt);
794 case TC_ADDTOCITEENGINE:
795 error = !readCiteEngine(lexrc, rt, true);
798 case TC_CITEENGINETYPE:
800 opt_enginetype_ = rtrim(lexrc.getString());
804 error = !readCiteFormat(lexrc, rt);
807 case TC_CITEFRAMEWORK:
809 citeframework_ = rtrim(lexrc.getString());
812 case TC_MAXCITENAMES:
814 maxcitenames_ = size_t(lexrc.getInteger());
817 case TC_DEFAULTBIBLIO:
819 vector<string> const dbs =
820 getVectorFromString(rtrim(lexrc.getString()), "|");
821 for (auto const & dbase : dbs) {
822 if (!contains(dbase, ':')) {
823 vector<string> const enginetypes =
824 getVectorFromString(opt_enginetype_, "|");
825 for (string const & s: enginetypes)
826 cite_default_biblio_style_[s] = dbase;
829 string const db = split(dbase, eng, ':');
830 cite_default_biblio_style_[eng] = db;
838 bibintoc_ = lexrc.getBool();
841 case TC_FULLAUTHORLIST:
843 cite_full_author_list_ &= lexrc.getBool();
848 docstring const cnt = lexrc.getDocString();
849 if (!counters_.remove(cnt))
850 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
859 docstring const name = lexrc.getDocString();
861 string s = "Could not read name for counter: `$$Token' "
862 + lexrc.getString() + " is probably not valid UTF-8!";
863 lexrc.printError(s.c_str());
865 // Since we couldn't read the name, we just scan the rest
869 error = !counters_.read(lexrc, name, !ifcounter);
872 lexrc.printError("No name given for style: `$$Token'.");
877 case TC_TITLELATEXTYPE:
878 readTitleType(lexrc);
881 case TC_TITLELATEXNAME:
883 titlename_ = lexrc.getString();
888 string const nofloat = lexrc.getString();
889 floatlist_.erase(nofloat);
893 case TC_OUTLINERNAME:
894 error = !readOutlinerName(lexrc);
899 tablestyle_ = rtrim(lexrc.getString());
904 docbookroot_ = lexrc.getString();
907 case TC_DOCBOOKFORCEABSTRACT:
909 docbookforceabstract_ = lexrc.getBool();
914 // at present, we abort if we encounter an error,
915 // so there is no point continuing.
922 if (defaultlayout_.empty()) {
923 LYXERR0("Error: Textclass '" << name_
924 << "' is missing a defaultstyle.");
928 // Try to erase "stdinsets" from the provides_ set.
930 // Provides stdinsets 1
931 // declaration simply tells us that the standard insets have been
932 // defined. (It's found in stdinsets.inc but could also be used in
933 // user-defined files.) There isn't really any such package. So we
934 // might as well go ahead and erase it.
935 // If we do not succeed, then it was not there, which means that
936 // the textclass did not provide the definitions of the standard
937 // insets. So we need to try to load them.
938 size_type const erased = provides_.erase("stdinsets");
940 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
943 frontend::Alert::warning(_("Missing File"),
944 _("Could not find stdinsets.inc! This may lead to data loss!"));
946 } else if (!read(tmp, MERGE)) {
947 frontend::Alert::warning(_("Corrupt File"),
948 _("Could not read stdinsets.inc! This may lead to data loss!"));
953 min_toclevel_ = Layout::NOT_IN_TOC;
954 max_toclevel_ = Layout::NOT_IN_TOC;
955 for (auto const & lay : *this) {
956 int const toclevel = lay.toclevel;
957 if (toclevel != Layout::NOT_IN_TOC) {
958 if (min_toclevel_ == Layout::NOT_IN_TOC)
959 min_toclevel_ = toclevel;
961 min_toclevel_ = min(min_toclevel_, toclevel);
962 max_toclevel_ = max(max_toclevel_, toclevel);
965 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
966 << ", maximum is " << max_toclevel_);
968 return (error ? ERROR : OK);
972 void TextClass::readTitleType(Lexer & lexrc)
974 LexerKeyword titleTypeTags[] = {
975 { "commandafter", TITLE_COMMAND_AFTER },
976 { "environment", TITLE_ENVIRONMENT }
979 PushPopHelper pph(lexrc, titleTypeTags);
981 int le = lexrc.lex();
983 case Lexer::LEX_UNDEF:
984 lexrc.printError("Unknown output type `$$Token'");
986 case TITLE_COMMAND_AFTER:
987 case TITLE_ENVIRONMENT:
988 titletype_ = static_cast<TitleLatexType>(le);
991 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
997 void TextClass::readOutputType(Lexer & lexrc)
999 LexerKeyword outputTypeTags[] = {
1001 { "literate", LITERATE }
1004 PushPopHelper pph(lexrc, outputTypeTags);
1006 int le = lexrc.lex();
1008 case Lexer::LEX_UNDEF:
1009 lexrc.printError("Unknown output type `$$Token'");
1013 outputType_ = static_cast<OutputType>(le);
1016 LYXERR0("Unhandled value " << le);
1022 void TextClass::readClassOptions(Lexer & lexrc)
1036 LexerKeyword classOptionsTags[] = {
1038 {"fontsize", CO_FONTSIZE },
1039 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1040 {"other", CO_OTHER },
1041 {"pagesize", CO_PAGESIZE },
1042 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1043 {"pagestyle", CO_PAGESTYLE },
1044 {"parskipfull", CO_PARSKIP_FULL },
1045 {"parskiphalf", CO_PARSKIP_HALF }
1048 lexrc.pushTable(classOptionsTags);
1049 bool getout = false;
1050 while (!getout && lexrc.isOK()) {
1051 int le = lexrc.lex();
1053 case Lexer::LEX_UNDEF:
1054 lexrc.printError("Unknown ClassOption tag `$$Token'");
1062 opt_fontsize_ = rtrim(lexrc.getString());
1064 case CO_FONTSIZE_FORMAT:
1066 fontsize_format_ = rtrim(lexrc.getString());
1070 opt_pagesize_ = rtrim(lexrc.getString());
1072 case CO_PAGESIZE_FORMAT:
1074 pagesize_format_ = rtrim(lexrc.getString());
1078 opt_pagestyle_ = rtrim(lexrc.getString());
1080 case CO_PARSKIP_FULL:
1082 parskip_full_ = rtrim(lexrc.getString());
1084 case CO_PARSKIP_HALF:
1086 parskip_half_ = rtrim(lexrc.getString());
1090 if (options_.empty())
1091 options_ = lexrc.getString();
1093 options_ += ',' + lexrc.getString();
1104 vector<CitationStyle> const & TextClass::getCiteStyles(
1105 CiteEngineType const & type) const
1107 static vector<CitationStyle> empty;
1108 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1109 if (it == cite_styles_.end())
1115 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1117 int const type = readCiteEngineType(lexrc);
1118 bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1119 bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1120 bool defce = (type & ENGINE_TYPE_DEFAULT);
1122 if (rt == CITE_ENGINE) {
1123 // The cite engines are not supposed to overwrite
1124 // CiteStyle defined by the class or a module.
1126 authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1128 numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1130 defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1133 if (rt != CITE_ENGINE && !add) {
1134 // Reset if we defined CiteStyle
1135 // from the class or a module
1137 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1139 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1141 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1145 bool getout = false;
1146 while (!getout && lexrc.isOK()) {
1148 def = lexrc.getString();
1149 def = subst(def, " ", "");
1150 def = subst(def, "\t", "");
1151 if (compare_ascii_no_case(def, "end") == 0) {
1156 char ichar = def[0];
1159 if (isUpperCase(ichar)) {
1160 cs.forceUpperCase = true;
1161 def[0] = lowercase(ichar);
1164 /** For portability reasons (between different
1165 * cite engines such as natbib and biblatex),
1166 * we distinguish between:
1167 * 1. The LyX name as output in the LyX file
1168 * 2. Possible aliases that might fall back to
1169 * the given LyX name in the current engine
1170 * 3. The actual LaTeX command that is output
1171 * (2) and (3) are optional.
1172 * Also, the GUI string for the starred version can
1175 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1184 ScanMode mode = LyXName;
1185 ScanMode oldmode = LyXName;
1190 size_t const n = def.size();
1191 for (size_t i = 0; i != n; ++i) {
1195 else if (ichar == '=')
1197 else if (ichar == '<') {
1200 } else if (ichar == '>')
1202 else if (mode == LaTeXCmd)
1204 else if (mode == StarDesc)
1206 else if (ichar == '$')
1207 cs.hasQualifiedList = true;
1208 else if (ichar == '*')
1209 cs.hasStarredVersion = true;
1210 else if (ichar == '[' && cs.textAfter)
1211 cs.textBefore = true;
1212 else if (ichar == '[')
1213 cs.textAfter = true;
1214 else if (ichar != ']') {
1222 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1223 if (!alias.empty()) {
1224 vector<string> const aliases = getVectorFromString(alias);
1225 for (string const & s: aliases)
1226 cite_command_aliases_[s] = lyx_cmd;
1228 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1229 int size = int(stardesc.size());
1231 cs.stardesc = stardescs[0];
1233 cs.startooltip = stardescs[1];
1236 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1238 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1240 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1243 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1245 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1247 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1250 // If we do AddToCiteEngine, do not apply yet,
1251 // except if we have already a style to add something to
1252 bool apply_ay = !add;
1253 bool apply_num = !add;
1254 bool apply_def = !add;
1256 if (type & ENGINE_TYPE_AUTHORYEAR)
1257 apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1258 if (type & ENGINE_TYPE_NUMERICAL)
1259 apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1260 if (type & ENGINE_TYPE_DEFAULT)
1261 apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1264 // Add the styles from AddToCiteEngine to the class' styles
1265 // (but only if they are not yet defined)
1266 for (auto const & cis : class_cite_styles_) {
1267 // Only consider the current CiteEngineType
1268 if (!(type & cis.first))
1270 for (auto const & ciss : cis.second) {
1271 bool defined = false;
1272 // Check if the style "name" is already def'ed
1273 for (auto const & av : getCiteStyles(cis.first))
1274 if (av.name == ciss.name)
1277 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1278 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1279 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1280 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1281 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1282 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1286 if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1287 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1288 if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1289 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1290 if (type & ENGINE_TYPE_DEFAULT && apply_def)
1291 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1296 int TextClass::readCiteEngineType(Lexer & lexrc) const
1298 static_assert(ENGINE_TYPE_DEFAULT ==
1299 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1300 "Incorrect default engine type");
1301 if (!lexrc.next()) {
1302 lexrc.printError("No cite engine type given for token: `$$Token'.");
1303 return ENGINE_TYPE_DEFAULT;
1305 string const type = rtrim(lexrc.getString());
1306 if (compare_ascii_no_case(type, "authoryear") == 0)
1307 return ENGINE_TYPE_AUTHORYEAR;
1308 else if (compare_ascii_no_case(type, "numerical") == 0)
1309 return ENGINE_TYPE_NUMERICAL;
1310 else if (compare_ascii_no_case(type, "default") != 0) {
1311 string const s = "Unknown cite engine type `" + type
1312 + "' given for token: `$$Token',";
1313 lexrc.printError(s);
1315 return ENGINE_TYPE_DEFAULT;
1319 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1321 int const type = readCiteEngineType(lexrc);
1324 // Cite engine definitions do not overwrite existing
1325 // definitions from the class or a module
1326 bool const overwrite = rt != CITE_ENGINE;
1327 while (lexrc.isOK()) {
1329 etype = lexrc.getString();
1330 if (compare_ascii_no_case(etype, "end") == 0)
1335 definition = lexrc.getString();
1336 char initchar = etype[0];
1337 if (initchar == '#')
1339 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1340 bool defined = false;
1341 bool aydefined = false;
1342 bool numdefined = false;
1343 // Check if the macro is already def'ed
1344 for (auto const & cm : cite_macros_) {
1345 if (!(type & cm.first))
1347 if (cm.second.find(etype) != cm.second.end()) {
1348 if (type == cm.first)
1349 // defined as default or specific type
1351 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1352 // defined for author-year
1354 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1355 // defined for numerical
1359 if (!defined || overwrite) {
1360 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1361 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1362 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1363 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1364 if (type == ENGINE_TYPE_DEFAULT)
1365 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1368 bool defined = false;
1369 bool aydefined = false;
1370 bool numdefined = false;
1371 // Check if the format is already def'ed
1372 for (auto const & cm : cite_formats_) {
1373 if (!(type & cm.first))
1375 if (cm.second.find(etype) != cm.second.end()) {
1376 if (type == cm.first)
1377 // defined as default or specific type
1379 if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1380 // defined for author-year
1382 else if (cm.first == ENGINE_TYPE_NUMERICAL)
1383 // defined for numerical
1387 if (!defined || overwrite){
1388 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1389 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1390 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1391 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1392 if (type == ENGINE_TYPE_DEFAULT)
1393 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1401 bool TextClass::readFloat(Lexer & lexrc)
1417 FT_DOCBOOKFLOATTYPE,
1423 FT_ALLOWED_PLACEMENT,
1431 LexerKeyword floatTags[] = {
1432 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1433 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1434 { "allowswide", FT_ALLOWS_WIDE },
1435 { "docbookattr", FT_DOCBOOKATTR },
1436 { "docbookcaption", FT_DOCBOOKCAPTION },
1437 { "docbookfloattype", FT_DOCBOOKFLOATTYPE },
1438 { "docbooktag", FT_DOCBOOKTAG },
1439 { "docbooktagtype", FT_DOCBOOKTAGTYPE },
1441 { "extension", FT_EXT },
1442 { "guiname", FT_NAME },
1443 { "htmlattr", FT_HTMLATTR },
1444 { "htmlstyle", FT_HTMLSTYLE },
1445 { "htmltag", FT_HTMLTAG },
1446 { "ispredefined", FT_PREDEFINED },
1447 { "listcommand", FT_LISTCOMMAND },
1448 { "listname", FT_LISTNAME },
1449 { "numberwithin", FT_WITHIN },
1450 { "placement", FT_PLACEMENT },
1451 { "prettyformat", FT_PRETTYFORMAT },
1452 { "refprefix", FT_REFPREFIX },
1453 { "requires", FT_REQUIRES },
1454 { "style", FT_STYLE },
1455 { "type", FT_TYPE },
1456 { "usesfloatpkg", FT_USESFLOAT }
1459 lexrc.pushTable(floatTags);
1463 docstring htmlstyle;
1466 string docbookcaption;
1468 string docbooktagtype;
1469 string docbookfloattype;
1474 string allowed_placement = "!htbpH";
1480 docstring prettyformat;
1481 bool usesfloat = true;
1482 bool ispredefined = false;
1483 bool allowswide = true;
1484 bool allowssideways = true;
1486 bool getout = false;
1487 while (!getout && lexrc.isOK()) {
1488 int le = lexrc.lex();
1490 case Lexer::LEX_UNDEF:
1491 lexrc.printError("Unknown float tag `$$Token'");
1499 type = lexrc.getString();
1500 if (floatlist_.typeExist(type)) {
1501 Floating const & fl = floatlist_.getType(type);
1502 placement = fl.placement();
1504 within = fl.within();
1507 listname = fl.listName();
1508 usesfloat = fl.usesFloatPkg();
1509 ispredefined = fl.isPredefined();
1510 listcommand = fl.listCommand();
1511 refprefix = fl.refPrefix();
1516 name = lexrc.getString();
1520 placement = lexrc.getString();
1522 case FT_ALLOWED_PLACEMENT:
1524 allowed_placement = lexrc.getString();
1528 ext = lexrc.getString();
1532 within = lexrc.getString();
1533 if (within == "none")
1538 style = lexrc.getString();
1540 case FT_LISTCOMMAND:
1542 listcommand = lexrc.getString();
1546 refprefix = lexrc.getString();
1550 listname = lexrc.getString();
1554 usesfloat = lexrc.getBool();
1558 required = lexrc.getString();
1562 ispredefined = lexrc.getBool();
1564 case FT_ALLOWS_SIDEWAYS:
1566 allowssideways = lexrc.getBool();
1568 case FT_ALLOWS_WIDE:
1570 allowswide = lexrc.getBool();
1574 htmlattr = lexrc.getString();
1578 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1582 htmltag = lexrc.getString();
1584 case FT_PRETTYFORMAT:
1586 prettyformat = lexrc.getDocString();
1588 case FT_DOCBOOKATTR:
1590 docbookattr = lexrc.getString();
1592 case FT_DOCBOOKCAPTION:
1594 docbookcaption = lexrc.getString();
1598 docbooktag = lexrc.getString();
1600 case FT_DOCBOOKTAGTYPE:
1602 docbooktagtype = lexrc.getString();
1604 case FT_DOCBOOKFLOATTYPE:
1606 docbookfloattype = lexrc.getString();
1612 LYXERR0("Unhandled value " << le << " in TextClass::readFloat.");
1619 // Here we have a full float if getout == true
1621 if (!usesfloat && listcommand.empty()) {
1622 // if this float uses the same auxfile as an existing one,
1623 // there is no need for it to provide a list command.
1624 bool found_ext = false;
1625 for (auto const & f : floatlist_) {
1626 if (f.second.ext() == ext) {
1632 LYXERR0("The layout does not provide a list command " <<
1633 "for the float `" << type << "'. LyX will " <<
1634 "not be able to produce a float list.");
1636 Floating fl(type, placement, ext, within, style, name,
1637 listname, listcommand, refprefix, allowed_placement,
1638 htmltag, htmlattr, htmlstyle, docbooktag, docbookattr,
1639 docbooktagtype, docbookfloattype, docbookcaption,
1640 required, usesfloat, ispredefined,
1641 allowswide, allowssideways);
1642 floatlist_.newFloat(fl);
1643 // each float has its own counter
1644 counters_.newCounter(from_ascii(type), from_ascii(within),
1645 docstring(), docstring(),
1646 prettyformat.empty() ? bformat(_("%1$s ##"), _(name)) : prettyformat,
1647 bformat(_("%1$s (Float)"), _(name)));
1648 // also define sub-float counters
1649 docstring const subtype = "sub-" + from_ascii(type);
1650 counters_.newCounter(subtype, from_ascii(type),
1651 "\\alph{" + subtype + "}", docstring(),
1652 prettyformat.empty() ? bformat(_("Sub-%1$s ##"), _(name)) : prettyformat,
1653 bformat(_("Sub-%1$s (Float)"), _(name)));
1659 bool TextClass::readOutlinerName(Lexer & lexrc)
1664 type = lexrc.getString();
1666 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1670 name = lexrc.getDocString();
1672 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1675 outliner_names_[type] = name;
1680 string const & TextClass::prerequisites(string const & sep) const
1682 if (contains(prerequisites_, ',')) {
1683 vector<string> const pres = getVectorFromString(prerequisites_);
1684 prerequisites_ = getStringFromVector(pres, sep);
1686 return prerequisites_;
1690 bool TextClass::hasLayout(docstring const & n) const
1692 docstring const name = n.empty() ? defaultLayoutName() : n;
1693 return getLayout(name) != nullptr;
1697 bool TextClass::hasInsetLayout(docstring const & n) const
1701 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1702 return it != insetlayoutlist_.end();
1706 Layout const & TextClass::operator[](docstring const & name) const
1708 LATTEST(!name.empty());
1710 Layout const * c = getLayout(name);
1712 LYXERR0("We failed to find the layout '" << name
1713 << "' in the layout list. You MUST investigate!");
1714 for (auto const & lay : *this)
1715 lyxerr << " " << to_utf8(lay.name()) << endl;
1717 // We require the name to exist
1718 static const Layout dummy;
1719 LASSERT(false, return dummy);
1726 Layout & TextClass::operator[](docstring const & name)
1728 LATTEST(!name.empty());
1729 // Safe to continue, given what we do below.
1731 Layout * c = getLayout(name);
1733 LYXERR0("We failed to find the layout '" << to_utf8(name)
1734 << "' in the layout list. You MUST investigate!");
1735 for (auto const & lay : *this)
1736 LYXERR0(" " << to_utf8(lay.name()));
1738 // we require the name to exist
1740 // we are here only in release mode
1741 layoutlist_.push_back(createBasicLayout(name, true));
1742 c = getLayout(name);
1749 bool TextClass::deleteLayout(docstring const & name)
1751 if (name == defaultLayoutName() || name == plainLayoutName())
1754 LayoutList::iterator it =
1755 remove_if(layoutlist_.begin(), layoutlist_.end(),
1756 [name](const Layout &c) { return c.name() == name; });
1758 LayoutList::iterator const end = layoutlist_.end();
1759 bool const ret = (it != end);
1760 layoutlist_.erase(it, end);
1765 bool TextClass::deleteInsetLayout(docstring const & name)
1767 return insetlayoutlist_.erase(name);
1771 // Load textclass info if not loaded yet
1772 bool TextClass::load(string const & path) const
1777 // Read style-file, provided path is searched before the system ones
1778 // If path is a file, it is loaded directly.
1779 FileName layout_file(path);
1780 if (!path.empty() && !layout_file.isReadableFile())
1781 layout_file = FileName(addName(path, name_ + ".layout"));
1782 if (layout_file.empty() || !layout_file.exists())
1783 layout_file = libFileSearch("layouts", name_, "layout");
1784 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1787 lyxerr << "Error reading `"
1788 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1789 << "'\n(Check `" << name_
1790 << "')\nCheck your installation and "
1791 "try Tools/Reconfigure..."
1799 Layout const * TextClass::getLayout(docstring const & name) const
1801 LayoutList::const_iterator cit =
1802 find_if(begin(), end(),
1803 [name](const Layout &c) { return c.name() == name; });
1804 if (cit == layoutlist_.end())
1811 Layout * TextClass::getLayout(docstring const & name)
1813 LayoutList::iterator it =
1814 find_if(layoutlist_.begin(), layoutlist_.end(),
1815 [name](const Layout &c) { return c.name() == name; });
1816 if (it == layoutlist_.end())
1823 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1828 layoutlist_.push_back(createBasicLayout(n, true));
1833 string DocumentClass::forcedLayouts() const
1837 for (auto const & lay : *this) {
1838 if (lay.forcelocal > 0) {
1840 os << "Format " << LAYOUT_FORMAT << '\n';
1850 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1852 // FIXME The fix for the InsetLayout part of 4812 would be here:
1853 // Add the InsetLayout to the document class if it is not found.
1855 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1856 while (!n.empty()) {
1857 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1858 if (cit != cen && cit->first == n) {
1859 if (cit->second.obsoleted_by().empty())
1861 n = cit->second.obsoleted_by();
1862 return insetLayout(n);
1864 // If we have a generic prefix (e.g., "Note:"),
1865 // try if this one alone is found.
1866 size_t i = n.find(':');
1867 if (i == string::npos)
1871 // Layout "name" not found.
1872 return plainInsetLayout();
1876 InsetLayout const & DocumentClass::plainInsetLayout() {
1877 static const InsetLayout plain_insetlayout_;
1878 return plain_insetlayout_;
1882 docstring const & TextClass::defaultLayoutName() const
1884 return defaultlayout_;
1888 Layout const & TextClass::defaultLayout() const
1890 return operator[](defaultLayoutName());
1894 bool TextClass::isDefaultLayout(Layout const & layout) const
1896 return layout.name() == defaultLayoutName();
1900 bool TextClass::isPlainLayout(Layout const & layout) const
1902 return layout.name() == plainLayoutName();
1906 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1908 static Layout * defaultLayout = nullptr;
1910 if (defaultLayout) {
1911 defaultLayout->setUnknown(unknown);
1912 defaultLayout->setName(name);
1913 return *defaultLayout;
1916 static char const * s = "Margin Static\n"
1917 "LatexType Paragraph\n"
1920 "AlignPossible Left, Right, Center\n"
1921 "LabelType No_Label\n"
1923 istringstream ss(s);
1924 Lexer lex(textClassTags);
1926 defaultLayout = new Layout;
1927 defaultLayout->setUnknown(unknown);
1928 defaultLayout->setName(name);
1929 if (!readStyle(lex, *defaultLayout, BASECLASS)) {
1930 // The only way this happens is because the hardcoded layout above
1934 return *defaultLayout;
1938 DocumentClassPtr getDocumentClass(LayoutFile const & baseClass, LayoutModuleList const & modlist,
1939 string const & cengine, bool clone, bool internal)
1941 bool const show_warnings = !clone && !internal;
1942 DocumentClassPtr doc_class =
1943 DocumentClassPtr(new DocumentClass(baseClass));
1944 for (auto const & mod : modlist) {
1945 LyXModule * lm = theModuleList[mod];
1947 if (show_warnings) {
1948 docstring const msg =
1949 bformat(_("The module %1$s has been requested by\n"
1950 "this document but has not been found in the list of\n"
1951 "available modules. If you recently installed it, you\n"
1952 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1953 frontend::Alert::warning(_("Module not available"), msg);
1957 if (!lm->isAvailable() && show_warnings) {
1958 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1959 docstring const msg =
1960 bformat(_("The module %1$s requires a package that is not\n"
1961 "available in your LaTeX installation, or a converter that\n"
1962 "you have not installed. LaTeX output may not be possible.\n"
1963 "Missing prerequisites:\n"
1965 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1966 from_utf8(mod), prereqs);
1967 frontend::Alert::warning(_("Package not available"), msg, true);
1969 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1970 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1971 docstring const msg =
1972 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1973 frontend::Alert::warning(_("Read Error"), msg);
1977 if (cengine.empty())
1980 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1982 if (show_warnings) {
1983 docstring const msg =
1984 bformat(_("The cite engine %1$s has been requested by\n"
1985 "this document but has not been found in the list of\n"
1986 "available engines. If you recently installed it, you\n"
1987 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1988 frontend::Alert::warning(_("Cite Engine not available"), msg);
1990 } else if (!ce->isAvailable() && show_warnings) {
1991 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1992 docstring const msg =
1993 bformat(_("The cite engine %1$s requires a package that is not\n"
1994 "available in your LaTeX installation, or a converter that\n"
1995 "you have not installed. LaTeX output may not be possible.\n"
1996 "Missing prerequisites:\n"
1998 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1999 from_utf8(cengine), prereqs);
2000 frontend::Alert::warning(_("Package not available"), msg, true);
2002 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
2003 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
2004 docstring const msg =
2005 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
2006 frontend::Alert::warning(_("Read Error"), msg);
2014 /////////////////////////////////////////////////////////////////////////
2018 /////////////////////////////////////////////////////////////////////////
2020 DocumentClass::DocumentClass(LayoutFile const & tc)
2025 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
2027 for (auto const & l : layoutlist_)
2028 if (l.latexname() == lay)
2034 bool DocumentClass::provides(string const & p) const
2036 return provides_.find(p) != provides_.end();
2040 bool DocumentClass::hasTocLevels() const
2042 return min_toclevel_ != Layout::NOT_IN_TOC;
2046 Layout const & DocumentClass::getTOCLayout() const
2048 // we're going to look for the layout with the minimum toclevel
2049 int minlevel = 1000;
2050 Layout const * lay = nullptr;
2051 for (auto const & l : *this) {
2052 int const level = l.toclevel;
2053 // we don't want Part or unnumbered sections
2054 if (level == Layout::NOT_IN_TOC || level < 0
2055 || level >= minlevel || l.counter.empty())
2062 // hmm. that is very odd, so we'll do our best.
2063 return operator[](defaultLayoutName());
2067 Layout const & DocumentClass::htmlTOCLayout() const
2069 if (html_toc_section_.empty())
2070 html_toc_section_ = getTOCLayout().name();
2071 return operator[](html_toc_section_);
2075 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
2076 string const & entry, bool const punct, string const & fallback) const
2078 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
2079 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
2080 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
2082 default_format += ".";
2084 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2085 if (itype == cite_formats_.end())
2086 return default_format;
2087 map<string, string>::const_iterator it = itype->second.find(entry);
2088 if (it == itype->second.end() && !fallback.empty())
2089 it = itype->second.find(fallback);
2090 if (it == itype->second.end())
2091 return default_format;
2093 return it->second + ".";
2098 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2099 string const & macro) const
2101 static string empty;
2102 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2103 if (itype == cite_macros_.end())
2105 map<string, string>::const_iterator it = itype->second.find(macro);
2106 if (it == itype->second.end())
2112 vector<string> const DocumentClass::citeCommands(
2113 CiteEngineType const & type) const
2115 vector<CitationStyle> const styles = citeStyles(type);
2116 vector<string> cmds;
2117 cmds.reserve(styles.size());
2118 for (auto const & cs : styles)
2119 cmds.push_back(cs.name);
2125 vector<CitationStyle> const & DocumentClass::citeStyles(
2126 CiteEngineType const & type) const
2128 static vector<CitationStyle> empty;
2129 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2130 if (it == cite_styles_.end())
2136 /////////////////////////////////////////////////////////////////////////
2140 /////////////////////////////////////////////////////////////////////////
2142 ostream & operator<<(ostream & os, PageSides p)