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 = 68; //spitz: New layout tag AddToCiteEngine
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 class LayoutNamesEqual : public unary_function<Layout, bool> {
77 LayoutNamesEqual(docstring const & name)
80 bool operator()(Layout const & c) const
82 return c.name() == name_;
89 bool layout2layout(FileName const & filename, FileName const & tempfile,
90 int const format = LAYOUT_FORMAT)
92 FileName const script = libFileSearch("scripts", "layout2layout.py");
94 LYXERR0("Could not find layout conversion "
95 "script layout2layout.py.");
99 ostringstream command;
100 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
102 << ' ' << quoteName(filename.toFilesystemEncoding())
103 << ' ' << quoteName(tempfile.toFilesystemEncoding());
104 string const command_str = command.str();
106 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
108 cmd_ret const ret = runCommand(command_str);
109 if (ret.first != 0) {
110 if (format == LAYOUT_FORMAT)
111 LYXERR0("Conversion of layout with layout2layout.py has failed.");
118 string translateReadType(TextClass::ReadType rt)
121 case TextClass::BASECLASS:
123 case TextClass::MERGE:
125 case TextClass::MODULE:
126 return "module file";
127 case TextClass::CITE_ENGINE:
128 return "cite engine";
129 case TextClass::VALIDATION:
139 // This string should not be translated here,
140 // because it is a layout identifier.
141 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
144 /////////////////////////////////////////////////////////////////////////
148 /////////////////////////////////////////////////////////////////////////
150 TextClass::TextClass()
151 : loaded_(false), tex_class_avail_(false),
152 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
153 opt_pagestyle_("empty|plain|headings|fancy"), pagestyle_("default"),
154 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3),
155 outputType_(LATEX), outputFormat_("latex"), has_output_format_(false),
156 defaultfont_(sane_font),
157 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
158 min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
159 cite_full_author_list_(true)
164 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
166 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
167 if (!lay.read(lexrc, *this)) {
168 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
172 lay.resfont = lay.font;
173 lay.resfont.realize(defaultfont_);
174 lay.reslabelfont = lay.labelfont;
175 lay.reslabelfont.realize(defaultfont_);
176 return true; // no errors
215 TC_ADDTOHTMLPREAMBLE,
235 LexerKeyword textClassTags[] = {
236 { "addtociteengine", TC_ADDTOCITEENGINE },
237 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
238 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
239 { "addtopreamble", TC_ADDTOPREAMBLE },
240 { "citeengine", TC_CITEENGINE },
241 { "citeenginetype", TC_CITEENGINETYPE },
242 { "citeformat", TC_CITEFORMAT },
243 { "citeframework", TC_CITEFRAMEWORK },
244 { "classoptions", TC_CLASSOPTIONS },
245 { "columns", TC_COLUMNS },
246 { "counter", TC_COUNTER },
247 { "defaultbiblio", TC_DEFAULTBIBLIO },
248 { "defaultfont", TC_DEFAULTFONT },
249 { "defaultmodule", TC_DEFAULTMODULE },
250 { "defaultstyle", TC_DEFAULTSTYLE },
251 { "excludesmodule", TC_EXCLUDESMODULE },
252 { "float", TC_FLOAT },
253 { "format", TC_FORMAT },
254 { "fullauthorlist", TC_FULLAUTHORLIST },
255 { "htmlpreamble", TC_HTMLPREAMBLE },
256 { "htmlstyles", TC_HTMLSTYLES },
257 { "htmltocsection", TC_HTMLTOCSECTION },
258 { "ifcounter", TC_IFCOUNTER },
259 { "input", TC_INPUT },
260 { "insetlayout", TC_INSETLAYOUT },
261 { "leftmargin", TC_LEFTMARGIN },
262 { "maxcitenames", TC_MAXCITENAMES },
263 { "modifystyle", TC_MODIFYSTYLE },
264 { "nocounter", TC_NOCOUNTER },
265 { "nofloat", TC_NOFLOAT },
266 { "noinsetlayout", TC_NOINSETLAYOUT },
267 { "nostyle", TC_NOSTYLE },
268 { "outlinername", TC_OUTLINERNAME },
269 { "outputformat", TC_OUTPUTFORMAT },
270 { "outputtype", TC_OUTPUTTYPE },
271 { "packageoptions", TC_PKGOPTS },
272 { "pagestyle", TC_PAGESTYLE },
273 { "preamble", TC_PREAMBLE },
274 { "provides", TC_PROVIDES },
275 { "providesmodule", TC_PROVIDESMODULE },
276 { "providestyle", TC_PROVIDESTYLE },
277 { "requires", TC_REQUIRES },
278 { "rightmargin", TC_RIGHTMARGIN },
279 { "secnumdepth", TC_SECNUMDEPTH },
280 { "sides", TC_SIDES },
281 { "style", TC_STYLE },
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.
438 bool modifystyle = false;
439 bool providestyle = false;
440 bool ifcounter = false;
442 switch (static_cast<TextClassTags>(le)) {
446 lexrc.printError("Duplicate Format directive");
449 case TC_OUTPUTFORMAT:
451 outputFormat_ = lexrc.getString();
452 has_output_format_ = true;
457 readOutputType(lexrc);
458 switch(outputType_) {
460 outputFormat_ = "latex";
463 outputFormat_ = "docbook";
466 outputFormat_ = "literate";
471 case TC_INPUT: // Include file
474 string const inc = lexrc.getString();
475 if (!path().empty() && (prefixIs(inc, "./") ||
476 prefixIs(inc, "../")))
477 tmp = fileSearch(path(), inc, "layout");
479 tmp = libFileSearch("layouts", inc,
483 lexrc.printError("Could not find input file: " + inc);
485 } else if (!read(tmp, MERGE)) {
486 lexrc.printError("Error reading input file: " + tmp.absFileName());
492 case TC_DEFAULTSTYLE:
494 docstring const name = from_utf8(subst(lexrc.getString(),
496 defaultlayout_ = name;
503 case TC_PROVIDESTYLE:
504 // if modifystyle is true, then we got here by falling through
505 // so we are not in an ProvideStyle block
511 lexrc.printError("No name given for style: `$$Token'.");
515 docstring const name = from_utf8(subst(lexrc.getString(),
518 string s = "Could not read name for style: `$$Token' "
519 + lexrc.getString() + " is probably not valid UTF-8!";
522 // Since we couldn't read the name, we just scan the rest
523 // of the style and discard it.
524 error = !readStyle(lexrc, lay);
528 bool const have_layout = hasLayout(name);
530 // If the layout already exists, then we want to add it to
531 // the existing layout, as long as we are not in an ProvideStyle
533 if (have_layout && !providestyle) {
534 Layout & lay = operator[](name);
535 error = !readStyle(lexrc, lay);
537 // If the layout does not exist, then we want to create a new
538 // one, but not if we are in a ModifyStyle block.
539 else if (!have_layout && !modifystyle) {
541 layout.setName(name);
542 error = !readStyle(lexrc, layout);
544 layoutlist_.push_back(layout);
546 if (defaultlayout_.empty()) {
547 // We do not have a default layout yet, so we choose
548 // the first layout we encounter.
549 defaultlayout_ = name;
552 // There are two ways to get here:
553 // (i) The layout exists but we are in an ProvideStyle block
554 // (ii) The layout doesn't exist, but we are in an ModifyStyle
556 // Either way, we just scan the rest and discard it
559 // signal to coverity that we do not care about the result
560 (void)readStyle(lexrc, lay);
567 docstring const style = from_utf8(subst(lexrc.getString(),
569 if (!deleteLayout(style))
570 lyxerr << "Cannot delete style `"
571 << to_utf8(style) << '\'' << endl;
575 case TC_NOINSETLAYOUT:
577 docstring const style = from_utf8(subst(lexrc.getString(),
579 if (!deleteInsetLayout(style))
580 LYXERR0("Style `" << style << "' cannot be removed\n"
581 "because it was not found!");
587 columns_ = lexrc.getInteger();
592 switch (lexrc.getInteger()) {
593 case 1: sides_ = OneSide; break;
594 case 2: sides_ = TwoSides; break;
596 lyxerr << "Impossible number of page"
597 " sides, setting to one."
607 pagestyle_ = rtrim(lexrc.getString());
611 defaultfont_ = lyxRead(lexrc);
612 if (!defaultfont_.resolved()) {
613 lexrc.printError("Warning: defaultfont should "
614 "be fully instantiated!");
615 defaultfont_.realize(sane_font);
621 secnumdepth_ = lexrc.getInteger();
626 tocdepth_ = lexrc.getInteger();
629 // First step to support options
630 case TC_CLASSOPTIONS:
631 readClassOptions(lexrc);
635 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
638 case TC_HTMLPREAMBLE:
639 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
643 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
646 case TC_HTMLTOCSECTION:
647 html_toc_section_ = from_utf8(trim(lexrc.getString()));
650 case TC_ADDTOPREAMBLE:
651 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
654 case TC_ADDTOHTMLPREAMBLE:
655 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
658 case TC_ADDTOHTMLSTYLES:
659 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
664 string const feature = lexrc.getString();
666 if (lexrc.getInteger())
667 provides_.insert(feature);
669 provides_.erase(feature);
675 vector<string> const req
676 = getVectorFromString(lexrc.getString());
677 requires_.insert(req.begin(), req.end());
683 string const pkg = lexrc.getString();
685 string const options = lexrc.getString();
686 package_options_[pkg] = options;
690 case TC_DEFAULTMODULE: {
692 string const module = lexrc.getString();
693 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
694 default_modules_.push_back(module);
698 case TC_PROVIDESMODULE: {
700 string const module = lexrc.getString();
701 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
702 provided_modules_.push_back(module);
706 case TC_EXCLUDESMODULE: {
708 string const module = lexrc.getString();
709 // modules already have their own way to exclude other modules
711 LYXERR0("ExcludesModule tag cannot be used in a module!");
714 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
715 excluded_modules_.push_back(module);
719 case TC_LEFTMARGIN: // left margin type
721 leftmargin_ = lexrc.getDocString();
724 case TC_RIGHTMARGIN: // right margin type
726 rightmargin_ = lexrc.getDocString();
729 case TC_INSETLAYOUT: {
731 lexrc.printError("No name given for InsetLayout: `$$Token'.");
735 docstring const name = subst(lexrc.getDocString(), '_', ' ');
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.
746 } else if (hasInsetLayout(name)) {
747 InsetLayout & il = insetlayoutlist_[name];
748 error = !il.read(lexrc, *this);
752 error = !il.read(lexrc, *this);
754 insetlayoutlist_[name] = il;
760 error = !readFloat(lexrc);
764 error = !readCiteEngine(lexrc, rt);
767 case TC_ADDTOCITEENGINE:
768 error = !readCiteEngine(lexrc, rt, true);
771 case TC_CITEENGINETYPE:
773 opt_enginetype_ = rtrim(lexrc.getString());
777 error = !readCiteFormat(lexrc, rt);
780 case TC_CITEFRAMEWORK:
782 citeframework_ = rtrim(lexrc.getString());
785 case TC_MAXCITENAMES:
787 maxcitenames_ = size_t(lexrc.getInteger());
790 case TC_DEFAULTBIBLIO:
792 vector<string> const dbs =
793 getVectorFromString(rtrim(lexrc.getString()), "|");
794 vector<string>::const_iterator it = dbs.begin();
795 vector<string>::const_iterator end = dbs.end();
796 for (; it != end; ++it) {
797 if (!contains(*it, ':')) {
798 vector<string> const enginetypes =
799 getVectorFromString(opt_enginetype_, "|");
800 for (string const &s: enginetypes)
801 cite_default_biblio_style_[s] = *it;
804 string const db = split(*it, eng, ':');
805 cite_default_biblio_style_[eng] = db;
811 case TC_FULLAUTHORLIST:
813 cite_full_author_list_ &= lexrc.getBool();
818 docstring const cnt = lexrc.getDocString();
819 if (!counters_.remove(cnt))
820 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
829 docstring const name = lexrc.getDocString();
831 string s = "Could not read name for counter: `$$Token' "
832 + lexrc.getString() + " is probably not valid UTF-8!";
833 lexrc.printError(s.c_str());
835 // Since we couldn't read the name, we just scan the rest
839 error = !counters_.read(lexrc, name, !ifcounter);
842 lexrc.printError("No name given for style: `$$Token'.");
847 case TC_TITLELATEXTYPE:
848 readTitleType(lexrc);
851 case TC_TITLELATEXNAME:
853 titlename_ = lexrc.getString();
858 string const nofloat = lexrc.getString();
859 floatlist_.erase(nofloat);
863 case TC_OUTLINERNAME:
864 error = !readOutlinerName(lexrc);
869 // at present, we abort if we encounter an error,
870 // so there is no point continuing.
877 if (defaultlayout_.empty()) {
878 LYXERR0("Error: Textclass '" << name_
879 << "' is missing a defaultstyle.");
883 // Try to erase "stdinsets" from the provides_ set.
885 // Provides stdinsets 1
886 // declaration simply tells us that the standard insets have been
887 // defined. (It's found in stdinsets.inc but could also be used in
888 // user-defined files.) There isn't really any such package. So we
889 // might as well go ahead and erase it.
890 // If we do not succeed, then it was not there, which means that
891 // the textclass did not provide the definitions of the standard
892 // insets. So we need to try to load them.
893 int erased = provides_.erase("stdinsets");
895 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
898 frontend::Alert::warning(_("Missing File"),
899 _("Could not find stdinsets.inc! This may lead to data loss!"));
901 } else if (!read(tmp, MERGE)) {
902 frontend::Alert::warning(_("Corrupt File"),
903 _("Could not read stdinsets.inc! This may lead to data loss!"));
908 min_toclevel_ = Layout::NOT_IN_TOC;
909 max_toclevel_ = Layout::NOT_IN_TOC;
910 const_iterator lit = begin();
911 const_iterator len = end();
912 for (; lit != len; ++lit) {
913 int const toclevel = lit->toclevel;
914 if (toclevel != Layout::NOT_IN_TOC) {
915 if (min_toclevel_ == Layout::NOT_IN_TOC)
916 min_toclevel_ = toclevel;
918 min_toclevel_ = min(min_toclevel_, toclevel);
919 max_toclevel_ = max(max_toclevel_, toclevel);
922 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
923 << ", maximum is " << max_toclevel_);
925 return (error ? ERROR : OK);
929 void TextClass::readTitleType(Lexer & lexrc)
931 LexerKeyword titleTypeTags[] = {
932 { "commandafter", TITLE_COMMAND_AFTER },
933 { "environment", TITLE_ENVIRONMENT }
936 PushPopHelper pph(lexrc, titleTypeTags);
938 int le = lexrc.lex();
940 case Lexer::LEX_UNDEF:
941 lexrc.printError("Unknown output type `$$Token'");
943 case TITLE_COMMAND_AFTER:
944 case TITLE_ENVIRONMENT:
945 titletype_ = static_cast<TitleLatexType>(le);
948 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
954 void TextClass::readOutputType(Lexer & lexrc)
956 LexerKeyword outputTypeTags[] = {
957 { "docbook", DOCBOOK },
959 { "literate", LITERATE }
962 PushPopHelper pph(lexrc, outputTypeTags);
964 int le = lexrc.lex();
966 case Lexer::LEX_UNDEF:
967 lexrc.printError("Unknown output type `$$Token'");
972 outputType_ = static_cast<OutputType>(le);
975 LYXERR0("Unhandled value " << le);
981 void TextClass::readClassOptions(Lexer & lexrc)
991 LexerKeyword classOptionsTags[] = {
993 {"fontsize", CO_FONTSIZE },
994 {"header", CO_HEADER },
995 {"other", CO_OTHER },
996 {"pagestyle", CO_PAGESTYLE }
999 lexrc.pushTable(classOptionsTags);
1000 bool getout = false;
1001 while (!getout && lexrc.isOK()) {
1002 int le = lexrc.lex();
1004 case Lexer::LEX_UNDEF:
1005 lexrc.printError("Unknown ClassOption tag `$$Token'");
1013 opt_fontsize_ = rtrim(lexrc.getString());
1017 opt_pagestyle_ = rtrim(lexrc.getString());
1021 if (options_.empty())
1022 options_ = lexrc.getString();
1024 options_ += ',' + lexrc.getString();
1028 class_header_ = subst(lexrc.getString(), """, "\"");
1039 vector<CitationStyle> const & TextClass::getCiteStyles(
1040 CiteEngineType const & type) const
1042 static vector<CitationStyle> empty;
1043 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1044 if (it == cite_styles_.end())
1050 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1052 int const type = readCiteEngineType(lexrc);
1053 CiteEngineType cetype = ENGINE_TYPE_DEFAULT;
1054 if (type & ENGINE_TYPE_AUTHORYEAR)
1055 cetype = ENGINE_TYPE_AUTHORYEAR;
1056 else if (type & ENGINE_TYPE_NUMERICAL)
1057 cetype = ENGINE_TYPE_NUMERICAL;
1059 if (rt == CITE_ENGINE && !getCiteStyles(cetype).empty())
1060 // The cite engines are not supposed to overwrite
1061 // CiteStyle defined by the class or a module.
1064 if (rt != CITE_ENGINE && !add)
1065 // Reset if we defined CiteStyle
1066 // from the class or a module
1067 cite_styles_[cetype].clear();
1070 bool getout = false;
1071 while (!getout && lexrc.isOK()) {
1073 def = lexrc.getString();
1074 def = subst(def, " ", "");
1075 def = subst(def, "\t", "");
1076 if (compare_ascii_no_case(def, "end") == 0) {
1081 char ichar = def[0];
1084 if (isUpperCase(ichar)) {
1085 cs.forceUpperCase = true;
1086 def[0] = lowercase(ichar);
1089 /** For portability reasons (between different
1090 * cite engines such as natbib and biblatex),
1091 * we distinguish between:
1092 * 1. The LyX name as output in the LyX file
1093 * 2. Possible aliases that might fall back to
1094 * the given LyX name in the current engine
1095 * 3. The actual LaTeX command that is output
1096 * (2) and (3) are optional.
1097 * Also, the GUI string for the starred version can
1100 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1109 ScanMode mode = LyXName;
1110 ScanMode oldmode = LyXName;
1115 size_t const n = def.size();
1116 for (size_t i = 0; i != n; ++i) {
1120 else if (ichar == '=')
1122 else if (ichar == '<') {
1125 } else if (ichar == '>')
1127 else if (mode == LaTeXCmd)
1129 else if (mode == StarDesc)
1131 else if (ichar == '$')
1132 cs.hasQualifiedList = true;
1133 else if (ichar == '*')
1134 cs.hasStarredVersion = true;
1135 else if (ichar == '[' && cs.textAfter)
1136 cs.textBefore = true;
1137 else if (ichar == '[')
1138 cs.textAfter = true;
1139 else if (ichar != ']') {
1147 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1148 if (!alias.empty()) {
1149 vector<string> const aliases = getVectorFromString(alias);
1150 for (string const &s: aliases)
1151 cite_command_aliases_[s] = lyx_cmd;
1153 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1154 int size = stardesc.size();
1156 cs.stardesc = stardescs[0];
1158 cs.startooltip = stardescs[1];
1160 class_cite_styles_[cetype].push_back(cs);
1162 cite_styles_[cetype].push_back(cs);
1164 // Stop here if we do AddToCiteEngine,
1165 // except if we have already a style to add something to
1166 if (add && getCiteStyles(cetype).empty())
1169 // Add the styles from AddToCiteEngine to the class' styles
1170 // (but only if they are not yet defined)
1171 for (auto const cis : class_cite_styles_) {
1172 // Only consider the current CiteEngineType
1173 if (cis.first != cetype)
1175 for (auto const ciss : cis.second) {
1176 bool defined = false;
1177 // Check if the style "name" is already def'ed
1178 for (auto const av : getCiteStyles(cis.first))
1179 if (av.name == ciss.name)
1182 cite_styles_[cis.first].push_back(ciss);
1185 class_cite_styles_[cetype].clear();
1190 int TextClass::readCiteEngineType(Lexer & lexrc) const
1192 static_assert(ENGINE_TYPE_DEFAULT ==
1193 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1194 "Incorrect default engine type");
1195 if (!lexrc.next()) {
1196 lexrc.printError("No cite engine type given for token: `$$Token'.");
1197 return ENGINE_TYPE_DEFAULT;
1199 string const type = rtrim(lexrc.getString());
1200 if (compare_ascii_no_case(type, "authoryear") == 0)
1201 return ENGINE_TYPE_AUTHORYEAR;
1202 else if (compare_ascii_no_case(type, "numerical") == 0)
1203 return ENGINE_TYPE_NUMERICAL;
1204 else if (compare_ascii_no_case(type, "default") != 0) {
1205 string const s = "Unknown cite engine type `" + type
1206 + "' given for token: `$$Token',";
1207 lexrc.printError(s);
1209 return ENGINE_TYPE_DEFAULT;
1213 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1215 int const type = readCiteEngineType(lexrc);
1216 CiteEngineType cetype = ENGINE_TYPE_DEFAULT;
1217 if (type & ENGINE_TYPE_AUTHORYEAR)
1218 cetype = ENGINE_TYPE_AUTHORYEAR;
1219 else if (type & ENGINE_TYPE_NUMERICAL)
1220 cetype = ENGINE_TYPE_NUMERICAL;
1224 // Cite engine definitions do not overwrite existing
1225 // definitions from the class or a module
1226 bool const overwrite = rt != CITE_ENGINE;
1227 while (lexrc.isOK()) {
1229 etype = lexrc.getString();
1230 if (compare_ascii_no_case(etype, "end") == 0)
1235 definition = lexrc.getString();
1236 char initchar = etype[0];
1237 if (initchar == '#')
1239 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1240 bool defined = false;
1241 // Check if the macro is already def'ed
1242 for (auto const cm : cite_macros_) {
1243 if (cm.first != cetype)
1245 if (cm.second.find(etype) != cm.second.end())
1248 if (!defined || overwrite)
1249 cite_macros_[cetype][etype] = definition;
1251 bool defined = false;
1252 // Check if the format is already def'ed
1253 for (auto const cm : cite_formats_) {
1254 if (cm.first != cetype)
1256 if (cm.second.find(etype) != cm.second.end())
1259 if (!defined || overwrite)
1260 cite_formats_[cetype][etype] = definition;
1267 bool TextClass::readFloat(Lexer & lexrc)
1284 FT_ALLOWED_PLACEMENT,
1290 LexerKeyword floatTags[] = {
1291 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1292 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1293 { "allowswide", FT_ALLOWS_WIDE },
1295 { "extension", FT_EXT },
1296 { "guiname", FT_NAME },
1297 { "htmlattr", FT_HTMLATTR },
1298 { "htmlstyle", FT_HTMLSTYLE },
1299 { "htmltag", FT_HTMLTAG },
1300 { "ispredefined", FT_PREDEFINED },
1301 { "listcommand", FT_LISTCOMMAND },
1302 { "listname", FT_LISTNAME },
1303 { "numberwithin", FT_WITHIN },
1304 { "placement", FT_PLACEMENT },
1305 { "refprefix", FT_REFPREFIX },
1306 { "style", FT_STYLE },
1307 { "type", FT_TYPE },
1308 { "usesfloatpkg", FT_USESFLOAT }
1311 lexrc.pushTable(floatTags);
1315 docstring htmlstyle;
1321 string allowed_placement = "!htbpH";
1326 bool usesfloat = true;
1327 bool ispredefined = false;
1328 bool allowswide = true;
1329 bool allowssideways = true;
1331 bool getout = false;
1332 while (!getout && lexrc.isOK()) {
1333 int le = lexrc.lex();
1335 case Lexer::LEX_UNDEF:
1336 lexrc.printError("Unknown float tag `$$Token'");
1344 type = lexrc.getString();
1345 if (floatlist_.typeExist(type)) {
1346 Floating const & fl = floatlist_.getType(type);
1347 placement = fl.placement();
1349 within = fl.within();
1352 listname = fl.listName();
1353 usesfloat = fl.usesFloatPkg();
1354 ispredefined = fl.isPredefined();
1355 listcommand = fl.listCommand();
1356 refprefix = fl.refPrefix();
1361 name = lexrc.getString();
1365 placement = lexrc.getString();
1367 case FT_ALLOWED_PLACEMENT:
1369 allowed_placement = lexrc.getString();
1373 ext = lexrc.getString();
1377 within = lexrc.getString();
1378 if (within == "none")
1383 style = lexrc.getString();
1385 case FT_LISTCOMMAND:
1387 listcommand = lexrc.getString();
1391 refprefix = lexrc.getString();
1395 listname = lexrc.getString();
1399 usesfloat = lexrc.getBool();
1403 ispredefined = lexrc.getBool();
1405 case FT_ALLOWS_SIDEWAYS:
1407 allowssideways = lexrc.getBool();
1409 case FT_ALLOWS_WIDE:
1411 allowswide = lexrc.getBool();
1415 htmlattr = lexrc.getString();
1419 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1423 htmltag = lexrc.getString();
1433 // Here we have a full float if getout == true
1435 if (!usesfloat && listcommand.empty()) {
1436 // if this float uses the same auxfile as an existing one,
1437 // there is no need for it to provide a list command.
1438 FloatList::const_iterator it = floatlist_.begin();
1439 FloatList::const_iterator en = floatlist_.end();
1440 bool found_ext = false;
1441 for (; it != en; ++it) {
1442 if (it->second.ext() == ext) {
1448 LYXERR0("The layout does not provide a list command " <<
1449 "for the float `" << type << "'. LyX will " <<
1450 "not be able to produce a float list.");
1452 Floating fl(type, placement, ext, within, style, name,
1453 listname, listcommand, refprefix, allowed_placement,
1454 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1455 allowswide, allowssideways);
1456 floatlist_.newFloat(fl);
1457 // each float has its own counter
1458 counters_.newCounter(from_ascii(type), from_ascii(within),
1459 docstring(), docstring());
1460 // also define sub-float counters
1461 docstring const subtype = "sub-" + from_ascii(type);
1462 counters_.newCounter(subtype, from_ascii(type),
1463 "\\alph{" + subtype + "}", docstring());
1469 bool TextClass::readOutlinerName(Lexer & lexrc)
1474 type = lexrc.getString();
1476 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1480 name = lexrc.getDocString();
1482 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1485 outliner_names_[type] = name;
1490 string const & TextClass::prerequisites(string const & sep) const
1492 if (contains(prerequisites_, ',')) {
1493 vector<string> const pres = getVectorFromString(prerequisites_);
1494 prerequisites_ = getStringFromVector(pres, sep);
1496 return prerequisites_;
1500 bool TextClass::hasLayout(docstring const & n) const
1502 docstring const name = n.empty() ? defaultLayoutName() : n;
1504 return find_if(layoutlist_.begin(), layoutlist_.end(),
1505 LayoutNamesEqual(name))
1506 != layoutlist_.end();
1510 bool TextClass::hasInsetLayout(docstring const & n) const
1514 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1515 return it != insetlayoutlist_.end();
1519 Layout const & TextClass::operator[](docstring const & name) const
1521 LATTEST(!name.empty());
1524 find_if(begin(), end(), LayoutNamesEqual(name));
1527 LYXERR0("We failed to find the layout '" << name
1528 << "' in the layout list. You MUST investigate!");
1529 for (const_iterator cit = begin(); cit != end(); ++cit)
1530 lyxerr << " " << to_utf8(cit->name()) << endl;
1532 // We require the name to exist
1533 static const Layout dummy;
1534 LASSERT(false, return dummy);
1541 Layout & TextClass::operator[](docstring const & name)
1543 LATTEST(!name.empty());
1544 // Safe to continue, given what we do below.
1546 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1549 LYXERR0("We failed to find the layout '" << to_utf8(name)
1550 << "' in the layout list. You MUST investigate!");
1551 for (const_iterator cit = begin(); cit != end(); ++cit)
1552 LYXERR0(" " << to_utf8(cit->name()));
1554 // we require the name to exist
1556 // we are here only in release mode
1557 layoutlist_.push_back(createBasicLayout(name, true));
1558 it = find_if(begin(), end(), LayoutNamesEqual(name));
1565 bool TextClass::deleteLayout(docstring const & name)
1567 if (name == defaultLayoutName() || name == plainLayoutName())
1570 LayoutList::iterator it =
1571 remove_if(layoutlist_.begin(), layoutlist_.end(),
1572 LayoutNamesEqual(name));
1574 LayoutList::iterator end = layoutlist_.end();
1575 bool const ret = (it != end);
1576 layoutlist_.erase(it, end);
1581 bool TextClass::deleteInsetLayout(docstring const & name)
1583 return insetlayoutlist_.erase(name);
1587 // Load textclass info if not loaded yet
1588 bool TextClass::load(string const & path) const
1593 // Read style-file, provided path is searched before the system ones
1594 // If path is a file, it is loaded directly.
1595 FileName layout_file(path);
1596 if (!path.empty() && !layout_file.isReadableFile())
1597 layout_file = FileName(addName(path, name_ + ".layout"));
1598 if (layout_file.empty() || !layout_file.exists())
1599 layout_file = libFileSearch("layouts", name_, "layout");
1600 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1603 lyxerr << "Error reading `"
1604 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1605 << "'\n(Check `" << name_
1606 << "')\nCheck your installation and "
1607 "try Options/Reconfigure..."
1615 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1620 layoutlist_.push_back(createBasicLayout(n, true));
1625 string DocumentClass::forcedLayouts() const
1629 const_iterator const e = end();
1630 for (const_iterator i = begin(); i != e; ++i) {
1631 if (i->forcelocal > 0) {
1633 os << "Format " << LAYOUT_FORMAT << '\n';
1643 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1645 // FIXME The fix for the InsetLayout part of 4812 would be here:
1646 // Add the InsetLayout to the document class if it is not found.
1648 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1649 while (!n.empty()) {
1650 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1651 if (cit != cen && cit->first == n) {
1652 if (cit->second.obsoleted_by().empty())
1654 n = cit->second.obsoleted_by();
1655 return insetLayout(n);
1657 // If we have a generic prefix (e.g., "Note:"),
1658 // try if this one alone is found.
1659 size_t i = n.find(':');
1660 if (i == string::npos)
1664 // Layout "name" not found.
1665 return plainInsetLayout();
1669 InsetLayout const & DocumentClass::plainInsetLayout() {
1670 static const InsetLayout plain_insetlayout_;
1671 return plain_insetlayout_;
1675 docstring const & TextClass::defaultLayoutName() const
1677 return defaultlayout_;
1681 Layout const & TextClass::defaultLayout() const
1683 return operator[](defaultLayoutName());
1687 bool TextClass::isDefaultLayout(Layout const & layout) const
1689 return layout.name() == defaultLayoutName();
1693 bool TextClass::isPlainLayout(Layout const & layout) const
1695 return layout.name() == plainLayoutName();
1699 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1701 static Layout * defaultLayout = NULL;
1703 if (defaultLayout) {
1704 defaultLayout->setUnknown(unknown);
1705 defaultLayout->setName(name);
1706 return *defaultLayout;
1709 static char const * s = "Margin Static\n"
1710 "LatexType Paragraph\n"
1713 "AlignPossible Left, Right, Center\n"
1714 "LabelType No_Label\n"
1716 istringstream ss(s);
1717 Lexer lex(textClassTags);
1719 defaultLayout = new Layout;
1720 defaultLayout->setUnknown(unknown);
1721 defaultLayout->setName(name);
1722 if (!readStyle(lex, *defaultLayout)) {
1723 // The only way this happens is because the hardcoded layout above
1727 return *defaultLayout;
1731 DocumentClassPtr getDocumentClass(
1732 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1733 string const & cengine, bool const clone)
1735 DocumentClassPtr doc_class =
1736 DocumentClassPtr(new DocumentClass(baseClass));
1737 LayoutModuleList::const_iterator it = modlist.begin();
1738 LayoutModuleList::const_iterator en = modlist.end();
1739 for (; it != en; ++it) {
1740 string const modName = *it;
1741 LyXModule * lm = theModuleList[modName];
1743 docstring const msg =
1744 bformat(_("The module %1$s has been requested by\n"
1745 "this document but has not been found in the list of\n"
1746 "available modules. If you recently installed it, you\n"
1747 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1749 frontend::Alert::warning(_("Module not available"), msg);
1752 if (!lm->isAvailable() && !clone) {
1753 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1754 docstring const msg =
1755 bformat(_("The module %1$s requires a package that is not\n"
1756 "available in your LaTeX installation, or a converter that\n"
1757 "you have not installed. LaTeX output may not be possible.\n"
1758 "Missing prerequisites:\n"
1760 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1761 from_utf8(modName), prereqs);
1762 frontend::Alert::warning(_("Package not available"), msg, true);
1764 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1765 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1766 docstring const msg =
1767 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1768 frontend::Alert::warning(_("Read Error"), msg);
1772 if (cengine.empty())
1775 LyXCiteEngine * ce = theCiteEnginesList[cengine];
1777 docstring const msg =
1778 bformat(_("The cite engine %1$s has been requested by\n"
1779 "this document but has not been found in the list of\n"
1780 "available engines. If you recently installed it, you\n"
1781 "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1783 frontend::Alert::warning(_("Cite Engine not available"), msg);
1784 } else if (!ce->isAvailable() && !clone) {
1785 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1786 docstring const msg =
1787 bformat(_("The cite engine %1$s requires a package that is not\n"
1788 "available in your LaTeX installation, or a converter that\n"
1789 "you have not installed. LaTeX output may not be possible.\n"
1790 "Missing prerequisites:\n"
1792 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1793 from_utf8(cengine), prereqs);
1794 frontend::Alert::warning(_("Package not available"), msg, true);
1796 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1797 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1798 docstring const msg =
1799 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1800 frontend::Alert::warning(_("Read Error"), msg);
1808 /////////////////////////////////////////////////////////////////////////
1812 /////////////////////////////////////////////////////////////////////////
1814 DocumentClass::DocumentClass(LayoutFile const & tc)
1819 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1821 LayoutList::const_iterator it = layoutlist_.begin();
1822 LayoutList::const_iterator end = layoutlist_.end();
1823 for (; it != end; ++it)
1824 if (it->latexname() == lay)
1830 bool DocumentClass::provides(string const & p) const
1832 return provides_.find(p) != provides_.end();
1836 bool DocumentClass::hasTocLevels() const
1838 return min_toclevel_ != Layout::NOT_IN_TOC;
1842 Layout const & DocumentClass::getTOCLayout() const
1844 // we're going to look for the layout with the minimum toclevel
1845 TextClass::LayoutList::const_iterator lit = begin();
1846 TextClass::LayoutList::const_iterator const len = end();
1847 int minlevel = 1000;
1848 Layout const * lay = NULL;
1849 for (; lit != len; ++lit) {
1850 int const level = lit->toclevel;
1851 // we don't want Part or unnumbered sections
1852 if (level == Layout::NOT_IN_TOC || level < 0
1853 || level >= minlevel || lit->counter.empty())
1860 // hmm. that is very odd, so we'll do our best.
1861 return operator[](defaultLayoutName());
1865 Layout const & DocumentClass::htmlTOCLayout() const
1867 if (html_toc_section_.empty())
1868 html_toc_section_ = getTOCLayout().name();
1869 return operator[](html_toc_section_);
1873 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1874 string const & entry, bool const punct, string const & fallback) const
1876 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1878 default_format += ".";
1880 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1881 if (itype == cite_formats_.end())
1882 return default_format;
1883 map<string, string>::const_iterator it = itype->second.find(entry);
1884 if (it == itype->second.end() && !fallback.empty())
1885 it = itype->second.find(fallback);
1886 if (it == itype->second.end())
1887 return default_format;
1889 return it->second + ".";
1894 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1895 string const & macro) const
1897 static string empty;
1898 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1899 if (itype == cite_macros_.end())
1901 map<string, string>::const_iterator it = itype->second.find(macro);
1902 if (it == itype->second.end())
1908 vector<string> const DocumentClass::citeCommands(
1909 CiteEngineType const & type) const
1911 vector<CitationStyle> const styles = citeStyles(type);
1912 vector<CitationStyle>::const_iterator it = styles.begin();
1913 vector<CitationStyle>::const_iterator end = styles.end();
1914 vector<string> cmds;
1915 for (; it != end; ++it) {
1916 CitationStyle const cite = *it;
1917 cmds.push_back(cite.name);
1923 vector<CitationStyle> const & DocumentClass::citeStyles(
1924 CiteEngineType const & type) const
1926 static vector<CitationStyle> empty;
1927 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1928 if (it == cite_styles_.end())
1934 /////////////////////////////////////////////////////////////////////////
1938 /////////////////////////////////////////////////////////////////////////
1940 ostream & operator<<(ostream & os, PageSides p)