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 string const & TextClass::prerequisites(string const & sep) const
1421 if (contains(prerequisites_, ',')) {
1422 vector<string> const pres = getVectorFromString(prerequisites_);
1423 prerequisites_ = getStringFromVector(pres, sep);
1425 return prerequisites_;
1429 bool TextClass::hasLayout(docstring const & n) const
1431 docstring const name = n.empty() ? defaultLayoutName() : n;
1433 return find_if(layoutlist_.begin(), layoutlist_.end(),
1434 LayoutNamesEqual(name))
1435 != layoutlist_.end();
1439 bool TextClass::hasInsetLayout(docstring const & n) const
1443 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1444 return it != insetlayoutlist_.end();
1448 Layout const & TextClass::operator[](docstring const & name) const
1450 LATTEST(!name.empty());
1453 find_if(begin(), end(), LayoutNamesEqual(name));
1456 LYXERR0("We failed to find the layout '" << name
1457 << "' in the layout list. You MUST investigate!");
1458 for (const_iterator cit = begin(); cit != end(); ++cit)
1459 lyxerr << " " << to_utf8(cit->name()) << endl;
1461 // We require the name to exist
1462 static const Layout dummy;
1463 LASSERT(false, return dummy);
1470 Layout & TextClass::operator[](docstring const & name)
1472 LATTEST(!name.empty());
1473 // Safe to continue, given what we do below.
1475 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1478 LYXERR0("We failed to find the layout '" << to_utf8(name)
1479 << "' in the layout list. You MUST investigate!");
1480 for (const_iterator cit = begin(); cit != end(); ++cit)
1481 LYXERR0(" " << to_utf8(cit->name()));
1483 // we require the name to exist
1485 // we are here only in release mode
1486 layoutlist_.push_back(createBasicLayout(name, true));
1487 it = find_if(begin(), end(), LayoutNamesEqual(name));
1494 bool TextClass::deleteLayout(docstring const & name)
1496 if (name == defaultLayoutName() || name == plainLayoutName())
1499 LayoutList::iterator it =
1500 remove_if(layoutlist_.begin(), layoutlist_.end(),
1501 LayoutNamesEqual(name));
1503 LayoutList::iterator end = layoutlist_.end();
1504 bool const ret = (it != end);
1505 layoutlist_.erase(it, end);
1510 bool TextClass::deleteInsetLayout(docstring const & name)
1512 return insetlayoutlist_.erase(name);
1516 // Load textclass info if not loaded yet
1517 bool TextClass::load(string const & path) const
1522 // Read style-file, provided path is searched before the system ones
1523 // If path is a file, it is loaded directly.
1524 FileName layout_file(path);
1525 if (!path.empty() && !layout_file.isReadableFile())
1526 layout_file = FileName(addName(path, name_ + ".layout"));
1527 if (layout_file.empty() || !layout_file.exists())
1528 layout_file = libFileSearch("layouts", name_, "layout");
1529 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1532 lyxerr << "Error reading `"
1533 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1534 << "'\n(Check `" << name_
1535 << "')\nCheck your installation and "
1536 "try Options/Reconfigure..."
1544 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1549 layoutlist_.push_back(createBasicLayout(n, true));
1554 string DocumentClass::forcedLayouts() const
1558 const_iterator const e = end();
1559 for (const_iterator i = begin(); i != e; ++i) {
1560 if (i->forcelocal > 0) {
1562 os << "Format " << LAYOUT_FORMAT << '\n';
1572 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1574 // FIXME The fix for the InsetLayout part of 4812 would be here:
1575 // Add the InsetLayout to the document class if it is not found.
1577 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1578 while (!n.empty()) {
1579 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1580 if (cit != cen && cit->first == n) {
1581 if (cit->second.obsoleted_by().empty())
1583 n = cit->second.obsoleted_by();
1584 return insetLayout(n);
1586 // If we have a generic prefix (e.g., "Note:"),
1587 // try if this one alone is found.
1588 size_t i = n.find(':');
1589 if (i == string::npos)
1593 // Layout "name" not found.
1594 return plainInsetLayout();
1598 InsetLayout const & DocumentClass::plainInsetLayout() {
1599 static const InsetLayout plain_insetlayout_;
1600 return plain_insetlayout_;
1604 docstring const & TextClass::defaultLayoutName() const
1606 return defaultlayout_;
1610 Layout const & TextClass::defaultLayout() const
1612 return operator[](defaultLayoutName());
1616 bool TextClass::isDefaultLayout(Layout const & layout) const
1618 return layout.name() == defaultLayoutName();
1622 bool TextClass::isPlainLayout(Layout const & layout) const
1624 return layout.name() == plainLayoutName();
1628 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1630 static Layout * defaultLayout = NULL;
1632 if (defaultLayout) {
1633 defaultLayout->setUnknown(unknown);
1634 defaultLayout->setName(name);
1635 return *defaultLayout;
1638 static char const * s = "Margin Static\n"
1639 "LatexType Paragraph\n"
1642 "AlignPossible Left, Right, Center\n"
1643 "LabelType No_Label\n"
1645 istringstream ss(s);
1646 Lexer lex(textClassTags);
1648 defaultLayout = new Layout;
1649 defaultLayout->setUnknown(unknown);
1650 defaultLayout->setName(name);
1651 if (!readStyle(lex, *defaultLayout)) {
1652 // The only way this happens is because the hardcoded layout above
1656 return *defaultLayout;
1660 DocumentClassPtr getDocumentClass(
1661 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1662 LayoutModuleList const & celist,
1665 DocumentClassPtr doc_class =
1666 DocumentClassPtr(new DocumentClass(baseClass));
1667 LayoutModuleList::const_iterator it = modlist.begin();
1668 LayoutModuleList::const_iterator en = modlist.end();
1669 for (; it != en; ++it) {
1670 string const modName = *it;
1671 LyXModule * lm = theModuleList[modName];
1673 docstring const msg =
1674 bformat(_("The module %1$s has been requested by\n"
1675 "this document but has not been found in the list of\n"
1676 "available modules. If you recently installed it, you\n"
1677 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1679 frontend::Alert::warning(_("Module not available"), msg);
1682 if (!lm->isAvailable() && !clone) {
1683 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1684 docstring const msg =
1685 bformat(_("The module %1$s requires a package that is not\n"
1686 "available in your LaTeX installation, or a converter that\n"
1687 "you have not installed. LaTeX output may not be possible.\n"
1688 "Missing prerequisites:\n"
1690 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1691 from_utf8(modName), prereqs);
1692 frontend::Alert::warning(_("Package not available"), msg, true);
1694 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1695 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1696 docstring const msg =
1697 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1698 frontend::Alert::warning(_("Read Error"), msg);
1702 LayoutModuleList::const_iterator cit = celist.begin();
1703 LayoutModuleList::const_iterator cen = celist.end();
1704 for (; cit != cen; ++cit) {
1705 string const ceName = *cit;
1706 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1708 docstring const msg =
1709 bformat(_("The cite engine %1$s has been requested by\n"
1710 "this document but has not been found in the list of\n"
1711 "available engines. If you recently installed it, you\n"
1712 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1714 frontend::Alert::warning(_("Cite Engine not available"), msg);
1717 if (!ce->isAvailable() && !clone) {
1718 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1719 docstring const msg =
1720 bformat(_("The cite engine %1$s requires a package that is not\n"
1721 "available in your LaTeX installation, or a converter that\n"
1722 "you have not installed. LaTeX output may not be possible.\n"
1723 "Missing prerequisites:\n"
1725 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1726 from_utf8(ceName), prereqs);
1727 frontend::Alert::warning(_("Package not available"), msg, true);
1729 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1730 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1731 docstring const msg =
1732 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1733 frontend::Alert::warning(_("Read Error"), msg);
1741 /////////////////////////////////////////////////////////////////////////
1745 /////////////////////////////////////////////////////////////////////////
1747 DocumentClass::DocumentClass(LayoutFile const & tc)
1752 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1754 LayoutList::const_iterator it = layoutlist_.begin();
1755 LayoutList::const_iterator end = layoutlist_.end();
1756 for (; it != end; ++it)
1757 if (it->latexname() == lay)
1763 bool DocumentClass::provides(string const & p) const
1765 return provides_.find(p) != provides_.end();
1769 bool DocumentClass::hasTocLevels() const
1771 return min_toclevel_ != Layout::NOT_IN_TOC;
1775 Layout const & DocumentClass::getTOCLayout() const
1777 // we're going to look for the layout with the minimum toclevel
1778 TextClass::LayoutList::const_iterator lit = begin();
1779 TextClass::LayoutList::const_iterator const len = end();
1780 int minlevel = 1000;
1781 Layout const * lay = NULL;
1782 for (; lit != len; ++lit) {
1783 int const level = lit->toclevel;
1784 // we don't want Part or unnumbered sections
1785 if (level == Layout::NOT_IN_TOC || level < 0
1786 || level >= minlevel || lit->counter.empty())
1793 // hmm. that is very odd, so we'll do our best.
1794 return operator[](defaultLayoutName());
1798 Layout const & DocumentClass::htmlTOCLayout() const
1800 if (html_toc_section_.empty())
1801 html_toc_section_ = getTOCLayout().name();
1802 return operator[](html_toc_section_);
1806 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1807 string const & entry, bool const punct, string const & fallback) const
1809 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%]]}";
1811 default_format += ".";
1813 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1814 if (itype == cite_formats_.end())
1815 return default_format;
1816 map<string, string>::const_iterator it = itype->second.find(entry);
1817 if (it == itype->second.end() && !fallback.empty())
1818 it = itype->second.find(fallback);
1819 if (it == itype->second.end())
1820 return default_format;
1822 return it->second + ".";
1827 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1828 string const & macro) const
1830 static string empty;
1831 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1832 if (itype == cite_macros_.end())
1834 map<string, string>::const_iterator it = itype->second.find(macro);
1835 if (it == itype->second.end())
1841 vector<string> const DocumentClass::citeCommands(
1842 CiteEngineType const & type) const
1844 vector<CitationStyle> const styles = citeStyles(type);
1845 vector<CitationStyle>::const_iterator it = styles.begin();
1846 vector<CitationStyle>::const_iterator end = styles.end();
1847 vector<string> cmds;
1848 for (; it != end; ++it) {
1849 CitationStyle const cite = *it;
1850 cmds.push_back(cite.name);
1856 vector<CitationStyle> const & DocumentClass::citeStyles(
1857 CiteEngineType const & type) const
1859 static vector<CitationStyle> empty;
1860 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1861 if (it == cite_styles_.end())
1867 /////////////////////////////////////////////////////////////////////////
1871 /////////////////////////////////////////////////////////////////////////
1873 ostream & operator<<(ostream & os, PageSides p)