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 = 62; //spitz PassThru for arguments.
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),
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,
233 LexerKeyword textClassTags[] = {
234 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
235 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
236 { "addtopreamble", TC_ADDTOPREAMBLE },
237 { "citeengine", TC_CITEENGINE },
238 { "citeenginetype", TC_CITEENGINETYPE },
239 { "citeformat", TC_CITEFORMAT },
240 { "citeframework", TC_CITEFRAMEWORK },
241 { "classoptions", TC_CLASSOPTIONS },
242 { "columns", TC_COLUMNS },
243 { "counter", TC_COUNTER },
244 { "defaultbiblio", TC_DEFAULTBIBLIO },
245 { "defaultfont", TC_DEFAULTFONT },
246 { "defaultmodule", TC_DEFAULTMODULE },
247 { "defaultstyle", TC_DEFAULTSTYLE },
248 { "excludesmodule", TC_EXCLUDESMODULE },
249 { "float", TC_FLOAT },
250 { "format", TC_FORMAT },
251 { "fullauthorlist", TC_FULLAUTHORLIST },
252 { "htmlpreamble", TC_HTMLPREAMBLE },
253 { "htmlstyles", TC_HTMLSTYLES },
254 { "htmltocsection", TC_HTMLTOCSECTION },
255 { "ifcounter", TC_IFCOUNTER },
256 { "input", TC_INPUT },
257 { "insetlayout", TC_INSETLAYOUT },
258 { "leftmargin", TC_LEFTMARGIN },
259 { "modifystyle", TC_MODIFYSTYLE },
260 { "nocounter", TC_NOCOUNTER },
261 { "nofloat", TC_NOFLOAT },
262 { "noinsetlayout", TC_NOINSETLAYOUT },
263 { "nostyle", TC_NOSTYLE },
264 { "outlinername", TC_OUTLINERNAME },
265 { "outputformat", TC_OUTPUTFORMAT },
266 { "outputtype", TC_OUTPUTTYPE },
267 { "packageoptions", TC_PKGOPTS },
268 { "pagestyle", TC_PAGESTYLE },
269 { "preamble", TC_PREAMBLE },
270 { "provides", TC_PROVIDES },
271 { "providesmodule", TC_PROVIDESMODULE },
272 { "providestyle", TC_PROVIDESTYLE },
273 { "requires", TC_REQUIRES },
274 { "rightmargin", TC_RIGHTMARGIN },
275 { "secnumdepth", TC_SECNUMDEPTH },
276 { "sides", TC_SIDES },
277 { "style", TC_STYLE },
278 { "titlelatexname", TC_TITLELATEXNAME },
279 { "titlelatextype", TC_TITLELATEXTYPE },
280 { "tocdepth", TC_TOCDEPTH }
286 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
288 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
289 TempFile tmp("convertXXXXXX.layout");
290 FileName const tempfile = tmp.name();
291 bool success = layout2layout(filename, tempfile);
293 success = readWithoutConv(tempfile, rt) == OK;
298 std::string TextClass::convert(std::string const & str)
300 TempFile tmp1("localXXXXXX.layout");
301 FileName const fn = tmp1.name();
302 ofstream os(fn.toFilesystemEncoding().c_str());
305 TempFile tmp2("convert_localXXXXXX.layout");
306 FileName const tempfile = tmp2.name();
307 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
310 ifstream is(tempfile.toFilesystemEncoding().c_str());
322 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
324 if (!filename.isReadableFile()) {
325 lyxerr << "Cannot read layout file `" << filename << "'."
330 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
331 to_utf8(makeDisplayPath(filename.absFileName())));
333 // Define the plain layout used in table cells, ert, etc. Note that
334 // we do this before loading any layout file, so that classes can
335 // override features of this layout if they should choose to do so.
336 if (rt == BASECLASS && !hasLayout(plain_layout_))
337 layoutlist_.push_back(createBasicLayout(plain_layout_));
339 Lexer lexrc(textClassTags);
340 lexrc.setFile(filename);
341 ReturnValues retval = read(lexrc, rt);
343 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
344 to_utf8(makeDisplayPath(filename.absFileName())));
350 bool TextClass::read(FileName const & filename, ReadType rt)
352 ReturnValues const retval = readWithoutConv(filename, rt);
353 if (retval != FORMAT_MISMATCH)
356 bool const worx = convertLayoutFormat(filename, rt);
358 LYXERR0 ("Unable to convert " << filename <<
359 " to format " << LAYOUT_FORMAT);
364 TextClass::ReturnValues TextClass::validate(std::string const & str)
367 return tc.read(str, VALIDATION);
371 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
373 Lexer lexrc(textClassTags);
374 istringstream is(str);
376 ReturnValues retval = read(lexrc, rt);
378 if (retval != FORMAT_MISMATCH)
381 // write the layout string to a temporary file
382 TempFile tmp("TextClass_read");
383 FileName const tempfile = tmp.name();
384 ofstream os(tempfile.toFilesystemEncoding().c_str());
386 LYXERR0("Unable to create temporary file");
392 // now try to convert it to LAYOUT_FORMAT
393 if (!convertLayoutFormat(tempfile, rt)) {
394 LYXERR0("Unable to convert internal layout information to format "
403 // Reads a textclass structure from file.
404 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
409 // Format of files before the 'Format' tag was introduced
414 while (lexrc.isOK() && !error) {
415 int le = lexrc.lex();
418 case Lexer::LEX_FEOF:
421 case Lexer::LEX_UNDEF:
422 lexrc.printError("Unknown TextClass tag `$$Token'");
430 // used below to track whether we are in an IfStyle or IfCounter tag.
431 bool modifystyle = false;
432 bool providestyle = false;
433 bool ifcounter = false;
435 switch (static_cast<TextClassTags>(le)) {
439 format = lexrc.getInteger();
442 case TC_OUTPUTFORMAT:
444 outputFormat_ = lexrc.getString();
448 readOutputType(lexrc);
449 switch(outputType_) {
451 outputFormat_ = "latex";
454 outputFormat_ = "docbook";
457 outputFormat_ = "literate";
462 case TC_INPUT: // Include file
464 string const inc = lexrc.getString();
465 FileName tmp = libFileSearch("layouts", inc,
469 lexrc.printError("Could not find input file: " + inc);
471 } else if (!read(tmp, MERGE)) {
472 lexrc.printError("Error reading input file: " + tmp.absFileName());
478 case TC_DEFAULTSTYLE:
480 docstring const name = from_utf8(subst(lexrc.getString(),
482 defaultlayout_ = name;
489 case TC_PROVIDESTYLE:
490 // if modifystyle is true, then we got here by falling through
491 // so we are not in an ProvideStyle block
497 lexrc.printError("No name given for style: `$$Token'.");
501 docstring const name = from_utf8(subst(lexrc.getString(),
504 string s = "Could not read name for style: `$$Token' "
505 + lexrc.getString() + " is probably not valid UTF-8!";
508 // Since we couldn't read the name, we just scan the rest
509 // of the style and discard it.
510 error = !readStyle(lexrc, lay);
514 bool const have_layout = hasLayout(name);
516 // If the layout already exists, then we want to add it to
517 // the existing layout, as long as we are not in an ProvideStyle
519 if (have_layout && !providestyle) {
520 Layout & lay = operator[](name);
521 error = !readStyle(lexrc, lay);
523 // If the layout does not exist, then we want to create a new
524 // one, but not if we are in a ModifyStyle block.
525 else if (!have_layout && !modifystyle) {
527 layout.setName(name);
528 error = !readStyle(lexrc, layout);
530 layoutlist_.push_back(layout);
532 if (defaultlayout_.empty()) {
533 // We do not have a default layout yet, so we choose
534 // the first layout we encounter.
535 defaultlayout_ = name;
538 // There are two ways to get here:
539 // (i) The layout exists but we are in an ProvideStyle block
540 // (ii) The layout doesn't exist, but we are in an ModifyStyle
542 // Either way, we just scan the rest and discard it
545 // false positive from coverity
546 // coverity[CHECKED_RETURN]
547 readStyle(lexrc, lay);
554 docstring const style = from_utf8(subst(lexrc.getString(),
556 if (!deleteLayout(style))
557 lyxerr << "Cannot delete style `"
558 << to_utf8(style) << '\'' << endl;
562 case TC_NOINSETLAYOUT:
564 docstring const style = from_utf8(subst(lexrc.getString(),
566 if (!deleteInsetLayout(style))
567 LYXERR0("Style `" << style << "' cannot be removed\n"
568 "because it was not found!");
574 columns_ = lexrc.getInteger();
579 switch (lexrc.getInteger()) {
580 case 1: sides_ = OneSide; break;
581 case 2: sides_ = TwoSides; break;
583 lyxerr << "Impossible number of page"
584 " sides, setting to one."
594 pagestyle_ = rtrim(lexrc.getString());
598 defaultfont_ = lyxRead(lexrc);
599 if (!defaultfont_.resolved()) {
600 lexrc.printError("Warning: defaultfont should "
601 "be fully instantiated!");
602 defaultfont_.realize(sane_font);
608 secnumdepth_ = lexrc.getInteger();
613 tocdepth_ = lexrc.getInteger();
616 // First step to support options
617 case TC_CLASSOPTIONS:
618 readClassOptions(lexrc);
622 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
625 case TC_HTMLPREAMBLE:
626 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
630 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
633 case TC_HTMLTOCSECTION:
634 html_toc_section_ = from_utf8(trim(lexrc.getString()));
637 case TC_ADDTOPREAMBLE:
638 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
641 case TC_ADDTOHTMLPREAMBLE:
642 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
645 case TC_ADDTOHTMLSTYLES:
646 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
651 string const feature = lexrc.getString();
653 if (lexrc.getInteger())
654 provides_.insert(feature);
656 provides_.erase(feature);
662 vector<string> const req
663 = getVectorFromString(lexrc.getString());
664 requires_.insert(req.begin(), req.end());
670 string const pkg = lexrc.getString();
672 string const options = lexrc.getString();
673 package_options_[pkg] = options;
677 case TC_DEFAULTMODULE: {
679 string const module = lexrc.getString();
680 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
681 default_modules_.push_back(module);
685 case TC_PROVIDESMODULE: {
687 string const module = lexrc.getString();
688 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
689 provided_modules_.push_back(module);
693 case TC_EXCLUDESMODULE: {
695 string const module = lexrc.getString();
696 // modules already have their own way to exclude other modules
698 LYXERR0("ExcludesModule tag cannot be used in a module!");
701 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
702 excluded_modules_.push_back(module);
706 case TC_LEFTMARGIN: // left margin type
708 leftmargin_ = lexrc.getDocString();
711 case TC_RIGHTMARGIN: // right margin type
713 rightmargin_ = lexrc.getDocString();
716 case TC_INSETLAYOUT: {
718 lexrc.printError("No name given for InsetLayout: `$$Token'.");
722 docstring const name = subst(lexrc.getDocString(), '_', ' ');
724 string s = "Could not read name for InsetLayout: `$$Token' "
725 + lexrc.getString() + " is probably not valid UTF-8!";
728 // Since we couldn't read the name, we just scan the rest
729 // of the style and discard it.
730 il.read(lexrc, *this);
731 // Let's try to continue rather than abort.
733 } else if (hasInsetLayout(name)) {
734 InsetLayout & il = insetlayoutlist_[name];
735 error = !il.read(lexrc, *this);
739 error = !il.read(lexrc, *this);
741 insetlayoutlist_[name] = il;
747 error = !readFloat(lexrc);
751 error = !readCiteEngine(lexrc);
754 case TC_CITEENGINETYPE:
756 opt_enginetype_ = rtrim(lexrc.getString());
760 error = !readCiteFormat(lexrc);
763 case TC_CITEFRAMEWORK:
765 citeframework_ = rtrim(lexrc.getString());
768 case TC_DEFAULTBIBLIO:
770 vector<string> const dbs =
771 getVectorFromString(rtrim(lexrc.getString()), "|");
772 vector<string>::const_iterator it = dbs.begin();
773 vector<string>::const_iterator end = dbs.end();
774 for (; it != end; ++it) {
775 if (!contains(*it, ':'))
776 cite_default_biblio_style_[opt_enginetype_] = *it;
779 string const db = split(*it, eng, ':');
780 cite_default_biblio_style_[eng] = db;
786 case TC_FULLAUTHORLIST:
788 cite_full_author_list_ &= lexrc.getBool();
793 docstring const cnt = lexrc.getDocString();
794 if (!counters_.remove(cnt))
795 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
804 docstring const name = lexrc.getDocString();
806 string s = "Could not read name for counter: `$$Token' "
807 + lexrc.getString() + " is probably not valid UTF-8!";
808 lexrc.printError(s.c_str());
810 // Since we couldn't read the name, we just scan the rest
814 error = !counters_.read(lexrc, name, !ifcounter);
817 lexrc.printError("No name given for style: `$$Token'.");
822 case TC_TITLELATEXTYPE:
823 readTitleType(lexrc);
826 case TC_TITLELATEXNAME:
828 titlename_ = lexrc.getString();
833 string const nofloat = lexrc.getString();
834 floatlist_.erase(nofloat);
838 case TC_OUTLINERNAME:
839 error = !readOutlinerName(lexrc);
843 // Note that this is triggered the first time through the loop unless
844 // we hit a format tag.
845 if (format != LAYOUT_FORMAT)
846 return FORMAT_MISMATCH;
849 // at present, we abort if we encounter an error,
850 // so there is no point continuing.
857 if (defaultlayout_.empty()) {
858 LYXERR0("Error: Textclass '" << name_
859 << "' is missing a defaultstyle.");
863 // Try to erase "stdinsets" from the provides_ set.
865 // Provides stdinsets 1
866 // declaration simply tells us that the standard insets have been
867 // defined. (It's found in stdinsets.inc but could also be used in
868 // user-defined files.) There isn't really any such package. So we
869 // might as well go ahead and erase it.
870 // If we do not succeed, then it was not there, which means that
871 // the textclass did not provide the definitions of the standard
872 // insets. So we need to try to load them.
873 int erased = provides_.erase("stdinsets");
875 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
878 frontend::Alert::warning(_("Missing File"),
879 _("Could not find stdinsets.inc! This may lead to data loss!"));
881 } else if (!read(tmp, MERGE)) {
882 frontend::Alert::warning(_("Corrupt File"),
883 _("Could not read stdinsets.inc! This may lead to data loss!"));
888 min_toclevel_ = Layout::NOT_IN_TOC;
889 max_toclevel_ = Layout::NOT_IN_TOC;
890 const_iterator lit = begin();
891 const_iterator len = end();
892 for (; lit != len; ++lit) {
893 int const toclevel = lit->toclevel;
894 if (toclevel != Layout::NOT_IN_TOC) {
895 if (min_toclevel_ == Layout::NOT_IN_TOC)
896 min_toclevel_ = toclevel;
898 min_toclevel_ = min(min_toclevel_, toclevel);
899 max_toclevel_ = max(max_toclevel_, toclevel);
902 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
903 << ", maximum is " << max_toclevel_);
905 return (error ? ERROR : OK);
909 void TextClass::readTitleType(Lexer & lexrc)
911 LexerKeyword titleTypeTags[] = {
912 { "commandafter", TITLE_COMMAND_AFTER },
913 { "environment", TITLE_ENVIRONMENT }
916 PushPopHelper pph(lexrc, titleTypeTags);
918 int le = lexrc.lex();
920 case Lexer::LEX_UNDEF:
921 lexrc.printError("Unknown output type `$$Token'");
923 case TITLE_COMMAND_AFTER:
924 case TITLE_ENVIRONMENT:
925 titletype_ = static_cast<TitleLatexType>(le);
928 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
934 void TextClass::readOutputType(Lexer & lexrc)
936 LexerKeyword outputTypeTags[] = {
937 { "docbook", DOCBOOK },
939 { "literate", LITERATE }
942 PushPopHelper pph(lexrc, outputTypeTags);
944 int le = lexrc.lex();
946 case Lexer::LEX_UNDEF:
947 lexrc.printError("Unknown output type `$$Token'");
952 outputType_ = static_cast<OutputType>(le);
955 LYXERR0("Unhandled value " << le);
961 void TextClass::readClassOptions(Lexer & lexrc)
971 LexerKeyword classOptionsTags[] = {
973 {"fontsize", CO_FONTSIZE },
974 {"header", CO_HEADER },
975 {"other", CO_OTHER },
976 {"pagestyle", CO_PAGESTYLE }
979 lexrc.pushTable(classOptionsTags);
981 while (!getout && lexrc.isOK()) {
982 int le = lexrc.lex();
984 case Lexer::LEX_UNDEF:
985 lexrc.printError("Unknown ClassOption tag `$$Token'");
993 opt_fontsize_ = rtrim(lexrc.getString());
997 opt_pagestyle_ = rtrim(lexrc.getString());
1001 if (options_.empty())
1002 options_ = lexrc.getString();
1004 options_ += ',' + lexrc.getString();
1008 class_header_ = subst(lexrc.getString(), """, "\"");
1019 bool TextClass::readCiteEngine(Lexer & lexrc)
1021 int const type = readCiteEngineType(lexrc);
1022 if (type & ENGINE_TYPE_AUTHORYEAR)
1023 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1024 if (type & ENGINE_TYPE_NUMERICAL)
1025 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1026 if (type & ENGINE_TYPE_DEFAULT)
1027 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1029 bool getout = false;
1030 while (!getout && lexrc.isOK()) {
1032 def = lexrc.getString();
1033 def = subst(def, " ", "");
1034 def = subst(def, "\t", "");
1035 if (compare_ascii_no_case(def, "end") == 0) {
1040 char ichar = def[0];
1043 if (isUpperCase(ichar)) {
1044 cs.forceUpperCase = true;
1045 def[0] = lowercase(ichar);
1048 /** For portability reasons (between different
1049 * cite engines such as natbib and biblatex),
1050 * we distinguish between:
1051 * 1. The LyX name as output in the LyX file
1052 * 2. Possible aliases that might fall back to
1053 * the given LyX name in the current engine
1054 * 3. The actual LaTeX command that is output
1055 * (2) and (3) are optional.
1056 * Also, the GUI string for the starred version can
1059 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1068 ScanMode mode = LyXName;
1069 ScanMode oldmode = LyXName;
1074 size_t const n = def.size();
1075 for (size_t i = 0; i != n; ++i) {
1079 else if (ichar == '=')
1081 else if (ichar == '<') {
1084 } else if (ichar == '>')
1086 else if (mode == LaTeXCmd)
1088 else if (mode == StarDesc)
1090 else if (ichar == '*')
1091 cs.hasStarredVersion = true;
1092 else if (ichar == '[' && cs.textAfter)
1093 cs.textBefore = true;
1094 else if (ichar == '[')
1095 cs.textAfter = true;
1096 else if (ichar != ']') {
1104 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1105 if (!alias.empty()) {
1106 vector<string> const aliases = getVectorFromString(alias);
1107 for (string const &s: aliases)
1108 cite_command_aliases_[s] = lyx_cmd;
1110 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1111 int size = stardesc.size();
1113 cs.stardesc = stardescs[0];
1115 cs.startooltip = stardescs[1];
1116 if (type & ENGINE_TYPE_AUTHORYEAR)
1117 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1118 if (type & ENGINE_TYPE_NUMERICAL)
1119 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1120 if (type & ENGINE_TYPE_DEFAULT)
1121 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1127 int TextClass::readCiteEngineType(Lexer & lexrc) const
1129 LATTEST(ENGINE_TYPE_DEFAULT ==
1130 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1131 if (!lexrc.next()) {
1132 lexrc.printError("No cite engine type given for token: `$$Token'.");
1133 return ENGINE_TYPE_DEFAULT;
1135 string const type = rtrim(lexrc.getString());
1136 if (compare_ascii_no_case(type, "authoryear") == 0)
1137 return ENGINE_TYPE_AUTHORYEAR;
1138 else if (compare_ascii_no_case(type, "numerical") == 0)
1139 return ENGINE_TYPE_NUMERICAL;
1140 else if (compare_ascii_no_case(type, "default") != 0) {
1141 string const s = "Unknown cite engine type `" + type
1142 + "' given for token: `$$Token',";
1143 lexrc.printError(s);
1145 return ENGINE_TYPE_DEFAULT;
1149 bool TextClass::readCiteFormat(Lexer & lexrc)
1151 int const type = readCiteEngineType(lexrc);
1154 while (lexrc.isOK()) {
1156 etype = lexrc.getString();
1157 if (compare_ascii_no_case(etype, "end") == 0)
1162 definition = lexrc.getString();
1163 char initchar = etype[0];
1164 if (initchar == '#')
1166 if (initchar == '!' || initchar == '_') {
1167 if (type & ENGINE_TYPE_AUTHORYEAR)
1168 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1169 if (type & ENGINE_TYPE_NUMERICAL)
1170 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1171 if (type & ENGINE_TYPE_DEFAULT)
1172 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1174 if (type & ENGINE_TYPE_AUTHORYEAR)
1175 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1176 if (type & ENGINE_TYPE_NUMERICAL)
1177 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1178 if (type & ENGINE_TYPE_DEFAULT)
1179 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1186 bool TextClass::readFloat(Lexer & lexrc)
1203 FT_ALLOWED_PLACEMENT,
1209 LexerKeyword floatTags[] = {
1210 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1211 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1212 { "allowswide", FT_ALLOWS_WIDE },
1214 { "extension", FT_EXT },
1215 { "guiname", FT_NAME },
1216 { "htmlattr", FT_HTMLATTR },
1217 { "htmlstyle", FT_HTMLSTYLE },
1218 { "htmltag", FT_HTMLTAG },
1219 { "ispredefined", FT_PREDEFINED },
1220 { "listcommand", FT_LISTCOMMAND },
1221 { "listname", FT_LISTNAME },
1222 { "numberwithin", FT_WITHIN },
1223 { "placement", FT_PLACEMENT },
1224 { "refprefix", FT_REFPREFIX },
1225 { "style", FT_STYLE },
1226 { "type", FT_TYPE },
1227 { "usesfloatpkg", FT_USESFLOAT }
1230 lexrc.pushTable(floatTags);
1234 docstring htmlstyle;
1240 string allowed_placement = "!htbpH";
1245 bool usesfloat = true;
1246 bool ispredefined = false;
1247 bool allowswide = true;
1248 bool allowssideways = true;
1250 bool getout = false;
1251 while (!getout && lexrc.isOK()) {
1252 int le = lexrc.lex();
1254 case Lexer::LEX_UNDEF:
1255 lexrc.printError("Unknown float tag `$$Token'");
1263 type = lexrc.getString();
1264 if (floatlist_.typeExist(type)) {
1265 Floating const & fl = floatlist_.getType(type);
1266 placement = fl.placement();
1268 within = fl.within();
1271 listname = fl.listName();
1272 usesfloat = fl.usesFloatPkg();
1273 ispredefined = fl.isPredefined();
1274 listcommand = fl.listCommand();
1275 refprefix = fl.refPrefix();
1280 name = lexrc.getString();
1284 placement = lexrc.getString();
1286 case FT_ALLOWED_PLACEMENT:
1288 allowed_placement = lexrc.getString();
1292 ext = lexrc.getString();
1296 within = lexrc.getString();
1297 if (within == "none")
1302 style = lexrc.getString();
1304 case FT_LISTCOMMAND:
1306 listcommand = lexrc.getString();
1310 refprefix = lexrc.getString();
1314 listname = lexrc.getString();
1318 usesfloat = lexrc.getBool();
1322 ispredefined = lexrc.getBool();
1324 case FT_ALLOWS_SIDEWAYS:
1326 allowssideways = lexrc.getBool();
1328 case FT_ALLOWS_WIDE:
1330 allowswide = lexrc.getBool();
1334 htmlattr = lexrc.getString();
1338 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1342 htmltag = lexrc.getString();
1352 // Here we have a full float if getout == true
1354 if (!usesfloat && listcommand.empty()) {
1355 // if this float uses the same auxfile as an existing one,
1356 // there is no need for it to provide a list command.
1357 FloatList::const_iterator it = floatlist_.begin();
1358 FloatList::const_iterator en = floatlist_.end();
1359 bool found_ext = false;
1360 for (; it != en; ++it) {
1361 if (it->second.ext() == ext) {
1367 LYXERR0("The layout does not provide a list command " <<
1368 "for the float `" << type << "'. LyX will " <<
1369 "not be able to produce a float list.");
1371 Floating fl(type, placement, ext, within, style, name,
1372 listname, listcommand, refprefix, allowed_placement,
1373 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1374 allowswide, allowssideways);
1375 floatlist_.newFloat(fl);
1376 // each float has its own counter
1377 counters_.newCounter(from_ascii(type), from_ascii(within),
1378 docstring(), docstring());
1379 // also define sub-float counters
1380 docstring const subtype = "sub-" + from_ascii(type);
1381 counters_.newCounter(subtype, from_ascii(type),
1382 "\\alph{" + subtype + "}", docstring());
1388 bool TextClass::readOutlinerName(Lexer & lexrc)
1393 type = lexrc.getString();
1395 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1399 name = lexrc.getDocString();
1401 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1404 outliner_names_[type] = name;
1409 docstring TextClass::outlinerName(std::string const & type) const
1411 std::map<std::string,docstring>::const_iterator const it
1412 = outliner_names_.find(type);
1413 if (it == outliner_names_.end()) {
1414 LYXERR0("Missing OutlinerName for " << type << "!");
1415 return from_utf8(type);
1421 string const & TextClass::prerequisites(string const & sep) const
1423 if (contains(prerequisites_, ',')) {
1424 vector<string> const pres = getVectorFromString(prerequisites_);
1425 prerequisites_ = getStringFromVector(pres, sep);
1427 return prerequisites_;
1431 bool TextClass::hasLayout(docstring const & n) const
1433 docstring const name = n.empty() ? defaultLayoutName() : n;
1435 return find_if(layoutlist_.begin(), layoutlist_.end(),
1436 LayoutNamesEqual(name))
1437 != layoutlist_.end();
1441 bool TextClass::hasInsetLayout(docstring const & n) const
1445 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1446 return it != insetlayoutlist_.end();
1450 Layout const & TextClass::operator[](docstring const & name) const
1452 LATTEST(!name.empty());
1455 find_if(begin(), end(), LayoutNamesEqual(name));
1458 LYXERR0("We failed to find the layout '" << name
1459 << "' in the layout list. You MUST investigate!");
1460 for (const_iterator cit = begin(); cit != end(); ++cit)
1461 lyxerr << " " << to_utf8(cit->name()) << endl;
1463 // We require the name to exist
1464 static const Layout dummy;
1465 LASSERT(false, return dummy);
1472 Layout & TextClass::operator[](docstring const & name)
1474 LATTEST(!name.empty());
1475 // Safe to continue, given what we do below.
1477 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1480 LYXERR0("We failed to find the layout '" << to_utf8(name)
1481 << "' in the layout list. You MUST investigate!");
1482 for (const_iterator cit = begin(); cit != end(); ++cit)
1483 LYXERR0(" " << to_utf8(cit->name()));
1485 // we require the name to exist
1487 // we are here only in release mode
1488 layoutlist_.push_back(createBasicLayout(name, true));
1489 it = find_if(begin(), end(), LayoutNamesEqual(name));
1496 bool TextClass::deleteLayout(docstring const & name)
1498 if (name == defaultLayoutName() || name == plainLayoutName())
1501 LayoutList::iterator it =
1502 remove_if(layoutlist_.begin(), layoutlist_.end(),
1503 LayoutNamesEqual(name));
1505 LayoutList::iterator end = layoutlist_.end();
1506 bool const ret = (it != end);
1507 layoutlist_.erase(it, end);
1512 bool TextClass::deleteInsetLayout(docstring const & name)
1514 return insetlayoutlist_.erase(name);
1518 // Load textclass info if not loaded yet
1519 bool TextClass::load(string const & path) const
1524 // Read style-file, provided path is searched before the system ones
1525 // If path is a file, it is loaded directly.
1526 FileName layout_file(path);
1527 if (!path.empty() && !layout_file.isReadableFile())
1528 layout_file = FileName(addName(path, name_ + ".layout"));
1529 if (layout_file.empty() || !layout_file.exists())
1530 layout_file = libFileSearch("layouts", name_, "layout");
1531 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1534 lyxerr << "Error reading `"
1535 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1536 << "'\n(Check `" << name_
1537 << "')\nCheck your installation and "
1538 "try Options/Reconfigure..."
1546 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1551 layoutlist_.push_back(createBasicLayout(n, true));
1556 string DocumentClass::forcedLayouts() const
1560 const_iterator const e = end();
1561 for (const_iterator i = begin(); i != e; ++i) {
1562 if (i->forcelocal > 0) {
1564 os << "Format " << LAYOUT_FORMAT << '\n';
1574 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1576 // FIXME The fix for the InsetLayout part of 4812 would be here:
1577 // Add the InsetLayout to the document class if it is not found.
1579 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1580 while (!n.empty()) {
1581 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1582 if (cit != cen && cit->first == n) {
1583 if (cit->second.obsoleted_by().empty())
1585 n = cit->second.obsoleted_by();
1586 return insetLayout(n);
1588 // If we have a generic prefix (e.g., "Note:"),
1589 // try if this one alone is found.
1590 size_t i = n.find(':');
1591 if (i == string::npos)
1595 // Layout "name" not found.
1596 return plainInsetLayout();
1600 InsetLayout const & DocumentClass::plainInsetLayout() {
1601 static const InsetLayout plain_insetlayout_;
1602 return plain_insetlayout_;
1606 docstring const & TextClass::defaultLayoutName() const
1608 return defaultlayout_;
1612 Layout const & TextClass::defaultLayout() const
1614 return operator[](defaultLayoutName());
1618 bool TextClass::isDefaultLayout(Layout const & layout) const
1620 return layout.name() == defaultLayoutName();
1624 bool TextClass::isPlainLayout(Layout const & layout) const
1626 return layout.name() == plainLayoutName();
1630 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1632 static Layout * defaultLayout = NULL;
1634 if (defaultLayout) {
1635 defaultLayout->setUnknown(unknown);
1636 defaultLayout->setName(name);
1637 return *defaultLayout;
1640 static char const * s = "Margin Static\n"
1641 "LatexType Paragraph\n"
1644 "AlignPossible Left, Right, Center\n"
1645 "LabelType No_Label\n"
1647 istringstream ss(s);
1648 Lexer lex(textClassTags);
1650 defaultLayout = new Layout;
1651 defaultLayout->setUnknown(unknown);
1652 defaultLayout->setName(name);
1653 if (!readStyle(lex, *defaultLayout)) {
1654 // The only way this happens is because the hardcoded layout above
1658 return *defaultLayout;
1662 DocumentClassPtr getDocumentClass(
1663 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1664 LayoutModuleList const & celist,
1667 DocumentClassPtr doc_class =
1668 DocumentClassPtr(new DocumentClass(baseClass));
1669 LayoutModuleList::const_iterator it = modlist.begin();
1670 LayoutModuleList::const_iterator en = modlist.end();
1671 for (; it != en; ++it) {
1672 string const modName = *it;
1673 LyXModule * lm = theModuleList[modName];
1675 docstring const msg =
1676 bformat(_("The module %1$s has been requested by\n"
1677 "this document but has not been found in the list of\n"
1678 "available modules. If you recently installed it, you\n"
1679 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1681 frontend::Alert::warning(_("Module not available"), msg);
1684 if (!lm->isAvailable() && !clone) {
1685 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1686 docstring const msg =
1687 bformat(_("The module %1$s requires a package that is not\n"
1688 "available in your LaTeX installation, or a converter that\n"
1689 "you have not installed. LaTeX output may not be possible.\n"
1690 "Missing prerequisites:\n"
1692 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1693 from_utf8(modName), prereqs);
1694 frontend::Alert::warning(_("Package not available"), msg, true);
1696 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1697 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1698 docstring const msg =
1699 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1700 frontend::Alert::warning(_("Read Error"), msg);
1704 LayoutModuleList::const_iterator cit = celist.begin();
1705 LayoutModuleList::const_iterator cen = celist.end();
1706 for (; cit != cen; ++cit) {
1707 string const ceName = *cit;
1708 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1710 docstring const msg =
1711 bformat(_("The cite engine %1$s has been requested by\n"
1712 "this document but has not been found in the list of\n"
1713 "available engines. If you recently installed it, you\n"
1714 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1716 frontend::Alert::warning(_("Cite Engine not available"), msg);
1719 if (!ce->isAvailable() && !clone) {
1720 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1721 docstring const msg =
1722 bformat(_("The cite engine %1$s requires a package that is not\n"
1723 "available in your LaTeX installation, or a converter that\n"
1724 "you have not installed. LaTeX output may not be possible.\n"
1725 "Missing prerequisites:\n"
1727 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1728 from_utf8(ceName), prereqs);
1729 frontend::Alert::warning(_("Package not available"), msg, true);
1731 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1732 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1733 docstring const msg =
1734 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1735 frontend::Alert::warning(_("Read Error"), msg);
1743 /////////////////////////////////////////////////////////////////////////
1747 /////////////////////////////////////////////////////////////////////////
1749 DocumentClass::DocumentClass(LayoutFile const & tc)
1754 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1756 LayoutList::const_iterator it = layoutlist_.begin();
1757 LayoutList::const_iterator end = layoutlist_.end();
1758 for (; it != end; ++it)
1759 if (it->latexname() == lay)
1765 bool DocumentClass::provides(string const & p) const
1767 return provides_.find(p) != provides_.end();
1771 bool DocumentClass::hasTocLevels() const
1773 return min_toclevel_ != Layout::NOT_IN_TOC;
1777 Layout const & DocumentClass::getTOCLayout() const
1779 // we're going to look for the layout with the minimum toclevel
1780 TextClass::LayoutList::const_iterator lit = begin();
1781 TextClass::LayoutList::const_iterator const len = end();
1782 int minlevel = 1000;
1783 Layout const * lay = NULL;
1784 for (; lit != len; ++lit) {
1785 int const level = lit->toclevel;
1786 // we don't want Part or unnumbered sections
1787 if (level == Layout::NOT_IN_TOC || level < 0
1788 || level >= minlevel || lit->counter.empty())
1795 // hmm. that is very odd, so we'll do our best.
1796 return operator[](defaultLayoutName());
1800 Layout const & DocumentClass::htmlTOCLayout() const
1802 if (html_toc_section_.empty())
1803 html_toc_section_ = getTOCLayout().name();
1804 return operator[](html_toc_section_);
1808 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1809 string const & entry, string const & fallback) const
1811 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
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;
1825 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1826 string const & macro) const
1828 static string empty;
1829 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1830 if (itype == cite_macros_.end())
1832 map<string, string>::const_iterator it = itype->second.find(macro);
1833 if (it == itype->second.end())
1839 vector<string> const DocumentClass::citeCommands(
1840 CiteEngineType const & type) const
1842 vector<CitationStyle> const styles = citeStyles(type);
1843 vector<CitationStyle>::const_iterator it = styles.begin();
1844 vector<CitationStyle>::const_iterator end = styles.end();
1845 vector<string> cmds;
1846 for (; it != end; ++it) {
1847 CitationStyle const cite = *it;
1848 cmds.push_back(cite.name);
1854 vector<CitationStyle> const & DocumentClass::citeStyles(
1855 CiteEngineType const & type) const
1857 static vector<CitationStyle> empty;
1858 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1859 if (it == cite_styles_.end())
1865 /////////////////////////////////////////////////////////////////////////
1869 /////////////////////////////////////////////////////////////////////////
1871 ostream & operator<<(ostream & os, PageSides p)