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 = 63; //spitz: new tags CiteFramework, MaxCiteNames, extended InsetCite syntax.
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"),
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,
234 LexerKeyword textClassTags[] = {
235 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
236 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
237 { "addtopreamble", TC_ADDTOPREAMBLE },
238 { "citeengine", TC_CITEENGINE },
239 { "citeenginetype", TC_CITEENGINETYPE },
240 { "citeformat", TC_CITEFORMAT },
241 { "citeframework", TC_CITEFRAMEWORK },
242 { "classoptions", TC_CLASSOPTIONS },
243 { "columns", TC_COLUMNS },
244 { "counter", TC_COUNTER },
245 { "defaultbiblio", TC_DEFAULTBIBLIO },
246 { "defaultfont", TC_DEFAULTFONT },
247 { "defaultmodule", TC_DEFAULTMODULE },
248 { "defaultstyle", TC_DEFAULTSTYLE },
249 { "excludesmodule", TC_EXCLUDESMODULE },
250 { "float", TC_FLOAT },
251 { "format", TC_FORMAT },
252 { "fullauthorlist", TC_FULLAUTHORLIST },
253 { "htmlpreamble", TC_HTMLPREAMBLE },
254 { "htmlstyles", TC_HTMLSTYLES },
255 { "htmltocsection", TC_HTMLTOCSECTION },
256 { "ifcounter", TC_IFCOUNTER },
257 { "input", TC_INPUT },
258 { "insetlayout", TC_INSETLAYOUT },
259 { "leftmargin", TC_LEFTMARGIN },
260 { "maxcitenames", TC_MAXCITENAMES },
261 { "modifystyle", TC_MODIFYSTYLE },
262 { "nocounter", TC_NOCOUNTER },
263 { "nofloat", TC_NOFLOAT },
264 { "noinsetlayout", TC_NOINSETLAYOUT },
265 { "nostyle", TC_NOSTYLE },
266 { "outlinername", TC_OUTLINERNAME },
267 { "outputformat", TC_OUTPUTFORMAT },
268 { "outputtype", TC_OUTPUTTYPE },
269 { "packageoptions", TC_PKGOPTS },
270 { "pagestyle", TC_PAGESTYLE },
271 { "preamble", TC_PREAMBLE },
272 { "provides", TC_PROVIDES },
273 { "providesmodule", TC_PROVIDESMODULE },
274 { "providestyle", TC_PROVIDESTYLE },
275 { "requires", TC_REQUIRES },
276 { "rightmargin", TC_RIGHTMARGIN },
277 { "secnumdepth", TC_SECNUMDEPTH },
278 { "sides", TC_SIDES },
279 { "style", TC_STYLE },
280 { "titlelatexname", TC_TITLELATEXNAME },
281 { "titlelatextype", TC_TITLELATEXTYPE },
282 { "tocdepth", TC_TOCDEPTH }
288 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
290 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
291 TempFile tmp("convertXXXXXX.layout");
292 FileName const tempfile = tmp.name();
293 bool success = layout2layout(filename, tempfile);
295 success = readWithoutConv(tempfile, rt) == OK;
300 std::string TextClass::convert(std::string const & str)
302 TempFile tmp1("localXXXXXX.layout");
303 FileName const fn = tmp1.name();
304 ofstream os(fn.toFilesystemEncoding().c_str());
307 TempFile tmp2("convert_localXXXXXX.layout");
308 FileName const tempfile = tmp2.name();
309 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
312 ifstream is(tempfile.toFilesystemEncoding().c_str());
324 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
326 if (!filename.isReadableFile()) {
327 lyxerr << "Cannot read layout file `" << filename << "'."
332 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
333 to_utf8(makeDisplayPath(filename.absFileName())));
335 // Define the plain layout used in table cells, ert, etc. Note that
336 // we do this before loading any layout file, so that classes can
337 // override features of this layout if they should choose to do so.
338 if (rt == BASECLASS && !hasLayout(plain_layout_))
339 layoutlist_.push_back(createBasicLayout(plain_layout_));
341 Lexer lexrc(textClassTags);
342 lexrc.setFile(filename);
343 ReturnValues retval = read(lexrc, rt);
345 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
346 to_utf8(makeDisplayPath(filename.absFileName())));
352 bool TextClass::read(FileName const & filename, ReadType rt)
354 ReturnValues const retval = readWithoutConv(filename, rt);
355 if (retval != FORMAT_MISMATCH)
358 bool const worx = convertLayoutFormat(filename, rt);
360 LYXERR0 ("Unable to convert " << filename <<
361 " to format " << LAYOUT_FORMAT);
366 TextClass::ReturnValues TextClass::validate(std::string const & str)
369 return tc.read(str, VALIDATION);
373 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
375 Lexer lexrc(textClassTags);
376 istringstream is(str);
378 ReturnValues retval = read(lexrc, rt);
380 if (retval != FORMAT_MISMATCH)
383 // write the layout string to a temporary file
384 TempFile tmp("TextClass_read");
385 FileName const tempfile = tmp.name();
386 ofstream os(tempfile.toFilesystemEncoding().c_str());
388 LYXERR0("Unable to create temporary file");
394 // now try to convert it to LAYOUT_FORMAT
395 if (!convertLayoutFormat(tempfile, rt)) {
396 LYXERR0("Unable to convert internal layout information to format "
405 // Reads a textclass structure from file.
406 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
411 // The first usable line should be
412 // Format LAYOUT_FORMAT
413 if (lexrc.lex() != TC_FORMAT || !lexrc.next()
414 || lexrc.getInteger() != LAYOUT_FORMAT)
415 return FORMAT_MISMATCH;
419 while (lexrc.isOK() && !error) {
420 int le = lexrc.lex();
423 case Lexer::LEX_FEOF:
426 case Lexer::LEX_UNDEF:
427 lexrc.printError("Unknown TextClass tag `$$Token'");
435 // used below to track whether we are in an IfStyle or IfCounter tag.
436 bool modifystyle = false;
437 bool providestyle = false;
438 bool ifcounter = false;
440 switch (static_cast<TextClassTags>(le)) {
444 lexrc.printError("Duplicate Format directive");
447 case TC_OUTPUTFORMAT:
449 outputFormat_ = lexrc.getString();
453 readOutputType(lexrc);
454 switch(outputType_) {
456 outputFormat_ = "latex";
459 outputFormat_ = "docbook";
462 outputFormat_ = "literate";
467 case TC_INPUT: // Include file
470 string const inc = lexrc.getString();
471 if (!path().empty() && (prefixIs(inc, "./") ||
472 prefixIs(inc, "../")))
473 tmp = fileSearch(path(), inc, "layout");
475 tmp = libFileSearch("layouts", inc,
479 lexrc.printError("Could not find input file: " + inc);
481 } else if (!read(tmp, MERGE)) {
482 lexrc.printError("Error reading input file: " + tmp.absFileName());
488 case TC_DEFAULTSTYLE:
490 docstring const name = from_utf8(subst(lexrc.getString(),
492 defaultlayout_ = name;
499 case TC_PROVIDESTYLE:
500 // if modifystyle is true, then we got here by falling through
501 // so we are not in an ProvideStyle block
507 lexrc.printError("No name given for style: `$$Token'.");
511 docstring const name = from_utf8(subst(lexrc.getString(),
514 string s = "Could not read name for style: `$$Token' "
515 + lexrc.getString() + " is probably not valid UTF-8!";
518 // Since we couldn't read the name, we just scan the rest
519 // of the style and discard it.
520 error = !readStyle(lexrc, lay);
524 bool const have_layout = hasLayout(name);
526 // If the layout already exists, then we want to add it to
527 // the existing layout, as long as we are not in an ProvideStyle
529 if (have_layout && !providestyle) {
530 Layout & lay = operator[](name);
531 error = !readStyle(lexrc, lay);
533 // If the layout does not exist, then we want to create a new
534 // one, but not if we are in a ModifyStyle block.
535 else if (!have_layout && !modifystyle) {
537 layout.setName(name);
538 error = !readStyle(lexrc, layout);
540 layoutlist_.push_back(layout);
542 if (defaultlayout_.empty()) {
543 // We do not have a default layout yet, so we choose
544 // the first layout we encounter.
545 defaultlayout_ = name;
548 // There are two ways to get here:
549 // (i) The layout exists but we are in an ProvideStyle block
550 // (ii) The layout doesn't exist, but we are in an ModifyStyle
552 // Either way, we just scan the rest and discard it
555 // signal to coverity that we do not care about the result
556 (void)readStyle(lexrc, lay);
563 docstring const style = from_utf8(subst(lexrc.getString(),
565 if (!deleteLayout(style))
566 lyxerr << "Cannot delete style `"
567 << to_utf8(style) << '\'' << endl;
571 case TC_NOINSETLAYOUT:
573 docstring const style = from_utf8(subst(lexrc.getString(),
575 if (!deleteInsetLayout(style))
576 LYXERR0("Style `" << style << "' cannot be removed\n"
577 "because it was not found!");
583 columns_ = lexrc.getInteger();
588 switch (lexrc.getInteger()) {
589 case 1: sides_ = OneSide; break;
590 case 2: sides_ = TwoSides; break;
592 lyxerr << "Impossible number of page"
593 " sides, setting to one."
603 pagestyle_ = rtrim(lexrc.getString());
607 defaultfont_ = lyxRead(lexrc);
608 if (!defaultfont_.resolved()) {
609 lexrc.printError("Warning: defaultfont should "
610 "be fully instantiated!");
611 defaultfont_.realize(sane_font);
617 secnumdepth_ = lexrc.getInteger();
622 tocdepth_ = lexrc.getInteger();
625 // First step to support options
626 case TC_CLASSOPTIONS:
627 readClassOptions(lexrc);
631 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
634 case TC_HTMLPREAMBLE:
635 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
639 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
642 case TC_HTMLTOCSECTION:
643 html_toc_section_ = from_utf8(trim(lexrc.getString()));
646 case TC_ADDTOPREAMBLE:
647 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
650 case TC_ADDTOHTMLPREAMBLE:
651 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
654 case TC_ADDTOHTMLSTYLES:
655 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
660 string const feature = lexrc.getString();
662 if (lexrc.getInteger())
663 provides_.insert(feature);
665 provides_.erase(feature);
671 vector<string> const req
672 = getVectorFromString(lexrc.getString());
673 requires_.insert(req.begin(), req.end());
679 string const pkg = lexrc.getString();
681 string const options = lexrc.getString();
682 package_options_[pkg] = options;
686 case TC_DEFAULTMODULE: {
688 string const module = lexrc.getString();
689 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
690 default_modules_.push_back(module);
694 case TC_PROVIDESMODULE: {
696 string const module = lexrc.getString();
697 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
698 provided_modules_.push_back(module);
702 case TC_EXCLUDESMODULE: {
704 string const module = lexrc.getString();
705 // modules already have their own way to exclude other modules
707 LYXERR0("ExcludesModule tag cannot be used in a module!");
710 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
711 excluded_modules_.push_back(module);
715 case TC_LEFTMARGIN: // left margin type
717 leftmargin_ = lexrc.getDocString();
720 case TC_RIGHTMARGIN: // right margin type
722 rightmargin_ = lexrc.getDocString();
725 case TC_INSETLAYOUT: {
727 lexrc.printError("No name given for InsetLayout: `$$Token'.");
731 docstring const name = subst(lexrc.getDocString(), '_', ' ');
733 string s = "Could not read name for InsetLayout: `$$Token' "
734 + lexrc.getString() + " is probably not valid UTF-8!";
737 // Since we couldn't read the name, we just scan the rest
738 // of the style and discard it.
739 il.read(lexrc, *this);
740 // Let's try to continue rather than abort.
742 } else if (hasInsetLayout(name)) {
743 InsetLayout & il = insetlayoutlist_[name];
744 error = !il.read(lexrc, *this);
748 error = !il.read(lexrc, *this);
750 insetlayoutlist_[name] = il;
756 error = !readFloat(lexrc);
760 error = !readCiteEngine(lexrc);
763 case TC_CITEENGINETYPE:
765 opt_enginetype_ = rtrim(lexrc.getString());
769 error = !readCiteFormat(lexrc);
772 case TC_CITEFRAMEWORK:
774 citeframework_ = rtrim(lexrc.getString());
777 case TC_MAXCITENAMES:
779 maxcitenames_ = size_t(lexrc.getInteger());
782 case TC_DEFAULTBIBLIO:
784 vector<string> const dbs =
785 getVectorFromString(rtrim(lexrc.getString()), "|");
786 vector<string>::const_iterator it = dbs.begin();
787 vector<string>::const_iterator end = dbs.end();
788 for (; it != end; ++it) {
789 if (!contains(*it, ':')) {
790 vector<string> const enginetypes =
791 getVectorFromString(opt_enginetype_, "|");
792 for (string const &s: enginetypes)
793 cite_default_biblio_style_[s] = *it;
796 string const db = split(*it, eng, ':');
797 cite_default_biblio_style_[eng] = db;
803 case TC_FULLAUTHORLIST:
805 cite_full_author_list_ &= lexrc.getBool();
810 docstring const cnt = lexrc.getDocString();
811 if (!counters_.remove(cnt))
812 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
821 docstring const name = lexrc.getDocString();
823 string s = "Could not read name for counter: `$$Token' "
824 + lexrc.getString() + " is probably not valid UTF-8!";
825 lexrc.printError(s.c_str());
827 // Since we couldn't read the name, we just scan the rest
831 error = !counters_.read(lexrc, name, !ifcounter);
834 lexrc.printError("No name given for style: `$$Token'.");
839 case TC_TITLELATEXTYPE:
840 readTitleType(lexrc);
843 case TC_TITLELATEXNAME:
845 titlename_ = lexrc.getString();
850 string const nofloat = lexrc.getString();
851 floatlist_.erase(nofloat);
855 case TC_OUTLINERNAME:
856 error = !readOutlinerName(lexrc);
861 // at present, we abort if we encounter an error,
862 // so there is no point continuing.
869 if (defaultlayout_.empty()) {
870 LYXERR0("Error: Textclass '" << name_
871 << "' is missing a defaultstyle.");
875 // Try to erase "stdinsets" from the provides_ set.
877 // Provides stdinsets 1
878 // declaration simply tells us that the standard insets have been
879 // defined. (It's found in stdinsets.inc but could also be used in
880 // user-defined files.) There isn't really any such package. So we
881 // might as well go ahead and erase it.
882 // If we do not succeed, then it was not there, which means that
883 // the textclass did not provide the definitions of the standard
884 // insets. So we need to try to load them.
885 int erased = provides_.erase("stdinsets");
887 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
890 frontend::Alert::warning(_("Missing File"),
891 _("Could not find stdinsets.inc! This may lead to data loss!"));
893 } else if (!read(tmp, MERGE)) {
894 frontend::Alert::warning(_("Corrupt File"),
895 _("Could not read stdinsets.inc! This may lead to data loss!"));
900 min_toclevel_ = Layout::NOT_IN_TOC;
901 max_toclevel_ = Layout::NOT_IN_TOC;
902 const_iterator lit = begin();
903 const_iterator len = end();
904 for (; lit != len; ++lit) {
905 int const toclevel = lit->toclevel;
906 if (toclevel != Layout::NOT_IN_TOC) {
907 if (min_toclevel_ == Layout::NOT_IN_TOC)
908 min_toclevel_ = toclevel;
910 min_toclevel_ = min(min_toclevel_, toclevel);
911 max_toclevel_ = max(max_toclevel_, toclevel);
914 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
915 << ", maximum is " << max_toclevel_);
917 return (error ? ERROR : OK);
921 void TextClass::readTitleType(Lexer & lexrc)
923 LexerKeyword titleTypeTags[] = {
924 { "commandafter", TITLE_COMMAND_AFTER },
925 { "environment", TITLE_ENVIRONMENT }
928 PushPopHelper pph(lexrc, titleTypeTags);
930 int le = lexrc.lex();
932 case Lexer::LEX_UNDEF:
933 lexrc.printError("Unknown output type `$$Token'");
935 case TITLE_COMMAND_AFTER:
936 case TITLE_ENVIRONMENT:
937 titletype_ = static_cast<TitleLatexType>(le);
940 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
946 void TextClass::readOutputType(Lexer & lexrc)
948 LexerKeyword outputTypeTags[] = {
949 { "docbook", DOCBOOK },
951 { "literate", LITERATE }
954 PushPopHelper pph(lexrc, outputTypeTags);
956 int le = lexrc.lex();
958 case Lexer::LEX_UNDEF:
959 lexrc.printError("Unknown output type `$$Token'");
964 outputType_ = static_cast<OutputType>(le);
967 LYXERR0("Unhandled value " << le);
973 void TextClass::readClassOptions(Lexer & lexrc)
983 LexerKeyword classOptionsTags[] = {
985 {"fontsize", CO_FONTSIZE },
986 {"header", CO_HEADER },
987 {"other", CO_OTHER },
988 {"pagestyle", CO_PAGESTYLE }
991 lexrc.pushTable(classOptionsTags);
993 while (!getout && lexrc.isOK()) {
994 int le = lexrc.lex();
996 case Lexer::LEX_UNDEF:
997 lexrc.printError("Unknown ClassOption tag `$$Token'");
1005 opt_fontsize_ = rtrim(lexrc.getString());
1009 opt_pagestyle_ = rtrim(lexrc.getString());
1013 if (options_.empty())
1014 options_ = lexrc.getString();
1016 options_ += ',' + lexrc.getString();
1020 class_header_ = subst(lexrc.getString(), """, "\"");
1031 bool TextClass::readCiteEngine(Lexer & lexrc)
1033 int const type = readCiteEngineType(lexrc);
1034 if (type & ENGINE_TYPE_AUTHORYEAR)
1035 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1036 if (type & ENGINE_TYPE_NUMERICAL)
1037 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1038 if (type & ENGINE_TYPE_DEFAULT)
1039 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1041 bool getout = false;
1042 while (!getout && lexrc.isOK()) {
1044 def = lexrc.getString();
1045 def = subst(def, " ", "");
1046 def = subst(def, "\t", "");
1047 if (compare_ascii_no_case(def, "end") == 0) {
1052 char ichar = def[0];
1055 if (isUpperCase(ichar)) {
1056 cs.forceUpperCase = true;
1057 def[0] = lowercase(ichar);
1060 /** For portability reasons (between different
1061 * cite engines such as natbib and biblatex),
1062 * we distinguish between:
1063 * 1. The LyX name as output in the LyX file
1064 * 2. Possible aliases that might fall back to
1065 * the given LyX name in the current engine
1066 * 3. The actual LaTeX command that is output
1067 * (2) and (3) are optional.
1068 * Also, the GUI string for the starred version can
1071 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1080 ScanMode mode = LyXName;
1081 ScanMode oldmode = LyXName;
1086 size_t const n = def.size();
1087 for (size_t i = 0; i != n; ++i) {
1091 else if (ichar == '=')
1093 else if (ichar == '<') {
1096 } else if (ichar == '>')
1098 else if (mode == LaTeXCmd)
1100 else if (mode == StarDesc)
1102 else if (ichar == '$')
1103 cs.hasQualifiedList = true;
1104 else if (ichar == '*')
1105 cs.hasStarredVersion = true;
1106 else if (ichar == '[' && cs.textAfter)
1107 cs.textBefore = true;
1108 else if (ichar == '[')
1109 cs.textAfter = true;
1110 else if (ichar != ']') {
1118 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1119 if (!alias.empty()) {
1120 vector<string> const aliases = getVectorFromString(alias);
1121 for (string const &s: aliases)
1122 cite_command_aliases_[s] = lyx_cmd;
1124 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1125 int size = stardesc.size();
1127 cs.stardesc = stardescs[0];
1129 cs.startooltip = stardescs[1];
1130 if (type & ENGINE_TYPE_AUTHORYEAR)
1131 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1132 if (type & ENGINE_TYPE_NUMERICAL)
1133 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1134 if (type & ENGINE_TYPE_DEFAULT)
1135 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1141 int TextClass::readCiteEngineType(Lexer & lexrc) const
1143 LATTEST(ENGINE_TYPE_DEFAULT ==
1144 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1145 if (!lexrc.next()) {
1146 lexrc.printError("No cite engine type given for token: `$$Token'.");
1147 return ENGINE_TYPE_DEFAULT;
1149 string const type = rtrim(lexrc.getString());
1150 if (compare_ascii_no_case(type, "authoryear") == 0)
1151 return ENGINE_TYPE_AUTHORYEAR;
1152 else if (compare_ascii_no_case(type, "numerical") == 0)
1153 return ENGINE_TYPE_NUMERICAL;
1154 else if (compare_ascii_no_case(type, "default") != 0) {
1155 string const s = "Unknown cite engine type `" + type
1156 + "' given for token: `$$Token',";
1157 lexrc.printError(s);
1159 return ENGINE_TYPE_DEFAULT;
1163 bool TextClass::readCiteFormat(Lexer & lexrc)
1165 int const type = readCiteEngineType(lexrc);
1168 while (lexrc.isOK()) {
1170 etype = lexrc.getString();
1171 if (compare_ascii_no_case(etype, "end") == 0)
1176 definition = lexrc.getString();
1177 char initchar = etype[0];
1178 if (initchar == '#')
1180 if (initchar == '!' || initchar == '_') {
1181 if (type & ENGINE_TYPE_AUTHORYEAR)
1182 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1183 if (type & ENGINE_TYPE_NUMERICAL)
1184 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1185 if (type & ENGINE_TYPE_DEFAULT)
1186 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1188 if (type & ENGINE_TYPE_AUTHORYEAR)
1189 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1190 if (type & ENGINE_TYPE_NUMERICAL)
1191 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1192 if (type & ENGINE_TYPE_DEFAULT)
1193 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1200 bool TextClass::readFloat(Lexer & lexrc)
1217 FT_ALLOWED_PLACEMENT,
1223 LexerKeyword floatTags[] = {
1224 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1225 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1226 { "allowswide", FT_ALLOWS_WIDE },
1228 { "extension", FT_EXT },
1229 { "guiname", FT_NAME },
1230 { "htmlattr", FT_HTMLATTR },
1231 { "htmlstyle", FT_HTMLSTYLE },
1232 { "htmltag", FT_HTMLTAG },
1233 { "ispredefined", FT_PREDEFINED },
1234 { "listcommand", FT_LISTCOMMAND },
1235 { "listname", FT_LISTNAME },
1236 { "numberwithin", FT_WITHIN },
1237 { "placement", FT_PLACEMENT },
1238 { "refprefix", FT_REFPREFIX },
1239 { "style", FT_STYLE },
1240 { "type", FT_TYPE },
1241 { "usesfloatpkg", FT_USESFLOAT }
1244 lexrc.pushTable(floatTags);
1248 docstring htmlstyle;
1254 string allowed_placement = "!htbpH";
1259 bool usesfloat = true;
1260 bool ispredefined = false;
1261 bool allowswide = true;
1262 bool allowssideways = true;
1264 bool getout = false;
1265 while (!getout && lexrc.isOK()) {
1266 int le = lexrc.lex();
1268 case Lexer::LEX_UNDEF:
1269 lexrc.printError("Unknown float tag `$$Token'");
1277 type = lexrc.getString();
1278 if (floatlist_.typeExist(type)) {
1279 Floating const & fl = floatlist_.getType(type);
1280 placement = fl.placement();
1282 within = fl.within();
1285 listname = fl.listName();
1286 usesfloat = fl.usesFloatPkg();
1287 ispredefined = fl.isPredefined();
1288 listcommand = fl.listCommand();
1289 refprefix = fl.refPrefix();
1294 name = lexrc.getString();
1298 placement = lexrc.getString();
1300 case FT_ALLOWED_PLACEMENT:
1302 allowed_placement = lexrc.getString();
1306 ext = lexrc.getString();
1310 within = lexrc.getString();
1311 if (within == "none")
1316 style = lexrc.getString();
1318 case FT_LISTCOMMAND:
1320 listcommand = lexrc.getString();
1324 refprefix = lexrc.getString();
1328 listname = lexrc.getString();
1332 usesfloat = lexrc.getBool();
1336 ispredefined = lexrc.getBool();
1338 case FT_ALLOWS_SIDEWAYS:
1340 allowssideways = lexrc.getBool();
1342 case FT_ALLOWS_WIDE:
1344 allowswide = lexrc.getBool();
1348 htmlattr = lexrc.getString();
1352 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1356 htmltag = lexrc.getString();
1366 // Here we have a full float if getout == true
1368 if (!usesfloat && listcommand.empty()) {
1369 // if this float uses the same auxfile as an existing one,
1370 // there is no need for it to provide a list command.
1371 FloatList::const_iterator it = floatlist_.begin();
1372 FloatList::const_iterator en = floatlist_.end();
1373 bool found_ext = false;
1374 for (; it != en; ++it) {
1375 if (it->second.ext() == ext) {
1381 LYXERR0("The layout does not provide a list command " <<
1382 "for the float `" << type << "'. LyX will " <<
1383 "not be able to produce a float list.");
1385 Floating fl(type, placement, ext, within, style, name,
1386 listname, listcommand, refprefix, allowed_placement,
1387 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1388 allowswide, allowssideways);
1389 floatlist_.newFloat(fl);
1390 // each float has its own counter
1391 counters_.newCounter(from_ascii(type), from_ascii(within),
1392 docstring(), docstring());
1393 // also define sub-float counters
1394 docstring const subtype = "sub-" + from_ascii(type);
1395 counters_.newCounter(subtype, from_ascii(type),
1396 "\\alph{" + subtype + "}", docstring());
1402 bool TextClass::readOutlinerName(Lexer & lexrc)
1407 type = lexrc.getString();
1409 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1413 name = lexrc.getDocString();
1415 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1418 outliner_names_[type] = name;
1423 string const & TextClass::prerequisites(string const & sep) const
1425 if (contains(prerequisites_, ',')) {
1426 vector<string> const pres = getVectorFromString(prerequisites_);
1427 prerequisites_ = getStringFromVector(pres, sep);
1429 return prerequisites_;
1433 bool TextClass::hasLayout(docstring const & n) const
1435 docstring const name = n.empty() ? defaultLayoutName() : n;
1437 return find_if(layoutlist_.begin(), layoutlist_.end(),
1438 LayoutNamesEqual(name))
1439 != layoutlist_.end();
1443 bool TextClass::hasInsetLayout(docstring const & n) const
1447 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1448 return it != insetlayoutlist_.end();
1452 Layout const & TextClass::operator[](docstring const & name) const
1454 LATTEST(!name.empty());
1457 find_if(begin(), end(), LayoutNamesEqual(name));
1460 LYXERR0("We failed to find the layout '" << name
1461 << "' in the layout list. You MUST investigate!");
1462 for (const_iterator cit = begin(); cit != end(); ++cit)
1463 lyxerr << " " << to_utf8(cit->name()) << endl;
1465 // We require the name to exist
1466 static const Layout dummy;
1467 LASSERT(false, return dummy);
1474 Layout & TextClass::operator[](docstring const & name)
1476 LATTEST(!name.empty());
1477 // Safe to continue, given what we do below.
1479 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1482 LYXERR0("We failed to find the layout '" << to_utf8(name)
1483 << "' in the layout list. You MUST investigate!");
1484 for (const_iterator cit = begin(); cit != end(); ++cit)
1485 LYXERR0(" " << to_utf8(cit->name()));
1487 // we require the name to exist
1489 // we are here only in release mode
1490 layoutlist_.push_back(createBasicLayout(name, true));
1491 it = find_if(begin(), end(), LayoutNamesEqual(name));
1498 bool TextClass::deleteLayout(docstring const & name)
1500 if (name == defaultLayoutName() || name == plainLayoutName())
1503 LayoutList::iterator it =
1504 remove_if(layoutlist_.begin(), layoutlist_.end(),
1505 LayoutNamesEqual(name));
1507 LayoutList::iterator end = layoutlist_.end();
1508 bool const ret = (it != end);
1509 layoutlist_.erase(it, end);
1514 bool TextClass::deleteInsetLayout(docstring const & name)
1516 return insetlayoutlist_.erase(name);
1520 // Load textclass info if not loaded yet
1521 bool TextClass::load(string const & path) const
1526 // Read style-file, provided path is searched before the system ones
1527 // If path is a file, it is loaded directly.
1528 FileName layout_file(path);
1529 if (!path.empty() && !layout_file.isReadableFile())
1530 layout_file = FileName(addName(path, name_ + ".layout"));
1531 if (layout_file.empty() || !layout_file.exists())
1532 layout_file = libFileSearch("layouts", name_, "layout");
1533 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1536 lyxerr << "Error reading `"
1537 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1538 << "'\n(Check `" << name_
1539 << "')\nCheck your installation and "
1540 "try Options/Reconfigure..."
1548 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1553 layoutlist_.push_back(createBasicLayout(n, true));
1558 string DocumentClass::forcedLayouts() const
1562 const_iterator const e = end();
1563 for (const_iterator i = begin(); i != e; ++i) {
1564 if (i->forcelocal > 0) {
1566 os << "Format " << LAYOUT_FORMAT << '\n';
1576 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1578 // FIXME The fix for the InsetLayout part of 4812 would be here:
1579 // Add the InsetLayout to the document class if it is not found.
1581 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1582 while (!n.empty()) {
1583 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1584 if (cit != cen && cit->first == n) {
1585 if (cit->second.obsoleted_by().empty())
1587 n = cit->second.obsoleted_by();
1588 return insetLayout(n);
1590 // If we have a generic prefix (e.g., "Note:"),
1591 // try if this one alone is found.
1592 size_t i = n.find(':');
1593 if (i == string::npos)
1597 // Layout "name" not found.
1598 return plainInsetLayout();
1602 InsetLayout const & DocumentClass::plainInsetLayout() {
1603 static const InsetLayout plain_insetlayout_;
1604 return plain_insetlayout_;
1608 docstring const & TextClass::defaultLayoutName() const
1610 return defaultlayout_;
1614 Layout const & TextClass::defaultLayout() const
1616 return operator[](defaultLayoutName());
1620 bool TextClass::isDefaultLayout(Layout const & layout) const
1622 return layout.name() == defaultLayoutName();
1626 bool TextClass::isPlainLayout(Layout const & layout) const
1628 return layout.name() == plainLayoutName();
1632 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1634 static Layout * defaultLayout = NULL;
1636 if (defaultLayout) {
1637 defaultLayout->setUnknown(unknown);
1638 defaultLayout->setName(name);
1639 return *defaultLayout;
1642 static char const * s = "Margin Static\n"
1643 "LatexType Paragraph\n"
1646 "AlignPossible Left, Right, Center\n"
1647 "LabelType No_Label\n"
1649 istringstream ss(s);
1650 Lexer lex(textClassTags);
1652 defaultLayout = new Layout;
1653 defaultLayout->setUnknown(unknown);
1654 defaultLayout->setName(name);
1655 if (!readStyle(lex, *defaultLayout)) {
1656 // The only way this happens is because the hardcoded layout above
1660 return *defaultLayout;
1664 DocumentClassPtr getDocumentClass(
1665 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1666 LayoutModuleList const & celist,
1669 DocumentClassPtr doc_class =
1670 DocumentClassPtr(new DocumentClass(baseClass));
1671 LayoutModuleList::const_iterator it = modlist.begin();
1672 LayoutModuleList::const_iterator en = modlist.end();
1673 for (; it != en; ++it) {
1674 string const modName = *it;
1675 LyXModule * lm = theModuleList[modName];
1677 docstring const msg =
1678 bformat(_("The module %1$s has been requested by\n"
1679 "this document but has not been found in the list of\n"
1680 "available modules. If you recently installed it, you\n"
1681 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1683 frontend::Alert::warning(_("Module not available"), msg);
1686 if (!lm->isAvailable() && !clone) {
1687 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1688 docstring const msg =
1689 bformat(_("The module %1$s requires a package that is not\n"
1690 "available in your LaTeX installation, or a converter that\n"
1691 "you have not installed. LaTeX output may not be possible.\n"
1692 "Missing prerequisites:\n"
1694 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1695 from_utf8(modName), prereqs);
1696 frontend::Alert::warning(_("Package not available"), msg, true);
1698 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1699 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1700 docstring const msg =
1701 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1702 frontend::Alert::warning(_("Read Error"), msg);
1706 LayoutModuleList::const_iterator cit = celist.begin();
1707 LayoutModuleList::const_iterator cen = celist.end();
1708 for (; cit != cen; ++cit) {
1709 string const ceName = *cit;
1710 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1712 docstring const msg =
1713 bformat(_("The cite engine %1$s has been requested by\n"
1714 "this document but has not been found in the list of\n"
1715 "available engines. If you recently installed it, you\n"
1716 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1718 frontend::Alert::warning(_("Cite Engine not available"), msg);
1721 if (!ce->isAvailable() && !clone) {
1722 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1723 docstring const msg =
1724 bformat(_("The cite engine %1$s requires a package that is not\n"
1725 "available in your LaTeX installation, or a converter that\n"
1726 "you have not installed. LaTeX output may not be possible.\n"
1727 "Missing prerequisites:\n"
1729 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1730 from_utf8(ceName), prereqs);
1731 frontend::Alert::warning(_("Package not available"), msg, true);
1733 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1734 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1735 docstring const msg =
1736 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1737 frontend::Alert::warning(_("Read Error"), msg);
1745 /////////////////////////////////////////////////////////////////////////
1749 /////////////////////////////////////////////////////////////////////////
1751 DocumentClass::DocumentClass(LayoutFile const & tc)
1756 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1758 LayoutList::const_iterator it = layoutlist_.begin();
1759 LayoutList::const_iterator end = layoutlist_.end();
1760 for (; it != end; ++it)
1761 if (it->latexname() == lay)
1767 bool DocumentClass::provides(string const & p) const
1769 return provides_.find(p) != provides_.end();
1773 bool DocumentClass::hasTocLevels() const
1775 return min_toclevel_ != Layout::NOT_IN_TOC;
1779 Layout const & DocumentClass::getTOCLayout() const
1781 // we're going to look for the layout with the minimum toclevel
1782 TextClass::LayoutList::const_iterator lit = begin();
1783 TextClass::LayoutList::const_iterator const len = end();
1784 int minlevel = 1000;
1785 Layout const * lay = NULL;
1786 for (; lit != len; ++lit) {
1787 int const level = lit->toclevel;
1788 // we don't want Part or unnumbered sections
1789 if (level == Layout::NOT_IN_TOC || level < 0
1790 || level >= minlevel || lit->counter.empty())
1797 // hmm. that is very odd, so we'll do our best.
1798 return operator[](defaultLayoutName());
1802 Layout const & DocumentClass::htmlTOCLayout() const
1804 if (html_toc_section_.empty())
1805 html_toc_section_ = getTOCLayout().name();
1806 return operator[](html_toc_section_);
1810 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1811 string const & entry, bool const punct, string const & fallback) const
1813 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%]]}";
1815 default_format += ".";
1817 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1818 if (itype == cite_formats_.end())
1819 return default_format;
1820 map<string, string>::const_iterator it = itype->second.find(entry);
1821 if (it == itype->second.end() && !fallback.empty())
1822 it = itype->second.find(fallback);
1823 if (it == itype->second.end())
1824 return default_format;
1826 return it->second + ".";
1831 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1832 string const & macro) const
1834 static string empty;
1835 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1836 if (itype == cite_macros_.end())
1838 map<string, string>::const_iterator it = itype->second.find(macro);
1839 if (it == itype->second.end())
1845 vector<string> const DocumentClass::citeCommands(
1846 CiteEngineType const & type) const
1848 vector<CitationStyle> const styles = citeStyles(type);
1849 vector<CitationStyle>::const_iterator it = styles.begin();
1850 vector<CitationStyle>::const_iterator end = styles.end();
1851 vector<string> cmds;
1852 for (; it != end; ++it) {
1853 CitationStyle const cite = *it;
1854 cmds.push_back(cite.name);
1860 vector<CitationStyle> const & DocumentClass::citeStyles(
1861 CiteEngineType const & type) const
1863 static vector<CitationStyle> empty;
1864 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1865 if (it == cite_styles_.end())
1871 /////////////////////////////////////////////////////////////////////////
1875 /////////////////////////////////////////////////////////////////////////
1877 ostream & operator<<(ostream & os, PageSides p)