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 = 67; //spitz: New layout tag NeedsCProtect
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(), '_', ' ');
735 string s = "Could not read name for InsetLayout: `$$Token' "
736 + lexrc.getString() + " is probably not valid UTF-8!";
739 // Since we couldn't read the name, we just scan the rest
740 // of the style and discard it.
741 il.read(lexrc, *this);
742 // Let's try to continue rather than abort.
744 } else if (hasInsetLayout(name)) {
745 InsetLayout & il = insetlayoutlist_[name];
746 error = !il.read(lexrc, *this);
750 error = !il.read(lexrc, *this);
752 insetlayoutlist_[name] = il;
758 error = !readFloat(lexrc);
762 error = !readCiteEngine(lexrc);
765 case TC_CITEENGINETYPE:
767 opt_enginetype_ = rtrim(lexrc.getString());
771 error = !readCiteFormat(lexrc);
774 case TC_CITEFRAMEWORK:
776 citeframework_ = rtrim(lexrc.getString());
779 case TC_MAXCITENAMES:
781 maxcitenames_ = size_t(lexrc.getInteger());
784 case TC_DEFAULTBIBLIO:
786 vector<string> const dbs =
787 getVectorFromString(rtrim(lexrc.getString()), "|");
788 vector<string>::const_iterator it = dbs.begin();
789 vector<string>::const_iterator end = dbs.end();
790 for (; it != end; ++it) {
791 if (!contains(*it, ':')) {
792 vector<string> const enginetypes =
793 getVectorFromString(opt_enginetype_, "|");
794 for (string const &s: enginetypes)
795 cite_default_biblio_style_[s] = *it;
798 string const db = split(*it, eng, ':');
799 cite_default_biblio_style_[eng] = db;
805 case TC_FULLAUTHORLIST:
807 cite_full_author_list_ &= lexrc.getBool();
812 docstring const cnt = lexrc.getDocString();
813 if (!counters_.remove(cnt))
814 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
823 docstring const name = lexrc.getDocString();
825 string s = "Could not read name for counter: `$$Token' "
826 + lexrc.getString() + " is probably not valid UTF-8!";
827 lexrc.printError(s.c_str());
829 // Since we couldn't read the name, we just scan the rest
833 error = !counters_.read(lexrc, name, !ifcounter);
836 lexrc.printError("No name given for style: `$$Token'.");
841 case TC_TITLELATEXTYPE:
842 readTitleType(lexrc);
845 case TC_TITLELATEXNAME:
847 titlename_ = lexrc.getString();
852 string const nofloat = lexrc.getString();
853 floatlist_.erase(nofloat);
857 case TC_OUTLINERNAME:
858 error = !readOutlinerName(lexrc);
863 // at present, we abort if we encounter an error,
864 // so there is no point continuing.
871 if (defaultlayout_.empty()) {
872 LYXERR0("Error: Textclass '" << name_
873 << "' is missing a defaultstyle.");
877 // Try to erase "stdinsets" from the provides_ set.
879 // Provides stdinsets 1
880 // declaration simply tells us that the standard insets have been
881 // defined. (It's found in stdinsets.inc but could also be used in
882 // user-defined files.) There isn't really any such package. So we
883 // might as well go ahead and erase it.
884 // If we do not succeed, then it was not there, which means that
885 // the textclass did not provide the definitions of the standard
886 // insets. So we need to try to load them.
887 int erased = provides_.erase("stdinsets");
889 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
892 frontend::Alert::warning(_("Missing File"),
893 _("Could not find stdinsets.inc! This may lead to data loss!"));
895 } else if (!read(tmp, MERGE)) {
896 frontend::Alert::warning(_("Corrupt File"),
897 _("Could not read stdinsets.inc! This may lead to data loss!"));
902 min_toclevel_ = Layout::NOT_IN_TOC;
903 max_toclevel_ = Layout::NOT_IN_TOC;
904 const_iterator lit = begin();
905 const_iterator len = end();
906 for (; lit != len; ++lit) {
907 int const toclevel = lit->toclevel;
908 if (toclevel != Layout::NOT_IN_TOC) {
909 if (min_toclevel_ == Layout::NOT_IN_TOC)
910 min_toclevel_ = toclevel;
912 min_toclevel_ = min(min_toclevel_, toclevel);
913 max_toclevel_ = max(max_toclevel_, toclevel);
916 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
917 << ", maximum is " << max_toclevel_);
919 return (error ? ERROR : OK);
923 void TextClass::readTitleType(Lexer & lexrc)
925 LexerKeyword titleTypeTags[] = {
926 { "commandafter", TITLE_COMMAND_AFTER },
927 { "environment", TITLE_ENVIRONMENT }
930 PushPopHelper pph(lexrc, titleTypeTags);
932 int le = lexrc.lex();
934 case Lexer::LEX_UNDEF:
935 lexrc.printError("Unknown output type `$$Token'");
937 case TITLE_COMMAND_AFTER:
938 case TITLE_ENVIRONMENT:
939 titletype_ = static_cast<TitleLatexType>(le);
942 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
948 void TextClass::readOutputType(Lexer & lexrc)
950 LexerKeyword outputTypeTags[] = {
951 { "docbook", DOCBOOK },
953 { "literate", LITERATE }
956 PushPopHelper pph(lexrc, outputTypeTags);
958 int le = lexrc.lex();
960 case Lexer::LEX_UNDEF:
961 lexrc.printError("Unknown output type `$$Token'");
966 outputType_ = static_cast<OutputType>(le);
969 LYXERR0("Unhandled value " << le);
975 void TextClass::readClassOptions(Lexer & lexrc)
985 LexerKeyword classOptionsTags[] = {
987 {"fontsize", CO_FONTSIZE },
988 {"header", CO_HEADER },
989 {"other", CO_OTHER },
990 {"pagestyle", CO_PAGESTYLE }
993 lexrc.pushTable(classOptionsTags);
995 while (!getout && lexrc.isOK()) {
996 int le = lexrc.lex();
998 case Lexer::LEX_UNDEF:
999 lexrc.printError("Unknown ClassOption tag `$$Token'");
1007 opt_fontsize_ = rtrim(lexrc.getString());
1011 opt_pagestyle_ = rtrim(lexrc.getString());
1015 if (options_.empty())
1016 options_ = lexrc.getString();
1018 options_ += ',' + lexrc.getString();
1022 class_header_ = subst(lexrc.getString(), """, "\"");
1033 bool TextClass::readCiteEngine(Lexer & lexrc)
1035 int const type = readCiteEngineType(lexrc);
1036 if (type & ENGINE_TYPE_AUTHORYEAR)
1037 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1038 if (type & ENGINE_TYPE_NUMERICAL)
1039 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1040 if (type & ENGINE_TYPE_DEFAULT)
1041 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1043 bool getout = false;
1044 while (!getout && lexrc.isOK()) {
1046 def = lexrc.getString();
1047 def = subst(def, " ", "");
1048 def = subst(def, "\t", "");
1049 if (compare_ascii_no_case(def, "end") == 0) {
1054 char ichar = def[0];
1057 if (isUpperCase(ichar)) {
1058 cs.forceUpperCase = true;
1059 def[0] = lowercase(ichar);
1062 /** For portability reasons (between different
1063 * cite engines such as natbib and biblatex),
1064 * we distinguish between:
1065 * 1. The LyX name as output in the LyX file
1066 * 2. Possible aliases that might fall back to
1067 * the given LyX name in the current engine
1068 * 3. The actual LaTeX command that is output
1069 * (2) and (3) are optional.
1070 * Also, the GUI string for the starred version can
1073 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1082 ScanMode mode = LyXName;
1083 ScanMode oldmode = LyXName;
1088 size_t const n = def.size();
1089 for (size_t i = 0; i != n; ++i) {
1093 else if (ichar == '=')
1095 else if (ichar == '<') {
1098 } else if (ichar == '>')
1100 else if (mode == LaTeXCmd)
1102 else if (mode == StarDesc)
1104 else if (ichar == '$')
1105 cs.hasQualifiedList = true;
1106 else if (ichar == '*')
1107 cs.hasStarredVersion = true;
1108 else if (ichar == '[' && cs.textAfter)
1109 cs.textBefore = true;
1110 else if (ichar == '[')
1111 cs.textAfter = true;
1112 else if (ichar != ']') {
1120 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1121 if (!alias.empty()) {
1122 vector<string> const aliases = getVectorFromString(alias);
1123 for (string const &s: aliases)
1124 cite_command_aliases_[s] = lyx_cmd;
1126 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1127 int size = stardesc.size();
1129 cs.stardesc = stardescs[0];
1131 cs.startooltip = stardescs[1];
1132 if (type & ENGINE_TYPE_AUTHORYEAR)
1133 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1134 if (type & ENGINE_TYPE_NUMERICAL)
1135 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1136 if (type & ENGINE_TYPE_DEFAULT)
1137 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1143 int TextClass::readCiteEngineType(Lexer & lexrc) const
1145 static_assert(ENGINE_TYPE_DEFAULT ==
1146 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1147 "Incorrect default engine type");
1148 if (!lexrc.next()) {
1149 lexrc.printError("No cite engine type given for token: `$$Token'.");
1150 return ENGINE_TYPE_DEFAULT;
1152 string const type = rtrim(lexrc.getString());
1153 if (compare_ascii_no_case(type, "authoryear") == 0)
1154 return ENGINE_TYPE_AUTHORYEAR;
1155 else if (compare_ascii_no_case(type, "numerical") == 0)
1156 return ENGINE_TYPE_NUMERICAL;
1157 else if (compare_ascii_no_case(type, "default") != 0) {
1158 string const s = "Unknown cite engine type `" + type
1159 + "' given for token: `$$Token',";
1160 lexrc.printError(s);
1162 return ENGINE_TYPE_DEFAULT;
1166 bool TextClass::readCiteFormat(Lexer & lexrc)
1168 int const type = readCiteEngineType(lexrc);
1171 while (lexrc.isOK()) {
1173 etype = lexrc.getString();
1174 if (compare_ascii_no_case(etype, "end") == 0)
1179 definition = lexrc.getString();
1180 char initchar = etype[0];
1181 if (initchar == '#')
1183 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1184 if (type & ENGINE_TYPE_AUTHORYEAR)
1185 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1186 if (type & ENGINE_TYPE_NUMERICAL)
1187 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1188 if (type & ENGINE_TYPE_DEFAULT)
1189 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1191 if (type & ENGINE_TYPE_AUTHORYEAR)
1192 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1193 if (type & ENGINE_TYPE_NUMERICAL)
1194 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1195 if (type & ENGINE_TYPE_DEFAULT)
1196 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1203 bool TextClass::readFloat(Lexer & lexrc)
1220 FT_ALLOWED_PLACEMENT,
1226 LexerKeyword floatTags[] = {
1227 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1228 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1229 { "allowswide", FT_ALLOWS_WIDE },
1231 { "extension", FT_EXT },
1232 { "guiname", FT_NAME },
1233 { "htmlattr", FT_HTMLATTR },
1234 { "htmlstyle", FT_HTMLSTYLE },
1235 { "htmltag", FT_HTMLTAG },
1236 { "ispredefined", FT_PREDEFINED },
1237 { "listcommand", FT_LISTCOMMAND },
1238 { "listname", FT_LISTNAME },
1239 { "numberwithin", FT_WITHIN },
1240 { "placement", FT_PLACEMENT },
1241 { "refprefix", FT_REFPREFIX },
1242 { "style", FT_STYLE },
1243 { "type", FT_TYPE },
1244 { "usesfloatpkg", FT_USESFLOAT }
1247 lexrc.pushTable(floatTags);
1251 docstring htmlstyle;
1257 string allowed_placement = "!htbpH";
1262 bool usesfloat = true;
1263 bool ispredefined = false;
1264 bool allowswide = true;
1265 bool allowssideways = true;
1267 bool getout = false;
1268 while (!getout && lexrc.isOK()) {
1269 int le = lexrc.lex();
1271 case Lexer::LEX_UNDEF:
1272 lexrc.printError("Unknown float tag `$$Token'");
1280 type = lexrc.getString();
1281 if (floatlist_.typeExist(type)) {
1282 Floating const & fl = floatlist_.getType(type);
1283 placement = fl.placement();
1285 within = fl.within();
1288 listname = fl.listName();
1289 usesfloat = fl.usesFloatPkg();
1290 ispredefined = fl.isPredefined();
1291 listcommand = fl.listCommand();
1292 refprefix = fl.refPrefix();
1297 name = lexrc.getString();
1301 placement = lexrc.getString();
1303 case FT_ALLOWED_PLACEMENT:
1305 allowed_placement = lexrc.getString();
1309 ext = lexrc.getString();
1313 within = lexrc.getString();
1314 if (within == "none")
1319 style = lexrc.getString();
1321 case FT_LISTCOMMAND:
1323 listcommand = lexrc.getString();
1327 refprefix = lexrc.getString();
1331 listname = lexrc.getString();
1335 usesfloat = lexrc.getBool();
1339 ispredefined = lexrc.getBool();
1341 case FT_ALLOWS_SIDEWAYS:
1343 allowssideways = lexrc.getBool();
1345 case FT_ALLOWS_WIDE:
1347 allowswide = lexrc.getBool();
1351 htmlattr = lexrc.getString();
1355 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1359 htmltag = lexrc.getString();
1369 // Here we have a full float if getout == true
1371 if (!usesfloat && listcommand.empty()) {
1372 // if this float uses the same auxfile as an existing one,
1373 // there is no need for it to provide a list command.
1374 FloatList::const_iterator it = floatlist_.begin();
1375 FloatList::const_iterator en = floatlist_.end();
1376 bool found_ext = false;
1377 for (; it != en; ++it) {
1378 if (it->second.ext() == ext) {
1384 LYXERR0("The layout does not provide a list command " <<
1385 "for the float `" << type << "'. LyX will " <<
1386 "not be able to produce a float list.");
1388 Floating fl(type, placement, ext, within, style, name,
1389 listname, listcommand, refprefix, allowed_placement,
1390 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1391 allowswide, allowssideways);
1392 floatlist_.newFloat(fl);
1393 // each float has its own counter
1394 counters_.newCounter(from_ascii(type), from_ascii(within),
1395 docstring(), docstring());
1396 // also define sub-float counters
1397 docstring const subtype = "sub-" + from_ascii(type);
1398 counters_.newCounter(subtype, from_ascii(type),
1399 "\\alph{" + subtype + "}", docstring());
1405 bool TextClass::readOutlinerName(Lexer & lexrc)
1410 type = lexrc.getString();
1412 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1416 name = lexrc.getDocString();
1418 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1421 outliner_names_[type] = name;
1426 string const & TextClass::prerequisites(string const & sep) const
1428 if (contains(prerequisites_, ',')) {
1429 vector<string> const pres = getVectorFromString(prerequisites_);
1430 prerequisites_ = getStringFromVector(pres, sep);
1432 return prerequisites_;
1436 bool TextClass::hasLayout(docstring const & n) const
1438 docstring const name = n.empty() ? defaultLayoutName() : n;
1440 return find_if(layoutlist_.begin(), layoutlist_.end(),
1441 LayoutNamesEqual(name))
1442 != layoutlist_.end();
1446 bool TextClass::hasInsetLayout(docstring const & n) const
1450 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1451 return it != insetlayoutlist_.end();
1455 Layout const & TextClass::operator[](docstring const & name) const
1457 LATTEST(!name.empty());
1460 find_if(begin(), end(), LayoutNamesEqual(name));
1463 LYXERR0("We failed to find the layout '" << name
1464 << "' in the layout list. You MUST investigate!");
1465 for (const_iterator cit = begin(); cit != end(); ++cit)
1466 lyxerr << " " << to_utf8(cit->name()) << endl;
1468 // We require the name to exist
1469 static const Layout dummy;
1470 LASSERT(false, return dummy);
1477 Layout & TextClass::operator[](docstring const & name)
1479 LATTEST(!name.empty());
1480 // Safe to continue, given what we do below.
1482 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1485 LYXERR0("We failed to find the layout '" << to_utf8(name)
1486 << "' in the layout list. You MUST investigate!");
1487 for (const_iterator cit = begin(); cit != end(); ++cit)
1488 LYXERR0(" " << to_utf8(cit->name()));
1490 // we require the name to exist
1492 // we are here only in release mode
1493 layoutlist_.push_back(createBasicLayout(name, true));
1494 it = find_if(begin(), end(), LayoutNamesEqual(name));
1501 bool TextClass::deleteLayout(docstring const & name)
1503 if (name == defaultLayoutName() || name == plainLayoutName())
1506 LayoutList::iterator it =
1507 remove_if(layoutlist_.begin(), layoutlist_.end(),
1508 LayoutNamesEqual(name));
1510 LayoutList::iterator end = layoutlist_.end();
1511 bool const ret = (it != end);
1512 layoutlist_.erase(it, end);
1517 bool TextClass::deleteInsetLayout(docstring const & name)
1519 return insetlayoutlist_.erase(name);
1523 // Load textclass info if not loaded yet
1524 bool TextClass::load(string const & path) const
1529 // Read style-file, provided path is searched before the system ones
1530 // If path is a file, it is loaded directly.
1531 FileName layout_file(path);
1532 if (!path.empty() && !layout_file.isReadableFile())
1533 layout_file = FileName(addName(path, name_ + ".layout"));
1534 if (layout_file.empty() || !layout_file.exists())
1535 layout_file = libFileSearch("layouts", name_, "layout");
1536 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1539 lyxerr << "Error reading `"
1540 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1541 << "'\n(Check `" << name_
1542 << "')\nCheck your installation and "
1543 "try Options/Reconfigure..."
1551 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1556 layoutlist_.push_back(createBasicLayout(n, true));
1561 string DocumentClass::forcedLayouts() const
1565 const_iterator const e = end();
1566 for (const_iterator i = begin(); i != e; ++i) {
1567 if (i->forcelocal > 0) {
1569 os << "Format " << LAYOUT_FORMAT << '\n';
1579 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1581 // FIXME The fix for the InsetLayout part of 4812 would be here:
1582 // Add the InsetLayout to the document class if it is not found.
1584 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1585 while (!n.empty()) {
1586 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1587 if (cit != cen && cit->first == n) {
1588 if (cit->second.obsoleted_by().empty())
1590 n = cit->second.obsoleted_by();
1591 return insetLayout(n);
1593 // If we have a generic prefix (e.g., "Note:"),
1594 // try if this one alone is found.
1595 size_t i = n.find(':');
1596 if (i == string::npos)
1600 // Layout "name" not found.
1601 return plainInsetLayout();
1605 InsetLayout const & DocumentClass::plainInsetLayout() {
1606 static const InsetLayout plain_insetlayout_;
1607 return plain_insetlayout_;
1611 docstring const & TextClass::defaultLayoutName() const
1613 return defaultlayout_;
1617 Layout const & TextClass::defaultLayout() const
1619 return operator[](defaultLayoutName());
1623 bool TextClass::isDefaultLayout(Layout const & layout) const
1625 return layout.name() == defaultLayoutName();
1629 bool TextClass::isPlainLayout(Layout const & layout) const
1631 return layout.name() == plainLayoutName();
1635 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1637 static Layout * defaultLayout = NULL;
1639 if (defaultLayout) {
1640 defaultLayout->setUnknown(unknown);
1641 defaultLayout->setName(name);
1642 return *defaultLayout;
1645 static char const * s = "Margin Static\n"
1646 "LatexType Paragraph\n"
1649 "AlignPossible Left, Right, Center\n"
1650 "LabelType No_Label\n"
1652 istringstream ss(s);
1653 Lexer lex(textClassTags);
1655 defaultLayout = new Layout;
1656 defaultLayout->setUnknown(unknown);
1657 defaultLayout->setName(name);
1658 if (!readStyle(lex, *defaultLayout)) {
1659 // The only way this happens is because the hardcoded layout above
1663 return *defaultLayout;
1667 DocumentClassPtr getDocumentClass(
1668 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1669 LayoutModuleList const & celist,
1672 DocumentClassPtr doc_class =
1673 DocumentClassPtr(new DocumentClass(baseClass));
1674 LayoutModuleList::const_iterator it = modlist.begin();
1675 LayoutModuleList::const_iterator en = modlist.end();
1676 for (; it != en; ++it) {
1677 string const modName = *it;
1678 LyXModule * lm = theModuleList[modName];
1680 docstring const msg =
1681 bformat(_("The module %1$s has been requested by\n"
1682 "this document but has not been found in the list of\n"
1683 "available modules. If you recently installed it, you\n"
1684 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1686 frontend::Alert::warning(_("Module not available"), msg);
1689 if (!lm->isAvailable() && !clone) {
1690 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1691 docstring const msg =
1692 bformat(_("The module %1$s requires a package that is not\n"
1693 "available in your LaTeX installation, or a converter that\n"
1694 "you have not installed. LaTeX output may not be possible.\n"
1695 "Missing prerequisites:\n"
1697 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1698 from_utf8(modName), prereqs);
1699 frontend::Alert::warning(_("Package not available"), msg, true);
1701 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1702 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1703 docstring const msg =
1704 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1705 frontend::Alert::warning(_("Read Error"), msg);
1709 LayoutModuleList::const_iterator cit = celist.begin();
1710 LayoutModuleList::const_iterator cen = celist.end();
1711 for (; cit != cen; ++cit) {
1712 string const ceName = *cit;
1713 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1715 docstring const msg =
1716 bformat(_("The cite engine %1$s has been requested by\n"
1717 "this document but has not been found in the list of\n"
1718 "available engines. If you recently installed it, you\n"
1719 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1721 frontend::Alert::warning(_("Cite Engine not available"), msg);
1724 if (!ce->isAvailable() && !clone) {
1725 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1726 docstring const msg =
1727 bformat(_("The cite engine %1$s requires a package that is not\n"
1728 "available in your LaTeX installation, or a converter that\n"
1729 "you have not installed. LaTeX output may not be possible.\n"
1730 "Missing prerequisites:\n"
1732 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1733 from_utf8(ceName), prereqs);
1734 frontend::Alert::warning(_("Package not available"), msg, true);
1736 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1737 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1738 docstring const msg =
1739 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1740 frontend::Alert::warning(_("Read Error"), msg);
1748 /////////////////////////////////////////////////////////////////////////
1752 /////////////////////////////////////////////////////////////////////////
1754 DocumentClass::DocumentClass(LayoutFile const & tc)
1759 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1761 LayoutList::const_iterator it = layoutlist_.begin();
1762 LayoutList::const_iterator end = layoutlist_.end();
1763 for (; it != end; ++it)
1764 if (it->latexname() == lay)
1770 bool DocumentClass::provides(string const & p) const
1772 return provides_.find(p) != provides_.end();
1776 bool DocumentClass::hasTocLevels() const
1778 return min_toclevel_ != Layout::NOT_IN_TOC;
1782 Layout const & DocumentClass::getTOCLayout() const
1784 // we're going to look for the layout with the minimum toclevel
1785 TextClass::LayoutList::const_iterator lit = begin();
1786 TextClass::LayoutList::const_iterator const len = end();
1787 int minlevel = 1000;
1788 Layout const * lay = NULL;
1789 for (; lit != len; ++lit) {
1790 int const level = lit->toclevel;
1791 // we don't want Part or unnumbered sections
1792 if (level == Layout::NOT_IN_TOC || level < 0
1793 || level >= minlevel || lit->counter.empty())
1800 // hmm. that is very odd, so we'll do our best.
1801 return operator[](defaultLayoutName());
1805 Layout const & DocumentClass::htmlTOCLayout() const
1807 if (html_toc_section_.empty())
1808 html_toc_section_ = getTOCLayout().name();
1809 return operator[](html_toc_section_);
1813 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1814 string const & entry, bool const punct, string const & fallback) const
1816 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%]]}";
1818 default_format += ".";
1820 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1821 if (itype == cite_formats_.end())
1822 return default_format;
1823 map<string, string>::const_iterator it = itype->second.find(entry);
1824 if (it == itype->second.end() && !fallback.empty())
1825 it = itype->second.find(fallback);
1826 if (it == itype->second.end())
1827 return default_format;
1829 return it->second + ".";
1834 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1835 string const & macro) const
1837 static string empty;
1838 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1839 if (itype == cite_macros_.end())
1841 map<string, string>::const_iterator it = itype->second.find(macro);
1842 if (it == itype->second.end())
1848 vector<string> const DocumentClass::citeCommands(
1849 CiteEngineType const & type) const
1851 vector<CitationStyle> const styles = citeStyles(type);
1852 vector<CitationStyle>::const_iterator it = styles.begin();
1853 vector<CitationStyle>::const_iterator end = styles.end();
1854 vector<string> cmds;
1855 for (; it != end; ++it) {
1856 CitationStyle const cite = *it;
1857 cmds.push_back(cite.name);
1863 vector<CitationStyle> const & DocumentClass::citeStyles(
1864 CiteEngineType const & type) const
1866 static vector<CitationStyle> empty;
1867 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1868 if (it == cite_styles_.end())
1874 /////////////////////////////////////////////////////////////////////////
1878 /////////////////////////////////////////////////////////////////////////
1880 ostream & operator<<(ostream & os, PageSides p)