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 // Format of files before the 'Format' tag was introduced
416 while (lexrc.isOK() && !error) {
417 int le = lexrc.lex();
420 case Lexer::LEX_FEOF:
423 case Lexer::LEX_UNDEF:
424 lexrc.printError("Unknown TextClass tag `$$Token'");
432 // used below to track whether we are in an IfStyle or IfCounter tag.
433 bool modifystyle = false;
434 bool providestyle = false;
435 bool ifcounter = false;
437 switch (static_cast<TextClassTags>(le)) {
441 format = lexrc.getInteger();
444 case TC_OUTPUTFORMAT:
446 outputFormat_ = lexrc.getString();
450 readOutputType(lexrc);
451 switch(outputType_) {
453 outputFormat_ = "latex";
456 outputFormat_ = "docbook";
459 outputFormat_ = "literate";
464 case TC_INPUT: // Include file
466 string const inc = lexrc.getString();
467 FileName tmp = libFileSearch("layouts", inc,
471 lexrc.printError("Could not find input file: " + inc);
473 } else if (!read(tmp, MERGE)) {
474 lexrc.printError("Error reading input file: " + tmp.absFileName());
480 case TC_DEFAULTSTYLE:
482 docstring const name = from_utf8(subst(lexrc.getString(),
484 defaultlayout_ = name;
491 case TC_PROVIDESTYLE:
492 // if modifystyle is true, then we got here by falling through
493 // so we are not in an ProvideStyle block
499 lexrc.printError("No name given for style: `$$Token'.");
503 docstring const name = from_utf8(subst(lexrc.getString(),
506 string s = "Could not read name for style: `$$Token' "
507 + lexrc.getString() + " is probably not valid UTF-8!";
510 // Since we couldn't read the name, we just scan the rest
511 // of the style and discard it.
512 error = !readStyle(lexrc, lay);
516 bool const have_layout = hasLayout(name);
518 // If the layout already exists, then we want to add it to
519 // the existing layout, as long as we are not in an ProvideStyle
521 if (have_layout && !providestyle) {
522 Layout & lay = operator[](name);
523 error = !readStyle(lexrc, lay);
525 // If the layout does not exist, then we want to create a new
526 // one, but not if we are in a ModifyStyle block.
527 else if (!have_layout && !modifystyle) {
529 layout.setName(name);
530 error = !readStyle(lexrc, layout);
532 layoutlist_.push_back(layout);
534 if (defaultlayout_.empty()) {
535 // We do not have a default layout yet, so we choose
536 // the first layout we encounter.
537 defaultlayout_ = name;
540 // There are two ways to get here:
541 // (i) The layout exists but we are in an ProvideStyle block
542 // (ii) The layout doesn't exist, but we are in an ModifyStyle
544 // Either way, we just scan the rest and discard it
547 // false positive from coverity
548 // coverity[CHECKED_RETURN]
549 readStyle(lexrc, lay);
556 docstring const style = from_utf8(subst(lexrc.getString(),
558 if (!deleteLayout(style))
559 lyxerr << "Cannot delete style `"
560 << to_utf8(style) << '\'' << endl;
564 case TC_NOINSETLAYOUT:
566 docstring const style = from_utf8(subst(lexrc.getString(),
568 if (!deleteInsetLayout(style))
569 LYXERR0("Style `" << style << "' cannot be removed\n"
570 "because it was not found!");
576 columns_ = lexrc.getInteger();
581 switch (lexrc.getInteger()) {
582 case 1: sides_ = OneSide; break;
583 case 2: sides_ = TwoSides; break;
585 lyxerr << "Impossible number of page"
586 " sides, setting to one."
596 pagestyle_ = rtrim(lexrc.getString());
600 defaultfont_ = lyxRead(lexrc);
601 if (!defaultfont_.resolved()) {
602 lexrc.printError("Warning: defaultfont should "
603 "be fully instantiated!");
604 defaultfont_.realize(sane_font);
610 secnumdepth_ = lexrc.getInteger();
615 tocdepth_ = lexrc.getInteger();
618 // First step to support options
619 case TC_CLASSOPTIONS:
620 readClassOptions(lexrc);
624 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
627 case TC_HTMLPREAMBLE:
628 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
632 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
635 case TC_HTMLTOCSECTION:
636 html_toc_section_ = from_utf8(trim(lexrc.getString()));
639 case TC_ADDTOPREAMBLE:
640 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
643 case TC_ADDTOHTMLPREAMBLE:
644 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
647 case TC_ADDTOHTMLSTYLES:
648 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
653 string const feature = lexrc.getString();
655 if (lexrc.getInteger())
656 provides_.insert(feature);
658 provides_.erase(feature);
664 vector<string> const req
665 = getVectorFromString(lexrc.getString());
666 requires_.insert(req.begin(), req.end());
672 string const pkg = lexrc.getString();
674 string const options = lexrc.getString();
675 package_options_[pkg] = options;
679 case TC_DEFAULTMODULE: {
681 string const module = lexrc.getString();
682 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
683 default_modules_.push_back(module);
687 case TC_PROVIDESMODULE: {
689 string const module = lexrc.getString();
690 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
691 provided_modules_.push_back(module);
695 case TC_EXCLUDESMODULE: {
697 string const module = lexrc.getString();
698 // modules already have their own way to exclude other modules
700 LYXERR0("ExcludesModule tag cannot be used in a module!");
703 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
704 excluded_modules_.push_back(module);
708 case TC_LEFTMARGIN: // left margin type
710 leftmargin_ = lexrc.getDocString();
713 case TC_RIGHTMARGIN: // right margin type
715 rightmargin_ = lexrc.getDocString();
718 case TC_INSETLAYOUT: {
720 lexrc.printError("No name given for InsetLayout: `$$Token'.");
724 docstring const name = subst(lexrc.getDocString(), '_', ' ');
726 string s = "Could not read name for InsetLayout: `$$Token' "
727 + lexrc.getString() + " is probably not valid UTF-8!";
730 // Since we couldn't read the name, we just scan the rest
731 // of the style and discard it.
732 il.read(lexrc, *this);
733 // Let's try to continue rather than abort.
735 } else if (hasInsetLayout(name)) {
736 InsetLayout & il = insetlayoutlist_[name];
737 error = !il.read(lexrc, *this);
741 error = !il.read(lexrc, *this);
743 insetlayoutlist_[name] = il;
749 error = !readFloat(lexrc);
753 error = !readCiteEngine(lexrc);
756 case TC_CITEENGINETYPE:
758 opt_enginetype_ = rtrim(lexrc.getString());
762 error = !readCiteFormat(lexrc);
765 case TC_CITEFRAMEWORK:
767 citeframework_ = rtrim(lexrc.getString());
770 case TC_MAXCITENAMES:
772 maxcitenames_ = size_t(lexrc.getInteger());
775 case TC_DEFAULTBIBLIO:
777 vector<string> const dbs =
778 getVectorFromString(rtrim(lexrc.getString()), "|");
779 vector<string>::const_iterator it = dbs.begin();
780 vector<string>::const_iterator end = dbs.end();
781 for (; it != end; ++it) {
782 if (!contains(*it, ':')) {
783 vector<string> const enginetypes =
784 getVectorFromString(opt_enginetype_, "|");
785 for (string const &s: enginetypes)
786 cite_default_biblio_style_[s] = *it;
789 string const db = split(*it, eng, ':');
790 cite_default_biblio_style_[eng] = db;
796 case TC_FULLAUTHORLIST:
798 cite_full_author_list_ &= lexrc.getBool();
803 docstring const cnt = lexrc.getDocString();
804 if (!counters_.remove(cnt))
805 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
814 docstring const name = lexrc.getDocString();
816 string s = "Could not read name for counter: `$$Token' "
817 + lexrc.getString() + " is probably not valid UTF-8!";
818 lexrc.printError(s.c_str());
820 // Since we couldn't read the name, we just scan the rest
824 error = !counters_.read(lexrc, name, !ifcounter);
827 lexrc.printError("No name given for style: `$$Token'.");
832 case TC_TITLELATEXTYPE:
833 readTitleType(lexrc);
836 case TC_TITLELATEXNAME:
838 titlename_ = lexrc.getString();
843 string const nofloat = lexrc.getString();
844 floatlist_.erase(nofloat);
848 case TC_OUTLINERNAME:
849 error = !readOutlinerName(lexrc);
853 // Note that this is triggered the first time through the loop unless
854 // we hit a format tag.
855 if (format != LAYOUT_FORMAT)
856 return FORMAT_MISMATCH;
859 // at present, we abort if we encounter an error,
860 // so there is no point continuing.
867 if (defaultlayout_.empty()) {
868 LYXERR0("Error: Textclass '" << name_
869 << "' is missing a defaultstyle.");
873 // Try to erase "stdinsets" from the provides_ set.
875 // Provides stdinsets 1
876 // declaration simply tells us that the standard insets have been
877 // defined. (It's found in stdinsets.inc but could also be used in
878 // user-defined files.) There isn't really any such package. So we
879 // might as well go ahead and erase it.
880 // If we do not succeed, then it was not there, which means that
881 // the textclass did not provide the definitions of the standard
882 // insets. So we need to try to load them.
883 int erased = provides_.erase("stdinsets");
885 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
888 frontend::Alert::warning(_("Missing File"),
889 _("Could not find stdinsets.inc! This may lead to data loss!"));
891 } else if (!read(tmp, MERGE)) {
892 frontend::Alert::warning(_("Corrupt File"),
893 _("Could not read stdinsets.inc! This may lead to data loss!"));
898 min_toclevel_ = Layout::NOT_IN_TOC;
899 max_toclevel_ = Layout::NOT_IN_TOC;
900 const_iterator lit = begin();
901 const_iterator len = end();
902 for (; lit != len; ++lit) {
903 int const toclevel = lit->toclevel;
904 if (toclevel != Layout::NOT_IN_TOC) {
905 if (min_toclevel_ == Layout::NOT_IN_TOC)
906 min_toclevel_ = toclevel;
908 min_toclevel_ = min(min_toclevel_, toclevel);
909 max_toclevel_ = max(max_toclevel_, toclevel);
912 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
913 << ", maximum is " << max_toclevel_);
915 return (error ? ERROR : OK);
919 void TextClass::readTitleType(Lexer & lexrc)
921 LexerKeyword titleTypeTags[] = {
922 { "commandafter", TITLE_COMMAND_AFTER },
923 { "environment", TITLE_ENVIRONMENT }
926 PushPopHelper pph(lexrc, titleTypeTags);
928 int le = lexrc.lex();
930 case Lexer::LEX_UNDEF:
931 lexrc.printError("Unknown output type `$$Token'");
933 case TITLE_COMMAND_AFTER:
934 case TITLE_ENVIRONMENT:
935 titletype_ = static_cast<TitleLatexType>(le);
938 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
944 void TextClass::readOutputType(Lexer & lexrc)
946 LexerKeyword outputTypeTags[] = {
947 { "docbook", DOCBOOK },
949 { "literate", LITERATE }
952 PushPopHelper pph(lexrc, outputTypeTags);
954 int le = lexrc.lex();
956 case Lexer::LEX_UNDEF:
957 lexrc.printError("Unknown output type `$$Token'");
962 outputType_ = static_cast<OutputType>(le);
965 LYXERR0("Unhandled value " << le);
971 void TextClass::readClassOptions(Lexer & lexrc)
981 LexerKeyword classOptionsTags[] = {
983 {"fontsize", CO_FONTSIZE },
984 {"header", CO_HEADER },
985 {"other", CO_OTHER },
986 {"pagestyle", CO_PAGESTYLE }
989 lexrc.pushTable(classOptionsTags);
991 while (!getout && lexrc.isOK()) {
992 int le = lexrc.lex();
994 case Lexer::LEX_UNDEF:
995 lexrc.printError("Unknown ClassOption tag `$$Token'");
1003 opt_fontsize_ = rtrim(lexrc.getString());
1007 opt_pagestyle_ = rtrim(lexrc.getString());
1011 if (options_.empty())
1012 options_ = lexrc.getString();
1014 options_ += ',' + lexrc.getString();
1018 class_header_ = subst(lexrc.getString(), """, "\"");
1029 bool TextClass::readCiteEngine(Lexer & lexrc)
1031 int const type = readCiteEngineType(lexrc);
1032 if (type & ENGINE_TYPE_AUTHORYEAR)
1033 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1034 if (type & ENGINE_TYPE_NUMERICAL)
1035 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1036 if (type & ENGINE_TYPE_DEFAULT)
1037 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1039 bool getout = false;
1040 while (!getout && lexrc.isOK()) {
1042 def = lexrc.getString();
1043 def = subst(def, " ", "");
1044 def = subst(def, "\t", "");
1045 if (compare_ascii_no_case(def, "end") == 0) {
1050 char ichar = def[0];
1053 if (isUpperCase(ichar)) {
1054 cs.forceUpperCase = true;
1055 def[0] = lowercase(ichar);
1058 /** For portability reasons (between different
1059 * cite engines such as natbib and biblatex),
1060 * we distinguish between:
1061 * 1. The LyX name as output in the LyX file
1062 * 2. Possible aliases that might fall back to
1063 * the given LyX name in the current engine
1064 * 3. The actual LaTeX command that is output
1065 * (2) and (3) are optional.
1066 * Also, the GUI string for the starred version can
1069 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1078 ScanMode mode = LyXName;
1079 ScanMode oldmode = LyXName;
1084 size_t const n = def.size();
1085 for (size_t i = 0; i != n; ++i) {
1089 else if (ichar == '=')
1091 else if (ichar == '<') {
1094 } else if (ichar == '>')
1096 else if (mode == LaTeXCmd)
1098 else if (mode == StarDesc)
1100 else if (ichar == '*')
1101 cs.hasStarredVersion = true;
1102 else if (ichar == '[' && cs.textAfter)
1103 cs.textBefore = true;
1104 else if (ichar == '[')
1105 cs.textAfter = true;
1106 else if (ichar != ']') {
1114 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1115 if (!alias.empty()) {
1116 vector<string> const aliases = getVectorFromString(alias);
1117 for (string const &s: aliases)
1118 cite_command_aliases_[s] = lyx_cmd;
1120 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1121 int size = stardesc.size();
1123 cs.stardesc = stardescs[0];
1125 cs.startooltip = stardescs[1];
1126 if (type & ENGINE_TYPE_AUTHORYEAR)
1127 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1128 if (type & ENGINE_TYPE_NUMERICAL)
1129 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1130 if (type & ENGINE_TYPE_DEFAULT)
1131 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1137 int TextClass::readCiteEngineType(Lexer & lexrc) const
1139 LATTEST(ENGINE_TYPE_DEFAULT ==
1140 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1141 if (!lexrc.next()) {
1142 lexrc.printError("No cite engine type given for token: `$$Token'.");
1143 return ENGINE_TYPE_DEFAULT;
1145 string const type = rtrim(lexrc.getString());
1146 if (compare_ascii_no_case(type, "authoryear") == 0)
1147 return ENGINE_TYPE_AUTHORYEAR;
1148 else if (compare_ascii_no_case(type, "numerical") == 0)
1149 return ENGINE_TYPE_NUMERICAL;
1150 else if (compare_ascii_no_case(type, "default") != 0) {
1151 string const s = "Unknown cite engine type `" + type
1152 + "' given for token: `$$Token',";
1153 lexrc.printError(s);
1155 return ENGINE_TYPE_DEFAULT;
1159 bool TextClass::readCiteFormat(Lexer & lexrc)
1161 int const type = readCiteEngineType(lexrc);
1164 while (lexrc.isOK()) {
1166 etype = lexrc.getString();
1167 if (compare_ascii_no_case(etype, "end") == 0)
1172 definition = lexrc.getString();
1173 char initchar = etype[0];
1174 if (initchar == '#')
1176 if (initchar == '!' || initchar == '_') {
1177 if (type & ENGINE_TYPE_AUTHORYEAR)
1178 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1179 if (type & ENGINE_TYPE_NUMERICAL)
1180 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1181 if (type & ENGINE_TYPE_DEFAULT)
1182 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1184 if (type & ENGINE_TYPE_AUTHORYEAR)
1185 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1186 if (type & ENGINE_TYPE_NUMERICAL)
1187 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1188 if (type & ENGINE_TYPE_DEFAULT)
1189 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1196 bool TextClass::readFloat(Lexer & lexrc)
1213 FT_ALLOWED_PLACEMENT,
1219 LexerKeyword floatTags[] = {
1220 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1221 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1222 { "allowswide", FT_ALLOWS_WIDE },
1224 { "extension", FT_EXT },
1225 { "guiname", FT_NAME },
1226 { "htmlattr", FT_HTMLATTR },
1227 { "htmlstyle", FT_HTMLSTYLE },
1228 { "htmltag", FT_HTMLTAG },
1229 { "ispredefined", FT_PREDEFINED },
1230 { "listcommand", FT_LISTCOMMAND },
1231 { "listname", FT_LISTNAME },
1232 { "numberwithin", FT_WITHIN },
1233 { "placement", FT_PLACEMENT },
1234 { "refprefix", FT_REFPREFIX },
1235 { "style", FT_STYLE },
1236 { "type", FT_TYPE },
1237 { "usesfloatpkg", FT_USESFLOAT }
1240 lexrc.pushTable(floatTags);
1244 docstring htmlstyle;
1250 string allowed_placement = "!htbpH";
1255 bool usesfloat = true;
1256 bool ispredefined = false;
1257 bool allowswide = true;
1258 bool allowssideways = true;
1260 bool getout = false;
1261 while (!getout && lexrc.isOK()) {
1262 int le = lexrc.lex();
1264 case Lexer::LEX_UNDEF:
1265 lexrc.printError("Unknown float tag `$$Token'");
1273 type = lexrc.getString();
1274 if (floatlist_.typeExist(type)) {
1275 Floating const & fl = floatlist_.getType(type);
1276 placement = fl.placement();
1278 within = fl.within();
1281 listname = fl.listName();
1282 usesfloat = fl.usesFloatPkg();
1283 ispredefined = fl.isPredefined();
1284 listcommand = fl.listCommand();
1285 refprefix = fl.refPrefix();
1290 name = lexrc.getString();
1294 placement = lexrc.getString();
1296 case FT_ALLOWED_PLACEMENT:
1298 allowed_placement = lexrc.getString();
1302 ext = lexrc.getString();
1306 within = lexrc.getString();
1307 if (within == "none")
1312 style = lexrc.getString();
1314 case FT_LISTCOMMAND:
1316 listcommand = lexrc.getString();
1320 refprefix = lexrc.getString();
1324 listname = lexrc.getString();
1328 usesfloat = lexrc.getBool();
1332 ispredefined = lexrc.getBool();
1334 case FT_ALLOWS_SIDEWAYS:
1336 allowssideways = lexrc.getBool();
1338 case FT_ALLOWS_WIDE:
1340 allowswide = lexrc.getBool();
1344 htmlattr = lexrc.getString();
1348 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1352 htmltag = lexrc.getString();
1362 // Here we have a full float if getout == true
1364 if (!usesfloat && listcommand.empty()) {
1365 // if this float uses the same auxfile as an existing one,
1366 // there is no need for it to provide a list command.
1367 FloatList::const_iterator it = floatlist_.begin();
1368 FloatList::const_iterator en = floatlist_.end();
1369 bool found_ext = false;
1370 for (; it != en; ++it) {
1371 if (it->second.ext() == ext) {
1377 LYXERR0("The layout does not provide a list command " <<
1378 "for the float `" << type << "'. LyX will " <<
1379 "not be able to produce a float list.");
1381 Floating fl(type, placement, ext, within, style, name,
1382 listname, listcommand, refprefix, allowed_placement,
1383 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1384 allowswide, allowssideways);
1385 floatlist_.newFloat(fl);
1386 // each float has its own counter
1387 counters_.newCounter(from_ascii(type), from_ascii(within),
1388 docstring(), docstring());
1389 // also define sub-float counters
1390 docstring const subtype = "sub-" + from_ascii(type);
1391 counters_.newCounter(subtype, from_ascii(type),
1392 "\\alph{" + subtype + "}", docstring());
1398 bool TextClass::readOutlinerName(Lexer & lexrc)
1403 type = lexrc.getString();
1405 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1409 name = lexrc.getDocString();
1411 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1414 outliner_names_[type] = name;
1419 docstring TextClass::outlinerName(std::string const & type) const
1421 std::map<std::string,docstring>::const_iterator const it
1422 = outliner_names_.find(type);
1423 if (it == outliner_names_.end()) {
1424 LYXERR0("Missing OutlinerName for " << type << "!");
1425 return from_utf8(type);
1431 string const & TextClass::prerequisites(string const & sep) const
1433 if (contains(prerequisites_, ',')) {
1434 vector<string> const pres = getVectorFromString(prerequisites_);
1435 prerequisites_ = getStringFromVector(pres, sep);
1437 return prerequisites_;
1441 bool TextClass::hasLayout(docstring const & n) const
1443 docstring const name = n.empty() ? defaultLayoutName() : n;
1445 return find_if(layoutlist_.begin(), layoutlist_.end(),
1446 LayoutNamesEqual(name))
1447 != layoutlist_.end();
1451 bool TextClass::hasInsetLayout(docstring const & n) const
1455 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1456 return it != insetlayoutlist_.end();
1460 Layout const & TextClass::operator[](docstring const & name) const
1462 LATTEST(!name.empty());
1465 find_if(begin(), end(), LayoutNamesEqual(name));
1468 LYXERR0("We failed to find the layout '" << name
1469 << "' in the layout list. You MUST investigate!");
1470 for (const_iterator cit = begin(); cit != end(); ++cit)
1471 lyxerr << " " << to_utf8(cit->name()) << endl;
1473 // We require the name to exist
1474 static const Layout dummy;
1475 LASSERT(false, return dummy);
1482 Layout & TextClass::operator[](docstring const & name)
1484 LATTEST(!name.empty());
1485 // Safe to continue, given what we do below.
1487 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1490 LYXERR0("We failed to find the layout '" << to_utf8(name)
1491 << "' in the layout list. You MUST investigate!");
1492 for (const_iterator cit = begin(); cit != end(); ++cit)
1493 LYXERR0(" " << to_utf8(cit->name()));
1495 // we require the name to exist
1497 // we are here only in release mode
1498 layoutlist_.push_back(createBasicLayout(name, true));
1499 it = find_if(begin(), end(), LayoutNamesEqual(name));
1506 bool TextClass::deleteLayout(docstring const & name)
1508 if (name == defaultLayoutName() || name == plainLayoutName())
1511 LayoutList::iterator it =
1512 remove_if(layoutlist_.begin(), layoutlist_.end(),
1513 LayoutNamesEqual(name));
1515 LayoutList::iterator end = layoutlist_.end();
1516 bool const ret = (it != end);
1517 layoutlist_.erase(it, end);
1522 bool TextClass::deleteInsetLayout(docstring const & name)
1524 return insetlayoutlist_.erase(name);
1528 // Load textclass info if not loaded yet
1529 bool TextClass::load(string const & path) const
1534 // Read style-file, provided path is searched before the system ones
1535 // If path is a file, it is loaded directly.
1536 FileName layout_file(path);
1537 if (!path.empty() && !layout_file.isReadableFile())
1538 layout_file = FileName(addName(path, name_ + ".layout"));
1539 if (layout_file.empty() || !layout_file.exists())
1540 layout_file = libFileSearch("layouts", name_, "layout");
1541 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1544 lyxerr << "Error reading `"
1545 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1546 << "'\n(Check `" << name_
1547 << "')\nCheck your installation and "
1548 "try Options/Reconfigure..."
1556 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1561 layoutlist_.push_back(createBasicLayout(n, true));
1566 string DocumentClass::forcedLayouts() const
1570 const_iterator const e = end();
1571 for (const_iterator i = begin(); i != e; ++i) {
1572 if (i->forcelocal > 0) {
1574 os << "Format " << LAYOUT_FORMAT << '\n';
1584 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1586 // FIXME The fix for the InsetLayout part of 4812 would be here:
1587 // Add the InsetLayout to the document class if it is not found.
1589 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1590 while (!n.empty()) {
1591 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1592 if (cit != cen && cit->first == n) {
1593 if (cit->second.obsoleted_by().empty())
1595 n = cit->second.obsoleted_by();
1596 return insetLayout(n);
1598 // If we have a generic prefix (e.g., "Note:"),
1599 // try if this one alone is found.
1600 size_t i = n.find(':');
1601 if (i == string::npos)
1605 // Layout "name" not found.
1606 return plainInsetLayout();
1610 InsetLayout const & DocumentClass::plainInsetLayout() {
1611 static const InsetLayout plain_insetlayout_;
1612 return plain_insetlayout_;
1616 docstring const & TextClass::defaultLayoutName() const
1618 return defaultlayout_;
1622 Layout const & TextClass::defaultLayout() const
1624 return operator[](defaultLayoutName());
1628 bool TextClass::isDefaultLayout(Layout const & layout) const
1630 return layout.name() == defaultLayoutName();
1634 bool TextClass::isPlainLayout(Layout const & layout) const
1636 return layout.name() == plainLayoutName();
1640 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1642 static Layout * defaultLayout = NULL;
1644 if (defaultLayout) {
1645 defaultLayout->setUnknown(unknown);
1646 defaultLayout->setName(name);
1647 return *defaultLayout;
1650 static char const * s = "Margin Static\n"
1651 "LatexType Paragraph\n"
1654 "AlignPossible Left, Right, Center\n"
1655 "LabelType No_Label\n"
1657 istringstream ss(s);
1658 Lexer lex(textClassTags);
1660 defaultLayout = new Layout;
1661 defaultLayout->setUnknown(unknown);
1662 defaultLayout->setName(name);
1663 if (!readStyle(lex, *defaultLayout)) {
1664 // The only way this happens is because the hardcoded layout above
1668 return *defaultLayout;
1672 DocumentClassPtr getDocumentClass(
1673 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1674 LayoutModuleList const & celist,
1677 DocumentClassPtr doc_class =
1678 DocumentClassPtr(new DocumentClass(baseClass));
1679 LayoutModuleList::const_iterator it = modlist.begin();
1680 LayoutModuleList::const_iterator en = modlist.end();
1681 for (; it != en; ++it) {
1682 string const modName = *it;
1683 LyXModule * lm = theModuleList[modName];
1685 docstring const msg =
1686 bformat(_("The module %1$s has been requested by\n"
1687 "this document but has not been found in the list of\n"
1688 "available modules. If you recently installed it, you\n"
1689 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1691 frontend::Alert::warning(_("Module not available"), msg);
1694 if (!lm->isAvailable() && !clone) {
1695 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1696 docstring const msg =
1697 bformat(_("The module %1$s requires a package that is not\n"
1698 "available in your LaTeX installation, or a converter that\n"
1699 "you have not installed. LaTeX output may not be possible.\n"
1700 "Missing prerequisites:\n"
1702 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1703 from_utf8(modName), prereqs);
1704 frontend::Alert::warning(_("Package not available"), msg, true);
1706 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1707 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1708 docstring const msg =
1709 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1710 frontend::Alert::warning(_("Read Error"), msg);
1714 LayoutModuleList::const_iterator cit = celist.begin();
1715 LayoutModuleList::const_iterator cen = celist.end();
1716 for (; cit != cen; ++cit) {
1717 string const ceName = *cit;
1718 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1720 docstring const msg =
1721 bformat(_("The cite engine %1$s has been requested by\n"
1722 "this document but has not been found in the list of\n"
1723 "available engines. If you recently installed it, you\n"
1724 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1726 frontend::Alert::warning(_("Cite Engine not available"), msg);
1729 if (!ce->isAvailable() && !clone) {
1730 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1731 docstring const msg =
1732 bformat(_("The cite engine %1$s requires a package that is not\n"
1733 "available in your LaTeX installation, or a converter that\n"
1734 "you have not installed. LaTeX output may not be possible.\n"
1735 "Missing prerequisites:\n"
1737 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1738 from_utf8(ceName), prereqs);
1739 frontend::Alert::warning(_("Package not available"), msg, true);
1741 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1742 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1743 docstring const msg =
1744 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1745 frontend::Alert::warning(_("Read Error"), msg);
1753 /////////////////////////////////////////////////////////////////////////
1757 /////////////////////////////////////////////////////////////////////////
1759 DocumentClass::DocumentClass(LayoutFile const & tc)
1764 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1766 LayoutList::const_iterator it = layoutlist_.begin();
1767 LayoutList::const_iterator end = layoutlist_.end();
1768 for (; it != end; ++it)
1769 if (it->latexname() == lay)
1775 bool DocumentClass::provides(string const & p) const
1777 return provides_.find(p) != provides_.end();
1781 bool DocumentClass::hasTocLevels() const
1783 return min_toclevel_ != Layout::NOT_IN_TOC;
1787 Layout const & DocumentClass::getTOCLayout() const
1789 // we're going to look for the layout with the minimum toclevel
1790 TextClass::LayoutList::const_iterator lit = begin();
1791 TextClass::LayoutList::const_iterator const len = end();
1792 int minlevel = 1000;
1793 Layout const * lay = NULL;
1794 for (; lit != len; ++lit) {
1795 int const level = lit->toclevel;
1796 // we don't want Part or unnumbered sections
1797 if (level == Layout::NOT_IN_TOC || level < 0
1798 || level >= minlevel || lit->counter.empty())
1805 // hmm. that is very odd, so we'll do our best.
1806 return operator[](defaultLayoutName());
1810 Layout const & DocumentClass::htmlTOCLayout() const
1812 if (html_toc_section_.empty())
1813 html_toc_section_ = getTOCLayout().name();
1814 return operator[](html_toc_section_);
1818 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1819 string const & entry, bool const punct, string const & fallback) const
1821 string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1823 default_format += ".";
1825 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1826 if (itype == cite_formats_.end())
1827 return default_format;
1828 map<string, string>::const_iterator it = itype->second.find(entry);
1829 if (it == itype->second.end() && !fallback.empty())
1830 it = itype->second.find(fallback);
1831 if (it == itype->second.end())
1832 return default_format;
1834 return it->second + ".";
1839 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1840 string const & macro) const
1842 static string empty;
1843 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1844 if (itype == cite_macros_.end())
1846 map<string, string>::const_iterator it = itype->second.find(macro);
1847 if (it == itype->second.end())
1853 vector<string> const DocumentClass::citeCommands(
1854 CiteEngineType const & type) const
1856 vector<CitationStyle> const styles = citeStyles(type);
1857 vector<CitationStyle>::const_iterator it = styles.begin();
1858 vector<CitationStyle>::const_iterator end = styles.end();
1859 vector<string> cmds;
1860 for (; it != end; ++it) {
1861 CitationStyle const cite = *it;
1862 cmds.push_back(cite.name);
1868 vector<CitationStyle> const & DocumentClass::citeStyles(
1869 CiteEngineType const & type) const
1871 static vector<CitationStyle> empty;
1872 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1873 if (it == cite_styles_.end())
1879 /////////////////////////////////////////////////////////////////////////
1883 /////////////////////////////////////////////////////////////////////////
1885 ostream & operator<<(ostream & os, PageSides p)