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 = 66; //spitz: New layout tag AutoNests
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,
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();
450 has_output_format_ = true;
455 readOutputType(lexrc);
456 switch(outputType_) {
458 outputFormat_ = "latex";
461 outputFormat_ = "docbook";
464 outputFormat_ = "literate";
469 case TC_INPUT: // Include file
472 string const inc = lexrc.getString();
473 if (!path().empty() && (prefixIs(inc, "./") ||
474 prefixIs(inc, "../")))
475 tmp = fileSearch(path(), inc, "layout");
477 tmp = libFileSearch("layouts", inc,
481 lexrc.printError("Could not find input file: " + inc);
483 } else if (!read(tmp, MERGE)) {
484 lexrc.printError("Error reading input file: " + tmp.absFileName());
490 case TC_DEFAULTSTYLE:
492 docstring const name = from_utf8(subst(lexrc.getString(),
494 defaultlayout_ = name;
501 case TC_PROVIDESTYLE:
502 // if modifystyle is true, then we got here by falling through
503 // so we are not in an ProvideStyle block
509 lexrc.printError("No name given for style: `$$Token'.");
513 docstring const name = from_utf8(subst(lexrc.getString(),
516 string s = "Could not read name for style: `$$Token' "
517 + lexrc.getString() + " is probably not valid UTF-8!";
520 // Since we couldn't read the name, we just scan the rest
521 // of the style and discard it.
522 error = !readStyle(lexrc, lay);
526 bool const have_layout = hasLayout(name);
528 // If the layout already exists, then we want to add it to
529 // the existing layout, as long as we are not in an ProvideStyle
531 if (have_layout && !providestyle) {
532 Layout & lay = operator[](name);
533 error = !readStyle(lexrc, lay);
535 // If the layout does not exist, then we want to create a new
536 // one, but not if we are in a ModifyStyle block.
537 else if (!have_layout && !modifystyle) {
539 layout.setName(name);
540 error = !readStyle(lexrc, layout);
542 layoutlist_.push_back(layout);
544 if (defaultlayout_.empty()) {
545 // We do not have a default layout yet, so we choose
546 // the first layout we encounter.
547 defaultlayout_ = name;
550 // There are two ways to get here:
551 // (i) The layout exists but we are in an ProvideStyle block
552 // (ii) The layout doesn't exist, but we are in an ModifyStyle
554 // Either way, we just scan the rest and discard it
557 // signal to coverity that we do not care about the result
558 (void)readStyle(lexrc, lay);
565 docstring const style = from_utf8(subst(lexrc.getString(),
567 if (!deleteLayout(style))
568 lyxerr << "Cannot delete style `"
569 << to_utf8(style) << '\'' << endl;
573 case TC_NOINSETLAYOUT:
575 docstring const style = from_utf8(subst(lexrc.getString(),
577 if (!deleteInsetLayout(style))
578 LYXERR0("Style `" << style << "' cannot be removed\n"
579 "because it was not found!");
585 columns_ = lexrc.getInteger();
590 switch (lexrc.getInteger()) {
591 case 1: sides_ = OneSide; break;
592 case 2: sides_ = TwoSides; break;
594 lyxerr << "Impossible number of page"
595 " sides, setting to one."
605 pagestyle_ = rtrim(lexrc.getString());
609 defaultfont_ = lyxRead(lexrc);
610 if (!defaultfont_.resolved()) {
611 lexrc.printError("Warning: defaultfont should "
612 "be fully instantiated!");
613 defaultfont_.realize(sane_font);
619 secnumdepth_ = lexrc.getInteger();
624 tocdepth_ = lexrc.getInteger();
627 // First step to support options
628 case TC_CLASSOPTIONS:
629 readClassOptions(lexrc);
633 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
636 case TC_HTMLPREAMBLE:
637 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
641 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
644 case TC_HTMLTOCSECTION:
645 html_toc_section_ = from_utf8(trim(lexrc.getString()));
648 case TC_ADDTOPREAMBLE:
649 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
652 case TC_ADDTOHTMLPREAMBLE:
653 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
656 case TC_ADDTOHTMLSTYLES:
657 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
662 string const feature = lexrc.getString();
664 if (lexrc.getInteger())
665 provides_.insert(feature);
667 provides_.erase(feature);
673 vector<string> const req
674 = getVectorFromString(lexrc.getString());
675 requires_.insert(req.begin(), req.end());
681 string const pkg = lexrc.getString();
683 string const options = lexrc.getString();
684 package_options_[pkg] = options;
688 case TC_DEFAULTMODULE: {
690 string const module = lexrc.getString();
691 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
692 default_modules_.push_back(module);
696 case TC_PROVIDESMODULE: {
698 string const module = lexrc.getString();
699 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
700 provided_modules_.push_back(module);
704 case TC_EXCLUDESMODULE: {
706 string const module = lexrc.getString();
707 // modules already have their own way to exclude other modules
709 LYXERR0("ExcludesModule tag cannot be used in a module!");
712 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
713 excluded_modules_.push_back(module);
717 case TC_LEFTMARGIN: // left margin type
719 leftmargin_ = lexrc.getDocString();
722 case TC_RIGHTMARGIN: // right margin type
724 rightmargin_ = lexrc.getDocString();
727 case TC_INSETLAYOUT: {
729 lexrc.printError("No name given for InsetLayout: `$$Token'.");
733 docstring const name = subst(lexrc.getDocString(), '_', ' ');
734 bool const validating = (rt == VALIDATION);
736 string s = "Could not read name for InsetLayout: `$$Token' "
737 + lexrc.getString() + " is probably not valid UTF-8!";
740 // Since we couldn't read the name, we just scan the rest
741 // of the style and discard it.
742 il.read(lexrc, *this);
743 // Let's try to continue rather than abort, unless we're validating
744 // in which case we want to report the error
747 } else if (hasInsetLayout(name)) {
748 InsetLayout & il = insetlayoutlist_[name];
749 error = !il.read(lexrc, *this, validating);
753 error = !il.read(lexrc, *this, validating);
755 insetlayoutlist_[name] = il;
761 error = !readFloat(lexrc);
765 error = !readCiteEngine(lexrc);
768 case TC_CITEENGINETYPE:
770 opt_enginetype_ = rtrim(lexrc.getString());
774 error = !readCiteFormat(lexrc);
777 case TC_CITEFRAMEWORK:
779 citeframework_ = rtrim(lexrc.getString());
782 case TC_MAXCITENAMES:
784 maxcitenames_ = size_t(lexrc.getInteger());
787 case TC_DEFAULTBIBLIO:
789 vector<string> const dbs =
790 getVectorFromString(rtrim(lexrc.getString()), "|");
791 vector<string>::const_iterator it = dbs.begin();
792 vector<string>::const_iterator end = dbs.end();
793 for (; it != end; ++it) {
794 if (!contains(*it, ':')) {
795 vector<string> const enginetypes =
796 getVectorFromString(opt_enginetype_, "|");
797 for (string const &s: enginetypes)
798 cite_default_biblio_style_[s] = *it;
801 string const db = split(*it, eng, ':');
802 cite_default_biblio_style_[eng] = db;
808 case TC_FULLAUTHORLIST:
810 cite_full_author_list_ &= lexrc.getBool();
815 docstring const cnt = lexrc.getDocString();
816 if (!counters_.remove(cnt))
817 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
826 docstring const name = lexrc.getDocString();
828 string s = "Could not read name for counter: `$$Token' "
829 + lexrc.getString() + " is probably not valid UTF-8!";
830 lexrc.printError(s.c_str());
832 // Since we couldn't read the name, we just scan the rest
836 error = !counters_.read(lexrc, name, !ifcounter);
839 lexrc.printError("No name given for style: `$$Token'.");
844 case TC_TITLELATEXTYPE:
845 readTitleType(lexrc);
848 case TC_TITLELATEXNAME:
850 titlename_ = lexrc.getString();
855 string const nofloat = lexrc.getString();
856 floatlist_.erase(nofloat);
860 case TC_OUTLINERNAME:
861 error = !readOutlinerName(lexrc);
866 // at present, we abort if we encounter an error,
867 // so there is no point continuing.
874 if (defaultlayout_.empty()) {
875 LYXERR0("Error: Textclass '" << name_
876 << "' is missing a defaultstyle.");
880 // Try to erase "stdinsets" from the provides_ set.
882 // Provides stdinsets 1
883 // declaration simply tells us that the standard insets have been
884 // defined. (It's found in stdinsets.inc but could also be used in
885 // user-defined files.) There isn't really any such package. So we
886 // might as well go ahead and erase it.
887 // If we do not succeed, then it was not there, which means that
888 // the textclass did not provide the definitions of the standard
889 // insets. So we need to try to load them.
890 int erased = provides_.erase("stdinsets");
892 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
895 frontend::Alert::warning(_("Missing File"),
896 _("Could not find stdinsets.inc! This may lead to data loss!"));
898 } else if (!read(tmp, MERGE)) {
899 frontend::Alert::warning(_("Corrupt File"),
900 _("Could not read stdinsets.inc! This may lead to data loss!"));
905 min_toclevel_ = Layout::NOT_IN_TOC;
906 max_toclevel_ = Layout::NOT_IN_TOC;
907 const_iterator lit = begin();
908 const_iterator len = end();
909 for (; lit != len; ++lit) {
910 int const toclevel = lit->toclevel;
911 if (toclevel != Layout::NOT_IN_TOC) {
912 if (min_toclevel_ == Layout::NOT_IN_TOC)
913 min_toclevel_ = toclevel;
915 min_toclevel_ = min(min_toclevel_, toclevel);
916 max_toclevel_ = max(max_toclevel_, toclevel);
919 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
920 << ", maximum is " << max_toclevel_);
922 return (error ? ERROR : OK);
926 void TextClass::readTitleType(Lexer & lexrc)
928 LexerKeyword titleTypeTags[] = {
929 { "commandafter", TITLE_COMMAND_AFTER },
930 { "environment", TITLE_ENVIRONMENT }
933 PushPopHelper pph(lexrc, titleTypeTags);
935 int le = lexrc.lex();
937 case Lexer::LEX_UNDEF:
938 lexrc.printError("Unknown output type `$$Token'");
940 case TITLE_COMMAND_AFTER:
941 case TITLE_ENVIRONMENT:
942 titletype_ = static_cast<TitleLatexType>(le);
945 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
951 void TextClass::readOutputType(Lexer & lexrc)
953 LexerKeyword outputTypeTags[] = {
954 { "docbook", DOCBOOK },
956 { "literate", LITERATE }
959 PushPopHelper pph(lexrc, outputTypeTags);
961 int le = lexrc.lex();
963 case Lexer::LEX_UNDEF:
964 lexrc.printError("Unknown output type `$$Token'");
969 outputType_ = static_cast<OutputType>(le);
972 LYXERR0("Unhandled value " << le);
978 void TextClass::readClassOptions(Lexer & lexrc)
988 LexerKeyword classOptionsTags[] = {
990 {"fontsize", CO_FONTSIZE },
991 {"header", CO_HEADER },
992 {"other", CO_OTHER },
993 {"pagestyle", CO_PAGESTYLE }
996 lexrc.pushTable(classOptionsTags);
998 while (!getout && lexrc.isOK()) {
999 int le = lexrc.lex();
1001 case Lexer::LEX_UNDEF:
1002 lexrc.printError("Unknown ClassOption tag `$$Token'");
1010 opt_fontsize_ = rtrim(lexrc.getString());
1014 opt_pagestyle_ = rtrim(lexrc.getString());
1018 if (options_.empty())
1019 options_ = lexrc.getString();
1021 options_ += ',' + lexrc.getString();
1025 class_header_ = subst(lexrc.getString(), """, "\"");
1036 bool TextClass::readCiteEngine(Lexer & lexrc)
1038 int const type = readCiteEngineType(lexrc);
1039 if (type & ENGINE_TYPE_AUTHORYEAR)
1040 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1041 if (type & ENGINE_TYPE_NUMERICAL)
1042 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1043 if (type & ENGINE_TYPE_DEFAULT)
1044 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1046 bool getout = false;
1047 while (!getout && lexrc.isOK()) {
1049 def = lexrc.getString();
1050 def = subst(def, " ", "");
1051 def = subst(def, "\t", "");
1052 if (compare_ascii_no_case(def, "end") == 0) {
1057 char ichar = def[0];
1060 if (isUpperCase(ichar)) {
1061 cs.forceUpperCase = true;
1062 def[0] = lowercase(ichar);
1065 /** For portability reasons (between different
1066 * cite engines such as natbib and biblatex),
1067 * we distinguish between:
1068 * 1. The LyX name as output in the LyX file
1069 * 2. Possible aliases that might fall back to
1070 * the given LyX name in the current engine
1071 * 3. The actual LaTeX command that is output
1072 * (2) and (3) are optional.
1073 * Also, the GUI string for the starred version can
1076 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1085 ScanMode mode = LyXName;
1086 ScanMode oldmode = LyXName;
1091 size_t const n = def.size();
1092 for (size_t i = 0; i != n; ++i) {
1096 else if (ichar == '=')
1098 else if (ichar == '<') {
1101 } else if (ichar == '>')
1103 else if (mode == LaTeXCmd)
1105 else if (mode == StarDesc)
1107 else if (ichar == '$')
1108 cs.hasQualifiedList = true;
1109 else if (ichar == '*')
1110 cs.hasStarredVersion = true;
1111 else if (ichar == '[' && cs.textAfter)
1112 cs.textBefore = true;
1113 else if (ichar == '[')
1114 cs.textAfter = true;
1115 else if (ichar != ']') {
1123 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1124 if (!alias.empty()) {
1125 vector<string> const aliases = getVectorFromString(alias);
1126 for (string const &s: aliases)
1127 cite_command_aliases_[s] = lyx_cmd;
1129 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1130 int size = stardesc.size();
1132 cs.stardesc = stardescs[0];
1134 cs.startooltip = stardescs[1];
1135 if (type & ENGINE_TYPE_AUTHORYEAR)
1136 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1137 if (type & ENGINE_TYPE_NUMERICAL)
1138 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1139 if (type & ENGINE_TYPE_DEFAULT)
1140 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1146 int TextClass::readCiteEngineType(Lexer & lexrc) const
1148 LATTEST(ENGINE_TYPE_DEFAULT ==
1149 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1150 if (!lexrc.next()) {
1151 lexrc.printError("No cite engine type given for token: `$$Token'.");
1152 return ENGINE_TYPE_DEFAULT;
1154 string const type = rtrim(lexrc.getString());
1155 if (compare_ascii_no_case(type, "authoryear") == 0)
1156 return ENGINE_TYPE_AUTHORYEAR;
1157 else if (compare_ascii_no_case(type, "numerical") == 0)
1158 return ENGINE_TYPE_NUMERICAL;
1159 else if (compare_ascii_no_case(type, "default") != 0) {
1160 string const s = "Unknown cite engine type `" + type
1161 + "' given for token: `$$Token',";
1162 lexrc.printError(s);
1164 return ENGINE_TYPE_DEFAULT;
1168 bool TextClass::readCiteFormat(Lexer & lexrc)
1170 int const type = readCiteEngineType(lexrc);
1173 while (lexrc.isOK()) {
1175 etype = lexrc.getString();
1176 if (compare_ascii_no_case(etype, "end") == 0)
1181 definition = lexrc.getString();
1182 char initchar = etype[0];
1183 if (initchar == '#')
1185 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1186 if (type & ENGINE_TYPE_AUTHORYEAR)
1187 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1188 if (type & ENGINE_TYPE_NUMERICAL)
1189 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1190 if (type & ENGINE_TYPE_DEFAULT)
1191 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1193 if (type & ENGINE_TYPE_AUTHORYEAR)
1194 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1195 if (type & ENGINE_TYPE_NUMERICAL)
1196 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1197 if (type & ENGINE_TYPE_DEFAULT)
1198 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1205 bool TextClass::readFloat(Lexer & lexrc)
1222 FT_ALLOWED_PLACEMENT,
1228 LexerKeyword floatTags[] = {
1229 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1230 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1231 { "allowswide", FT_ALLOWS_WIDE },
1233 { "extension", FT_EXT },
1234 { "guiname", FT_NAME },
1235 { "htmlattr", FT_HTMLATTR },
1236 { "htmlstyle", FT_HTMLSTYLE },
1237 { "htmltag", FT_HTMLTAG },
1238 { "ispredefined", FT_PREDEFINED },
1239 { "listcommand", FT_LISTCOMMAND },
1240 { "listname", FT_LISTNAME },
1241 { "numberwithin", FT_WITHIN },
1242 { "placement", FT_PLACEMENT },
1243 { "refprefix", FT_REFPREFIX },
1244 { "style", FT_STYLE },
1245 { "type", FT_TYPE },
1246 { "usesfloatpkg", FT_USESFLOAT }
1249 lexrc.pushTable(floatTags);
1253 docstring htmlstyle;
1259 string allowed_placement = "!htbpH";
1264 bool usesfloat = true;
1265 bool ispredefined = false;
1266 bool allowswide = true;
1267 bool allowssideways = true;
1269 bool getout = false;
1270 while (!getout && lexrc.isOK()) {
1271 int le = lexrc.lex();
1273 case Lexer::LEX_UNDEF:
1274 lexrc.printError("Unknown float tag `$$Token'");
1282 type = lexrc.getString();
1283 if (floatlist_.typeExist(type)) {
1284 Floating const & fl = floatlist_.getType(type);
1285 placement = fl.placement();
1287 within = fl.within();
1290 listname = fl.listName();
1291 usesfloat = fl.usesFloatPkg();
1292 ispredefined = fl.isPredefined();
1293 listcommand = fl.listCommand();
1294 refprefix = fl.refPrefix();
1299 name = lexrc.getString();
1303 placement = lexrc.getString();
1305 case FT_ALLOWED_PLACEMENT:
1307 allowed_placement = lexrc.getString();
1311 ext = lexrc.getString();
1315 within = lexrc.getString();
1316 if (within == "none")
1321 style = lexrc.getString();
1323 case FT_LISTCOMMAND:
1325 listcommand = lexrc.getString();
1329 refprefix = lexrc.getString();
1333 listname = lexrc.getString();
1337 usesfloat = lexrc.getBool();
1341 ispredefined = lexrc.getBool();
1343 case FT_ALLOWS_SIDEWAYS:
1345 allowssideways = lexrc.getBool();
1347 case FT_ALLOWS_WIDE:
1349 allowswide = lexrc.getBool();
1353 htmlattr = lexrc.getString();
1357 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1361 htmltag = lexrc.getString();
1371 // Here we have a full float if getout == true
1373 if (!usesfloat && listcommand.empty()) {
1374 // if this float uses the same auxfile as an existing one,
1375 // there is no need for it to provide a list command.
1376 FloatList::const_iterator it = floatlist_.begin();
1377 FloatList::const_iterator en = floatlist_.end();
1378 bool found_ext = false;
1379 for (; it != en; ++it) {
1380 if (it->second.ext() == ext) {
1386 LYXERR0("The layout does not provide a list command " <<
1387 "for the float `" << type << "'. LyX will " <<
1388 "not be able to produce a float list.");
1390 Floating fl(type, placement, ext, within, style, name,
1391 listname, listcommand, refprefix, allowed_placement,
1392 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1393 allowswide, allowssideways);
1394 floatlist_.newFloat(fl);
1395 // each float has its own counter
1396 counters_.newCounter(from_ascii(type), from_ascii(within),
1397 docstring(), docstring());
1398 // also define sub-float counters
1399 docstring const subtype = "sub-" + from_ascii(type);
1400 counters_.newCounter(subtype, from_ascii(type),
1401 "\\alph{" + subtype + "}", docstring());
1407 bool TextClass::readOutlinerName(Lexer & lexrc)
1412 type = lexrc.getString();
1414 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1418 name = lexrc.getDocString();
1420 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1423 outliner_names_[type] = name;
1428 string const & TextClass::prerequisites(string const & sep) const
1430 if (contains(prerequisites_, ',')) {
1431 vector<string> const pres = getVectorFromString(prerequisites_);
1432 prerequisites_ = getStringFromVector(pres, sep);
1434 return prerequisites_;
1438 bool TextClass::hasLayout(docstring const & n) const
1440 docstring const name = n.empty() ? defaultLayoutName() : n;
1442 return find_if(layoutlist_.begin(), layoutlist_.end(),
1443 LayoutNamesEqual(name))
1444 != layoutlist_.end();
1448 bool TextClass::hasInsetLayout(docstring const & n) const
1452 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1453 return it != insetlayoutlist_.end();
1457 Layout const & TextClass::operator[](docstring const & name) const
1459 LATTEST(!name.empty());
1462 find_if(begin(), end(), LayoutNamesEqual(name));
1465 LYXERR0("We failed to find the layout '" << name
1466 << "' in the layout list. You MUST investigate!");
1467 for (const_iterator cit = begin(); cit != end(); ++cit)
1468 lyxerr << " " << to_utf8(cit->name()) << endl;
1470 // We require the name to exist
1471 static const Layout dummy;
1472 LASSERT(false, return dummy);
1479 Layout & TextClass::operator[](docstring const & name)
1481 LATTEST(!name.empty());
1482 // Safe to continue, given what we do below.
1484 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1487 LYXERR0("We failed to find the layout '" << to_utf8(name)
1488 << "' in the layout list. You MUST investigate!");
1489 for (const_iterator cit = begin(); cit != end(); ++cit)
1490 LYXERR0(" " << to_utf8(cit->name()));
1492 // we require the name to exist
1494 // we are here only in release mode
1495 layoutlist_.push_back(createBasicLayout(name, true));
1496 it = find_if(begin(), end(), LayoutNamesEqual(name));
1503 bool TextClass::deleteLayout(docstring const & name)
1505 if (name == defaultLayoutName() || name == plainLayoutName())
1508 LayoutList::iterator it =
1509 remove_if(layoutlist_.begin(), layoutlist_.end(),
1510 LayoutNamesEqual(name));
1512 LayoutList::iterator end = layoutlist_.end();
1513 bool const ret = (it != end);
1514 layoutlist_.erase(it, end);
1519 bool TextClass::deleteInsetLayout(docstring const & name)
1521 return insetlayoutlist_.erase(name);
1525 // Load textclass info if not loaded yet
1526 bool TextClass::load(string const & path) const
1531 // Read style-file, provided path is searched before the system ones
1532 // If path is a file, it is loaded directly.
1533 FileName layout_file(path);
1534 if (!path.empty() && !layout_file.isReadableFile())
1535 layout_file = FileName(addName(path, name_ + ".layout"));
1536 if (layout_file.empty() || !layout_file.exists())
1537 layout_file = libFileSearch("layouts", name_, "layout");
1538 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1541 lyxerr << "Error reading `"
1542 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1543 << "'\n(Check `" << name_
1544 << "')\nCheck your installation and "
1545 "try Options/Reconfigure..."
1553 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1558 layoutlist_.push_back(createBasicLayout(n, true));
1563 string DocumentClass::forcedLayouts() const
1567 const_iterator const e = end();
1568 for (const_iterator i = begin(); i != e; ++i) {
1569 if (i->forcelocal > 0) {
1571 os << "Format " << LAYOUT_FORMAT << '\n';
1581 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1583 // FIXME The fix for the InsetLayout part of 4812 would be here:
1584 // Add the InsetLayout to the document class if it is not found.
1586 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1587 while (!n.empty()) {
1588 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1589 if (cit != cen && cit->first == n) {
1590 if (cit->second.obsoleted_by().empty())
1592 n = cit->second.obsoleted_by();
1593 return insetLayout(n);
1595 // If we have a generic prefix (e.g., "Note:"),
1596 // try if this one alone is found.
1597 size_t i = n.find(':');
1598 if (i == string::npos)
1602 // Layout "name" not found.
1603 return plainInsetLayout();
1607 InsetLayout const & DocumentClass::plainInsetLayout() {
1608 static const InsetLayout plain_insetlayout_;
1609 return plain_insetlayout_;
1613 docstring const & TextClass::defaultLayoutName() const
1615 return defaultlayout_;
1619 Layout const & TextClass::defaultLayout() const
1621 return operator[](defaultLayoutName());
1625 bool TextClass::isDefaultLayout(Layout const & layout) const
1627 return layout.name() == defaultLayoutName();
1631 bool TextClass::isPlainLayout(Layout const & layout) const
1633 return layout.name() == plainLayoutName();
1637 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1639 static Layout * defaultLayout = NULL;
1641 if (defaultLayout) {
1642 defaultLayout->setUnknown(unknown);
1643 defaultLayout->setName(name);
1644 return *defaultLayout;
1647 static char const * s = "Margin Static\n"
1648 "LatexType Paragraph\n"
1651 "AlignPossible Left, Right, Center\n"
1652 "LabelType No_Label\n"
1654 istringstream ss(s);
1655 Lexer lex(textClassTags);
1657 defaultLayout = new Layout;
1658 defaultLayout->setUnknown(unknown);
1659 defaultLayout->setName(name);
1660 if (!readStyle(lex, *defaultLayout)) {
1661 // The only way this happens is because the hardcoded layout above
1665 return *defaultLayout;
1669 DocumentClassPtr getDocumentClass(
1670 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1671 LayoutModuleList const & celist,
1674 DocumentClassPtr doc_class =
1675 DocumentClassPtr(new DocumentClass(baseClass));
1676 LayoutModuleList::const_iterator it = modlist.begin();
1677 LayoutModuleList::const_iterator en = modlist.end();
1678 for (; it != en; ++it) {
1679 string const modName = *it;
1680 LyXModule * lm = theModuleList[modName];
1682 docstring const msg =
1683 bformat(_("The module %1$s has been requested by\n"
1684 "this document but has not been found in the list of\n"
1685 "available modules. If you recently installed it, you\n"
1686 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1688 frontend::Alert::warning(_("Module not available"), msg);
1691 if (!lm->isAvailable() && !clone) {
1692 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1693 docstring const msg =
1694 bformat(_("The module %1$s requires a package that is not\n"
1695 "available in your LaTeX installation, or a converter that\n"
1696 "you have not installed. LaTeX output may not be possible.\n"
1697 "Missing prerequisites:\n"
1699 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1700 from_utf8(modName), prereqs);
1701 frontend::Alert::warning(_("Package not available"), msg, true);
1703 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1704 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1705 docstring const msg =
1706 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1707 frontend::Alert::warning(_("Read Error"), msg);
1711 LayoutModuleList::const_iterator cit = celist.begin();
1712 LayoutModuleList::const_iterator cen = celist.end();
1713 for (; cit != cen; ++cit) {
1714 string const ceName = *cit;
1715 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1717 docstring const msg =
1718 bformat(_("The cite engine %1$s has been requested by\n"
1719 "this document but has not been found in the list of\n"
1720 "available engines. If you recently installed it, you\n"
1721 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1723 frontend::Alert::warning(_("Cite Engine not available"), msg);
1726 if (!ce->isAvailable() && !clone) {
1727 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1728 docstring const msg =
1729 bformat(_("The cite engine %1$s requires a package that is not\n"
1730 "available in your LaTeX installation, or a converter that\n"
1731 "you have not installed. LaTeX output may not be possible.\n"
1732 "Missing prerequisites:\n"
1734 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1735 from_utf8(ceName), prereqs);
1736 frontend::Alert::warning(_("Package not available"), msg, true);
1738 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1739 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1740 docstring const msg =
1741 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1742 frontend::Alert::warning(_("Read Error"), msg);
1750 /////////////////////////////////////////////////////////////////////////
1754 /////////////////////////////////////////////////////////////////////////
1756 DocumentClass::DocumentClass(LayoutFile const & tc)
1761 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1763 LayoutList::const_iterator it = layoutlist_.begin();
1764 LayoutList::const_iterator end = layoutlist_.end();
1765 for (; it != end; ++it)
1766 if (it->latexname() == lay)
1772 bool DocumentClass::provides(string const & p) const
1774 return provides_.find(p) != provides_.end();
1778 bool DocumentClass::hasTocLevels() const
1780 return min_toclevel_ != Layout::NOT_IN_TOC;
1784 Layout const & DocumentClass::getTOCLayout() const
1786 // we're going to look for the layout with the minimum toclevel
1787 TextClass::LayoutList::const_iterator lit = begin();
1788 TextClass::LayoutList::const_iterator const len = end();
1789 int minlevel = 1000;
1790 Layout const * lay = NULL;
1791 for (; lit != len; ++lit) {
1792 int const level = lit->toclevel;
1793 // we don't want Part or unnumbered sections
1794 if (level == Layout::NOT_IN_TOC || level < 0
1795 || level >= minlevel || lit->counter.empty())
1802 // hmm. that is very odd, so we'll do our best.
1803 return operator[](defaultLayoutName());
1807 Layout const & DocumentClass::htmlTOCLayout() const
1809 if (html_toc_section_.empty())
1810 html_toc_section_ = getTOCLayout().name();
1811 return operator[](html_toc_section_);
1815 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1816 string const & entry, bool const punct, string const & fallback) const
1818 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%]]}";
1820 default_format += ".";
1822 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1823 if (itype == cite_formats_.end())
1824 return default_format;
1825 map<string, string>::const_iterator it = itype->second.find(entry);
1826 if (it == itype->second.end() && !fallback.empty())
1827 it = itype->second.find(fallback);
1828 if (it == itype->second.end())
1829 return default_format;
1831 return it->second + ".";
1836 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1837 string const & macro) const
1839 static string empty;
1840 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1841 if (itype == cite_macros_.end())
1843 map<string, string>::const_iterator it = itype->second.find(macro);
1844 if (it == itype->second.end())
1850 vector<string> const DocumentClass::citeCommands(
1851 CiteEngineType const & type) const
1853 vector<CitationStyle> const styles = citeStyles(type);
1854 vector<CitationStyle>::const_iterator it = styles.begin();
1855 vector<CitationStyle>::const_iterator end = styles.end();
1856 vector<string> cmds;
1857 for (; it != end; ++it) {
1858 CitationStyle const cite = *it;
1859 cmds.push_back(cite.name);
1865 vector<CitationStyle> const & DocumentClass::citeStyles(
1866 CiteEngineType const & type) const
1868 static vector<CitationStyle> empty;
1869 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1870 if (it == cite_styles_.end())
1876 /////////////////////////////////////////////////////////////////////////
1880 /////////////////////////////////////////////////////////////////////////
1882 ostream & operator<<(ostream & os, PageSides p)