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.
1057 * LyXName|alias,nextalias*[][]=latexcmd
1065 ScanMode mode = LyXName;
1069 size_t const n = def.size();
1070 for (size_t i = 0; i != n; ++i) {
1074 else if (ichar == '=')
1076 else if (mode == LaTeXCmd)
1078 else if (ichar == '*')
1079 cs.fullAuthorList = true;
1080 else if (ichar == '[' && cs.textAfter)
1081 cs.textBefore = true;
1082 else if (ichar == '[')
1083 cs.textAfter = true;
1084 else if (ichar != ']') {
1092 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1093 if (!alias.empty()) {
1094 vector<string> const aliases = getVectorFromString(alias);
1095 for (string const &s: aliases)
1096 cite_command_aliases_[s] = lyx_cmd;
1098 if (type & ENGINE_TYPE_AUTHORYEAR)
1099 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1100 if (type & ENGINE_TYPE_NUMERICAL)
1101 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1102 if (type & ENGINE_TYPE_DEFAULT)
1103 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1109 int TextClass::readCiteEngineType(Lexer & lexrc) const
1111 LATTEST(ENGINE_TYPE_DEFAULT ==
1112 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1113 if (!lexrc.next()) {
1114 lexrc.printError("No cite engine type given for token: `$$Token'.");
1115 return ENGINE_TYPE_DEFAULT;
1117 string const type = rtrim(lexrc.getString());
1118 if (compare_ascii_no_case(type, "authoryear") == 0)
1119 return ENGINE_TYPE_AUTHORYEAR;
1120 else if (compare_ascii_no_case(type, "numerical") == 0)
1121 return ENGINE_TYPE_NUMERICAL;
1122 else if (compare_ascii_no_case(type, "default") != 0) {
1123 string const s = "Unknown cite engine type `" + type
1124 + "' given for token: `$$Token',";
1125 lexrc.printError(s);
1127 return ENGINE_TYPE_DEFAULT;
1131 bool TextClass::readCiteFormat(Lexer & lexrc)
1133 int const type = readCiteEngineType(lexrc);
1136 while (lexrc.isOK()) {
1138 etype = lexrc.getString();
1139 if (compare_ascii_no_case(etype, "end") == 0)
1144 definition = lexrc.getString();
1145 char initchar = etype[0];
1146 if (initchar == '#')
1148 if (initchar == '!' || initchar == '_') {
1149 if (type & ENGINE_TYPE_AUTHORYEAR)
1150 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1151 if (type & ENGINE_TYPE_NUMERICAL)
1152 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1153 if (type & ENGINE_TYPE_DEFAULT)
1154 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1156 if (type & ENGINE_TYPE_AUTHORYEAR)
1157 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1158 if (type & ENGINE_TYPE_NUMERICAL)
1159 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1160 if (type & ENGINE_TYPE_DEFAULT)
1161 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1168 bool TextClass::readFloat(Lexer & lexrc)
1185 FT_ALLOWED_PLACEMENT,
1191 LexerKeyword floatTags[] = {
1192 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1193 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1194 { "allowswide", FT_ALLOWS_WIDE },
1196 { "extension", FT_EXT },
1197 { "guiname", FT_NAME },
1198 { "htmlattr", FT_HTMLATTR },
1199 { "htmlstyle", FT_HTMLSTYLE },
1200 { "htmltag", FT_HTMLTAG },
1201 { "ispredefined", FT_PREDEFINED },
1202 { "listcommand", FT_LISTCOMMAND },
1203 { "listname", FT_LISTNAME },
1204 { "numberwithin", FT_WITHIN },
1205 { "placement", FT_PLACEMENT },
1206 { "refprefix", FT_REFPREFIX },
1207 { "style", FT_STYLE },
1208 { "type", FT_TYPE },
1209 { "usesfloatpkg", FT_USESFLOAT }
1212 lexrc.pushTable(floatTags);
1216 docstring htmlstyle;
1222 string allowed_placement = "!htbpH";
1227 bool usesfloat = true;
1228 bool ispredefined = false;
1229 bool allowswide = true;
1230 bool allowssideways = true;
1232 bool getout = false;
1233 while (!getout && lexrc.isOK()) {
1234 int le = lexrc.lex();
1236 case Lexer::LEX_UNDEF:
1237 lexrc.printError("Unknown float tag `$$Token'");
1245 type = lexrc.getString();
1246 if (floatlist_.typeExist(type)) {
1247 Floating const & fl = floatlist_.getType(type);
1248 placement = fl.placement();
1250 within = fl.within();
1253 listname = fl.listName();
1254 usesfloat = fl.usesFloatPkg();
1255 ispredefined = fl.isPredefined();
1256 listcommand = fl.listCommand();
1257 refprefix = fl.refPrefix();
1262 name = lexrc.getString();
1266 placement = lexrc.getString();
1268 case FT_ALLOWED_PLACEMENT:
1270 allowed_placement = lexrc.getString();
1274 ext = lexrc.getString();
1278 within = lexrc.getString();
1279 if (within == "none")
1284 style = lexrc.getString();
1286 case FT_LISTCOMMAND:
1288 listcommand = lexrc.getString();
1292 refprefix = lexrc.getString();
1296 listname = lexrc.getString();
1300 usesfloat = lexrc.getBool();
1304 ispredefined = lexrc.getBool();
1306 case FT_ALLOWS_SIDEWAYS:
1308 allowssideways = lexrc.getBool();
1310 case FT_ALLOWS_WIDE:
1312 allowswide = lexrc.getBool();
1316 htmlattr = lexrc.getString();
1320 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1324 htmltag = lexrc.getString();
1334 // Here we have a full float if getout == true
1336 if (!usesfloat && listcommand.empty()) {
1337 // if this float uses the same auxfile as an existing one,
1338 // there is no need for it to provide a list command.
1339 FloatList::const_iterator it = floatlist_.begin();
1340 FloatList::const_iterator en = floatlist_.end();
1341 bool found_ext = false;
1342 for (; it != en; ++it) {
1343 if (it->second.ext() == ext) {
1349 LYXERR0("The layout does not provide a list command " <<
1350 "for the float `" << type << "'. LyX will " <<
1351 "not be able to produce a float list.");
1353 Floating fl(type, placement, ext, within, style, name,
1354 listname, listcommand, refprefix, allowed_placement,
1355 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1356 allowswide, allowssideways);
1357 floatlist_.newFloat(fl);
1358 // each float has its own counter
1359 counters_.newCounter(from_ascii(type), from_ascii(within),
1360 docstring(), docstring());
1361 // also define sub-float counters
1362 docstring const subtype = "sub-" + from_ascii(type);
1363 counters_.newCounter(subtype, from_ascii(type),
1364 "\\alph{" + subtype + "}", docstring());
1370 bool TextClass::readOutlinerName(Lexer & lexrc)
1375 type = lexrc.getString();
1377 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1381 name = lexrc.getDocString();
1383 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1386 outliner_names_[type] = name;
1391 docstring TextClass::outlinerName(std::string const & type) const
1393 std::map<std::string,docstring>::const_iterator const it
1394 = outliner_names_.find(type);
1395 if (it == outliner_names_.end()) {
1396 LYXERR0("Missing OutlinerName for " << type << "!");
1397 return from_utf8(type);
1403 string const & TextClass::prerequisites(string const & sep) const
1405 if (contains(prerequisites_, ',')) {
1406 vector<string> const pres = getVectorFromString(prerequisites_);
1407 prerequisites_ = getStringFromVector(pres, sep);
1409 return prerequisites_;
1413 bool TextClass::hasLayout(docstring const & n) const
1415 docstring const name = n.empty() ? defaultLayoutName() : n;
1417 return find_if(layoutlist_.begin(), layoutlist_.end(),
1418 LayoutNamesEqual(name))
1419 != layoutlist_.end();
1423 bool TextClass::hasInsetLayout(docstring const & n) const
1427 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1428 return it != insetlayoutlist_.end();
1432 Layout const & TextClass::operator[](docstring const & name) const
1434 LATTEST(!name.empty());
1437 find_if(begin(), end(), LayoutNamesEqual(name));
1440 LYXERR0("We failed to find the layout '" << name
1441 << "' in the layout list. You MUST investigate!");
1442 for (const_iterator cit = begin(); cit != end(); ++cit)
1443 lyxerr << " " << to_utf8(cit->name()) << endl;
1445 // We require the name to exist
1446 static const Layout dummy;
1447 LASSERT(false, return dummy);
1454 Layout & TextClass::operator[](docstring const & name)
1456 LATTEST(!name.empty());
1457 // Safe to continue, given what we do below.
1459 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1462 LYXERR0("We failed to find the layout '" << to_utf8(name)
1463 << "' in the layout list. You MUST investigate!");
1464 for (const_iterator cit = begin(); cit != end(); ++cit)
1465 LYXERR0(" " << to_utf8(cit->name()));
1467 // we require the name to exist
1469 // we are here only in release mode
1470 layoutlist_.push_back(createBasicLayout(name, true));
1471 it = find_if(begin(), end(), LayoutNamesEqual(name));
1478 bool TextClass::deleteLayout(docstring const & name)
1480 if (name == defaultLayoutName() || name == plainLayoutName())
1483 LayoutList::iterator it =
1484 remove_if(layoutlist_.begin(), layoutlist_.end(),
1485 LayoutNamesEqual(name));
1487 LayoutList::iterator end = layoutlist_.end();
1488 bool const ret = (it != end);
1489 layoutlist_.erase(it, end);
1494 bool TextClass::deleteInsetLayout(docstring const & name)
1496 return insetlayoutlist_.erase(name);
1500 // Load textclass info if not loaded yet
1501 bool TextClass::load(string const & path) const
1506 // Read style-file, provided path is searched before the system ones
1507 // If path is a file, it is loaded directly.
1508 FileName layout_file(path);
1509 if (!path.empty() && !layout_file.isReadableFile())
1510 layout_file = FileName(addName(path, name_ + ".layout"));
1511 if (layout_file.empty() || !layout_file.exists())
1512 layout_file = libFileSearch("layouts", name_, "layout");
1513 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1516 lyxerr << "Error reading `"
1517 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1518 << "'\n(Check `" << name_
1519 << "')\nCheck your installation and "
1520 "try Options/Reconfigure..."
1528 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1533 layoutlist_.push_back(createBasicLayout(n, true));
1538 string DocumentClass::forcedLayouts() const
1542 const_iterator const e = end();
1543 for (const_iterator i = begin(); i != e; ++i) {
1544 if (i->forcelocal > 0) {
1546 os << "Format " << LAYOUT_FORMAT << '\n';
1556 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1558 // FIXME The fix for the InsetLayout part of 4812 would be here:
1559 // Add the InsetLayout to the document class if it is not found.
1561 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1562 while (!n.empty()) {
1563 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1564 if (cit != cen && cit->first == n) {
1565 if (cit->second.obsoleted_by().empty())
1567 n = cit->second.obsoleted_by();
1568 return insetLayout(n);
1570 // If we have a generic prefix (e.g., "Note:"),
1571 // try if this one alone is found.
1572 size_t i = n.find(':');
1573 if (i == string::npos)
1577 // Layout "name" not found.
1578 return plainInsetLayout();
1582 InsetLayout const & DocumentClass::plainInsetLayout() {
1583 static const InsetLayout plain_insetlayout_;
1584 return plain_insetlayout_;
1588 docstring const & TextClass::defaultLayoutName() const
1590 return defaultlayout_;
1594 Layout const & TextClass::defaultLayout() const
1596 return operator[](defaultLayoutName());
1600 bool TextClass::isDefaultLayout(Layout const & layout) const
1602 return layout.name() == defaultLayoutName();
1606 bool TextClass::isPlainLayout(Layout const & layout) const
1608 return layout.name() == plainLayoutName();
1612 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1614 static Layout * defaultLayout = NULL;
1616 if (defaultLayout) {
1617 defaultLayout->setUnknown(unknown);
1618 defaultLayout->setName(name);
1619 return *defaultLayout;
1622 static char const * s = "Margin Static\n"
1623 "LatexType Paragraph\n"
1626 "AlignPossible Left, Right, Center\n"
1627 "LabelType No_Label\n"
1629 istringstream ss(s);
1630 Lexer lex(textClassTags);
1632 defaultLayout = new Layout;
1633 defaultLayout->setUnknown(unknown);
1634 defaultLayout->setName(name);
1635 if (!readStyle(lex, *defaultLayout)) {
1636 // The only way this happens is because the hardcoded layout above
1640 return *defaultLayout;
1644 DocumentClassPtr getDocumentClass(
1645 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1646 LayoutModuleList const & celist,
1649 DocumentClassPtr doc_class =
1650 DocumentClassPtr(new DocumentClass(baseClass));
1651 LayoutModuleList::const_iterator it = modlist.begin();
1652 LayoutModuleList::const_iterator en = modlist.end();
1653 for (; it != en; ++it) {
1654 string const modName = *it;
1655 LyXModule * lm = theModuleList[modName];
1657 docstring const msg =
1658 bformat(_("The module %1$s has been requested by\n"
1659 "this document but has not been found in the list of\n"
1660 "available modules. If you recently installed it, you\n"
1661 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1663 frontend::Alert::warning(_("Module not available"), msg);
1666 if (!lm->isAvailable() && !clone) {
1667 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1668 docstring const msg =
1669 bformat(_("The module %1$s requires a package that is not\n"
1670 "available in your LaTeX installation, or a converter that\n"
1671 "you have not installed. LaTeX output may not be possible.\n"
1672 "Missing prerequisites:\n"
1674 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1675 from_utf8(modName), prereqs);
1676 frontend::Alert::warning(_("Package not available"), msg, true);
1678 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1679 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1680 docstring const msg =
1681 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1682 frontend::Alert::warning(_("Read Error"), msg);
1686 LayoutModuleList::const_iterator cit = celist.begin();
1687 LayoutModuleList::const_iterator cen = celist.end();
1688 for (; cit != cen; ++cit) {
1689 string const ceName = *cit;
1690 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1692 docstring const msg =
1693 bformat(_("The cite engine %1$s has been requested by\n"
1694 "this document but has not been found in the list of\n"
1695 "available engines. If you recently installed it, you\n"
1696 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1698 frontend::Alert::warning(_("Cite Engine not available"), msg);
1701 if (!ce->isAvailable() && !clone) {
1702 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1703 docstring const msg =
1704 bformat(_("The cite engine %1$s requires a package that is not\n"
1705 "available in your LaTeX installation, or a converter that\n"
1706 "you have not installed. LaTeX output may not be possible.\n"
1707 "Missing prerequisites:\n"
1709 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1710 from_utf8(ceName), prereqs);
1711 frontend::Alert::warning(_("Package not available"), msg, true);
1713 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1714 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1715 docstring const msg =
1716 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1717 frontend::Alert::warning(_("Read Error"), msg);
1725 /////////////////////////////////////////////////////////////////////////
1729 /////////////////////////////////////////////////////////////////////////
1731 DocumentClass::DocumentClass(LayoutFile const & tc)
1736 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1738 LayoutList::const_iterator it = layoutlist_.begin();
1739 LayoutList::const_iterator end = layoutlist_.end();
1740 for (; it != end; ++it)
1741 if (it->latexname() == lay)
1747 bool DocumentClass::provides(string const & p) const
1749 return provides_.find(p) != provides_.end();
1753 bool DocumentClass::hasTocLevels() const
1755 return min_toclevel_ != Layout::NOT_IN_TOC;
1759 Layout const & DocumentClass::getTOCLayout() const
1761 // we're going to look for the layout with the minimum toclevel
1762 TextClass::LayoutList::const_iterator lit = begin();
1763 TextClass::LayoutList::const_iterator const len = end();
1764 int minlevel = 1000;
1765 Layout const * lay = NULL;
1766 for (; lit != len; ++lit) {
1767 int const level = lit->toclevel;
1768 // we don't want Part or unnumbered sections
1769 if (level == Layout::NOT_IN_TOC || level < 0
1770 || level >= minlevel || lit->counter.empty())
1777 // hmm. that is very odd, so we'll do our best.
1778 return operator[](defaultLayoutName());
1782 Layout const & DocumentClass::htmlTOCLayout() const
1784 if (html_toc_section_.empty())
1785 html_toc_section_ = getTOCLayout().name();
1786 return operator[](html_toc_section_);
1790 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1791 string const & entry, string const & fallback) const
1793 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1795 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1796 if (itype == cite_formats_.end())
1797 return default_format;
1798 map<string, string>::const_iterator it = itype->second.find(entry);
1799 if (it == itype->second.end() && !fallback.empty())
1800 it = itype->second.find(fallback);
1801 if (it == itype->second.end())
1802 return default_format;
1807 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1808 string const & macro) const
1810 static string empty;
1811 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1812 if (itype == cite_macros_.end())
1814 map<string, string>::const_iterator it = itype->second.find(macro);
1815 if (it == itype->second.end())
1821 vector<string> const DocumentClass::citeCommands(
1822 CiteEngineType const & type) const
1824 vector<CitationStyle> const styles = citeStyles(type);
1825 vector<CitationStyle>::const_iterator it = styles.begin();
1826 vector<CitationStyle>::const_iterator end = styles.end();
1827 vector<string> cmds;
1828 for (; it != end; ++it) {
1829 CitationStyle const cite = *it;
1830 cmds.push_back(cite.name);
1836 vector<CitationStyle> const & DocumentClass::citeStyles(
1837 CiteEngineType const & type) const
1839 static vector<CitationStyle> empty;
1840 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1841 if (it == cite_styles_.end())
1847 /////////////////////////////////////////////////////////////////////////
1851 /////////////////////////////////////////////////////////////////////////
1853 ostream & operator<<(ostream & os, PageSides p)