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"
23 #include "FloatList.h"
27 #include "ModuleList.h"
29 #include "frontends/alert.h"
31 #include "support/lassert.h"
32 #include "support/debug.h"
33 #include "support/ExceptionMessage.h"
34 #include "support/FileName.h"
35 #include "support/filetools.h"
36 #include "support/gettext.h"
37 #include "support/lstrings.h"
38 #include "support/os.h"
39 #include "support/TempFile.h"
50 using namespace lyx::support;
54 // Keep the changes documented in the Customization manual.
56 // If you change this format, then you MUST also make sure that
57 // your changes do not invalidate the hardcoded layout file in
58 // LayoutFile.cpp. Additions will never do so, but syntax changes
59 // could. See LayoutFileList::addEmptyClass() and, especially, the
60 // definition of the layoutpost string.
61 // You should also run the development/tools/updatelayouts.py script,
62 // to update the format of all of our layout files.
64 int const LAYOUT_FORMAT = 60; //lasgouttes LongTableNoNumber => Unnumbered
68 class LayoutNamesEqual : public unary_function<Layout, bool> {
70 LayoutNamesEqual(docstring const & name)
73 bool operator()(Layout const & c) const
75 return c.name() == name_;
82 bool layout2layout(FileName const & filename, FileName const & tempfile)
84 FileName const script = libFileSearch("scripts", "layout2layout.py");
86 LYXERR0("Could not find layout conversion "
87 "script layout2layout.py.");
91 ostringstream command;
92 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
93 << ' ' << quoteName(filename.toFilesystemEncoding())
94 << ' ' << quoteName(tempfile.toFilesystemEncoding());
95 string const command_str = command.str();
97 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
99 cmd_ret const ret = runCommand(command_str);
100 if (ret.first != 0) {
101 LYXERR0("Could not run layout conversion script layout2layout.py.");
108 string translateReadType(TextClass::ReadType rt)
111 case TextClass::BASECLASS:
113 case TextClass::MERGE:
115 case TextClass::MODULE:
116 return "module file";
117 case TextClass::VALIDATION:
127 // This string should not be translated here,
128 // because it is a layout identifier.
129 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
132 /////////////////////////////////////////////////////////////////////////
136 /////////////////////////////////////////////////////////////////////////
138 TextClass::TextClass()
139 : loaded_(false), tex_class_avail_(false),
140 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
141 opt_pagestyle_("empty|plain|headings|fancy"), pagestyle_("default"),
142 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3),
143 outputType_(LATEX), outputFormat_("latex"),
144 defaultfont_(sane_font),
145 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
146 min_toclevel_(0), max_toclevel_(0),
147 cite_full_author_list_(true)
152 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
154 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
155 if (!lay.read(lexrc, *this)) {
156 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
160 lay.resfont = lay.font;
161 lay.resfont.realize(defaultfont_);
162 lay.reslabelfont = lay.labelfont;
163 lay.reslabelfont.realize(defaultfont_);
164 return true; // no errors
203 TC_ADDTOHTMLPREAMBLE,
220 LexerKeyword textClassTags[] = {
221 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
222 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
223 { "addtopreamble", TC_ADDTOPREAMBLE },
224 { "citeengine", TC_CITEENGINE },
225 { "citeenginetype", TC_CITEENGINETYPE },
226 { "citeformat", TC_CITEFORMAT },
227 { "classoptions", TC_CLASSOPTIONS },
228 { "columns", TC_COLUMNS },
229 { "counter", TC_COUNTER },
230 { "defaultbiblio", TC_DEFAULTBIBLIO },
231 { "defaultfont", TC_DEFAULTFONT },
232 { "defaultmodule", TC_DEFAULTMODULE },
233 { "defaultstyle", TC_DEFAULTSTYLE },
234 { "excludesmodule", TC_EXCLUDESMODULE },
235 { "float", TC_FLOAT },
236 { "format", TC_FORMAT },
237 { "fullauthorlist", TC_FULLAUTHORLIST },
238 { "htmlpreamble", TC_HTMLPREAMBLE },
239 { "htmlstyles", TC_HTMLSTYLES },
240 { "htmltocsection", TC_HTMLTOCSECTION },
241 { "ifcounter", TC_IFCOUNTER },
242 { "input", TC_INPUT },
243 { "insetlayout", TC_INSETLAYOUT },
244 { "leftmargin", TC_LEFTMARGIN },
245 { "modifystyle", TC_MODIFYSTYLE },
246 { "nocounter", TC_NOCOUNTER },
247 { "nofloat", TC_NOFLOAT },
248 { "noinsetlayout", TC_NOINSETLAYOUT },
249 { "nostyle", TC_NOSTYLE },
250 { "outlinername", TC_OUTLINERNAME },
251 { "outputformat", TC_OUTPUTFORMAT },
252 { "outputtype", TC_OUTPUTTYPE },
253 { "packageoptions", TC_PKGOPTS },
254 { "pagestyle", TC_PAGESTYLE },
255 { "preamble", TC_PREAMBLE },
256 { "provides", TC_PROVIDES },
257 { "providesmodule", TC_PROVIDESMODULE },
258 { "providestyle", TC_PROVIDESTYLE },
259 { "requires", TC_REQUIRES },
260 { "rightmargin", TC_RIGHTMARGIN },
261 { "secnumdepth", TC_SECNUMDEPTH },
262 { "sides", TC_SIDES },
263 { "style", TC_STYLE },
264 { "titlelatexname", TC_TITLELATEXNAME },
265 { "titlelatextype", TC_TITLELATEXTYPE },
266 { "tocdepth", TC_TOCDEPTH }
272 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
274 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
275 TempFile tmp("convertXXXXXX.layout");
276 FileName const tempfile = tmp.name();
277 bool success = layout2layout(filename, tempfile);
279 success = readWithoutConv(tempfile, rt) == OK;
284 std::string TextClass::convert(std::string const & str)
286 TempFile tmp1("localXXXXXX.layout");
287 FileName const fn = tmp1.name();
288 ofstream os(fn.toFilesystemEncoding().c_str());
291 TempFile tmp2("convert_localXXXXXX.layout");
292 FileName const tempfile = tmp2.name();
293 bool success = layout2layout(fn, tempfile);
296 ifstream is(tempfile.toFilesystemEncoding().c_str());
308 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
310 if (!filename.isReadableFile()) {
311 lyxerr << "Cannot read layout file `" << filename << "'."
316 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
317 to_utf8(makeDisplayPath(filename.absFileName())));
319 // Define the plain layout used in table cells, ert, etc. Note that
320 // we do this before loading any layout file, so that classes can
321 // override features of this layout if they should choose to do so.
322 if (rt == BASECLASS && !hasLayout(plain_layout_))
323 layoutlist_.push_back(createBasicLayout(plain_layout_));
325 Lexer lexrc(textClassTags);
326 lexrc.setFile(filename);
327 ReturnValues retval = read(lexrc, rt);
329 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
330 to_utf8(makeDisplayPath(filename.absFileName())));
336 bool TextClass::read(FileName const & filename, ReadType rt)
338 ReturnValues const retval = readWithoutConv(filename, rt);
339 if (retval != FORMAT_MISMATCH)
342 bool const worx = convertLayoutFormat(filename, rt);
344 LYXERR0 ("Unable to convert " << filename <<
345 " to format " << LAYOUT_FORMAT);
350 TextClass::ReturnValues TextClass::validate(std::string const & str)
353 return tc.read(str, VALIDATION);
357 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
359 Lexer lexrc(textClassTags);
360 istringstream is(str);
362 ReturnValues retval = read(lexrc, rt);
364 if (retval != FORMAT_MISMATCH)
367 // write the layout string to a temporary file
368 TempFile tmp("TextClass_read");
369 FileName const tempfile = tmp.name();
370 ofstream os(tempfile.toFilesystemEncoding().c_str());
372 LYXERR0("Unable to create temporary file");
378 // now try to convert it
379 bool const worx = convertLayoutFormat(tempfile, rt);
381 LYXERR0("Unable to convert internal layout information to format "
389 // Reads a textclass structure from file.
390 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
395 // Format of files before the 'Format' tag was introduced
400 while (lexrc.isOK() && !error) {
401 int le = lexrc.lex();
404 case Lexer::LEX_FEOF:
407 case Lexer::LEX_UNDEF:
408 lexrc.printError("Unknown TextClass tag `$$Token'");
416 // used below to track whether we are in an IfStyle or IfCounter tag.
417 bool modifystyle = false;
418 bool providestyle = false;
419 bool ifcounter = false;
421 switch (static_cast<TextClassTags>(le)) {
425 format = lexrc.getInteger();
428 case TC_OUTPUTFORMAT:
430 outputFormat_ = lexrc.getString();
434 readOutputType(lexrc);
435 switch(outputType_) {
437 outputFormat_ = "latex";
440 outputFormat_ = "docbook";
443 outputFormat_ = "literate";
448 case TC_INPUT: // Include file
450 string const inc = lexrc.getString();
451 FileName tmp = libFileSearch("layouts", inc,
455 lexrc.printError("Could not find input file: " + inc);
457 } else if (!read(tmp, MERGE)) {
458 lexrc.printError("Error reading input file: " + tmp.absFileName());
464 case TC_DEFAULTSTYLE:
466 docstring const name = from_utf8(subst(lexrc.getString(),
468 defaultlayout_ = name;
475 case TC_PROVIDESTYLE:
476 // if modifystyle is true, then we got here by falling through
477 // so we are not in an ProvideStyle block
483 lexrc.printError("No name given for style: `$$Token'.");
487 docstring const name = from_utf8(subst(lexrc.getString(),
490 string s = "Could not read name for style: `$$Token' "
491 + lexrc.getString() + " is probably not valid UTF-8!";
494 // Since we couldn't read the name, we just scan the rest
495 // of the style and discard it.
496 error = !readStyle(lexrc, lay);
500 bool const have_layout = hasLayout(name);
502 // If the layout already exists, then we want to add it to
503 // the existing layout, as long as we are not in an ProvideStyle
505 if (have_layout && !providestyle) {
506 Layout & lay = operator[](name);
507 error = !readStyle(lexrc, lay);
509 // If the layout does not exist, then we want to create a new
510 // one, but not if we are in a ModifyStyle block.
511 else if (!have_layout && !modifystyle) {
513 layout.setName(name);
514 error = !readStyle(lexrc, layout);
516 layoutlist_.push_back(layout);
518 if (defaultlayout_.empty()) {
519 // We do not have a default layout yet, so we choose
520 // the first layout we encounter.
521 defaultlayout_ = name;
524 // There are two ways to get here:
525 // (i) The layout exists but we are in an ProvideStyle block
526 // (ii) The layout doesn't exist, but we are in an ModifyStyle
528 // Either way, we just scan the rest and discard it
531 // false positive from coverity
532 // coverity[CHECKED_RETURN]
533 readStyle(lexrc, lay);
540 docstring const style = from_utf8(subst(lexrc.getString(),
542 if (!deleteLayout(style))
543 lyxerr << "Cannot delete style `"
544 << to_utf8(style) << '\'' << endl;
548 case TC_NOINSETLAYOUT:
550 docstring const style = from_utf8(subst(lexrc.getString(),
552 if (!deleteInsetLayout(style))
553 LYXERR0("Style `" << style << "' cannot be removed\n"
554 "because it was not found!");
560 columns_ = lexrc.getInteger();
565 switch (lexrc.getInteger()) {
566 case 1: sides_ = OneSide; break;
567 case 2: sides_ = TwoSides; break;
569 lyxerr << "Impossible number of page"
570 " sides, setting to one."
580 pagestyle_ = rtrim(lexrc.getString());
584 defaultfont_ = lyxRead(lexrc);
585 if (!defaultfont_.resolved()) {
586 lexrc.printError("Warning: defaultfont should "
587 "be fully instantiated!");
588 defaultfont_.realize(sane_font);
594 secnumdepth_ = lexrc.getInteger();
599 tocdepth_ = lexrc.getInteger();
602 // First step to support options
603 case TC_CLASSOPTIONS:
604 readClassOptions(lexrc);
608 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
611 case TC_HTMLPREAMBLE:
612 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
616 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
619 case TC_HTMLTOCSECTION:
620 html_toc_section_ = from_utf8(trim(lexrc.getString()));
623 case TC_ADDTOPREAMBLE:
624 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
627 case TC_ADDTOHTMLPREAMBLE:
628 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
631 case TC_ADDTOHTMLSTYLES:
632 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
637 string const feature = lexrc.getString();
639 if (lexrc.getInteger())
640 provides_.insert(feature);
642 provides_.erase(feature);
648 vector<string> const req
649 = getVectorFromString(lexrc.getString());
650 requires_.insert(req.begin(), req.end());
656 string const pkg = lexrc.getString();
658 string const options = lexrc.getString();
659 package_options_[pkg] = options;
663 case TC_DEFAULTMODULE: {
665 string const module = lexrc.getString();
666 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
667 default_modules_.push_back(module);
671 case TC_PROVIDESMODULE: {
673 string const module = lexrc.getString();
674 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
675 provided_modules_.push_back(module);
679 case TC_EXCLUDESMODULE: {
681 string const module = lexrc.getString();
682 // modules already have their own way to exclude other modules
684 LYXERR0("ExcludesModule tag cannot be used in a module!");
687 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
688 excluded_modules_.push_back(module);
692 case TC_LEFTMARGIN: // left margin type
694 leftmargin_ = lexrc.getDocString();
697 case TC_RIGHTMARGIN: // right margin type
699 rightmargin_ = lexrc.getDocString();
702 case TC_INSETLAYOUT: {
704 lexrc.printError("No name given for InsetLayout: `$$Token'.");
708 docstring const name = subst(lexrc.getDocString(), '_', ' ');
710 string s = "Could not read name for InsetLayout: `$$Token' "
711 + lexrc.getString() + " is probably not valid UTF-8!";
714 // Since we couldn't read the name, we just scan the rest
715 // of the style and discard it.
716 il.read(lexrc, *this);
717 // Let's try to continue rather than abort.
719 } else if (hasInsetLayout(name)) {
720 InsetLayout & il = insetlayoutlist_[name];
721 error = !il.read(lexrc, *this);
725 error = !il.read(lexrc, *this);
727 insetlayoutlist_[name] = il;
733 error = !readFloat(lexrc);
737 error = !readCiteEngine(lexrc);
740 case TC_CITEENGINETYPE:
742 opt_enginetype_ = rtrim(lexrc.getString());
746 error = !readCiteFormat(lexrc);
749 case TC_DEFAULTBIBLIO:
751 cite_default_biblio_style_ = rtrim(lexrc.getString());
754 case TC_FULLAUTHORLIST:
756 cite_full_author_list_ &= lexrc.getBool();
761 docstring const cnt = lexrc.getDocString();
762 if (!counters_.remove(cnt))
763 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
772 docstring const name = lexrc.getDocString();
774 string s = "Could not read name for counter: `$$Token' "
775 + lexrc.getString() + " is probably not valid UTF-8!";
776 lexrc.printError(s.c_str());
778 // Since we couldn't read the name, we just scan the rest
782 error = !counters_.read(lexrc, name, !ifcounter);
785 lexrc.printError("No name given for style: `$$Token'.");
790 case TC_TITLELATEXTYPE:
791 readTitleType(lexrc);
794 case TC_TITLELATEXNAME:
796 titlename_ = lexrc.getString();
801 string const nofloat = lexrc.getString();
802 floatlist_.erase(nofloat);
806 case TC_OUTLINERNAME:
807 error = !readOutlinerName(lexrc);
811 // Note that this is triggered the first time through the loop unless
812 // we hit a format tag.
813 if (format != LAYOUT_FORMAT)
814 return FORMAT_MISMATCH;
817 // at present, we abort if we encounter an error,
818 // so there is no point continuing.
825 if (defaultlayout_.empty()) {
826 LYXERR0("Error: Textclass '" << name_
827 << "' is missing a defaultstyle.");
831 // Try to erase "stdinsets" from the provides_ set.
833 // Provides stdinsets 1
834 // declaration simply tells us that the standard insets have been
835 // defined. (It's found in stdinsets.inc but could also be used in
836 // user-defined files.) There isn't really any such package. So we
837 // might as well go ahead and erase it.
838 // If we do not succeed, then it was not there, which means that
839 // the textclass did not provide the definitions of the standard
840 // insets. So we need to try to load them.
841 int erased = provides_.erase("stdinsets");
843 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
846 frontend::Alert::warning(_("Missing File"),
847 _("Could not find stdinsets.inc! This may lead to data loss!"));
849 } else if (!read(tmp, MERGE)) {
850 frontend::Alert::warning(_("Corrupt File"),
851 _("Could not read stdinsets.inc! This may lead to data loss!"));
856 min_toclevel_ = Layout::NOT_IN_TOC;
857 max_toclevel_ = Layout::NOT_IN_TOC;
858 const_iterator lit = begin();
859 const_iterator len = end();
860 for (; lit != len; ++lit) {
861 int const toclevel = lit->toclevel;
862 if (toclevel != Layout::NOT_IN_TOC) {
863 if (min_toclevel_ == Layout::NOT_IN_TOC)
864 min_toclevel_ = toclevel;
866 min_toclevel_ = min(min_toclevel_, toclevel);
867 max_toclevel_ = max(max_toclevel_, toclevel);
870 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
871 << ", maximum is " << max_toclevel_);
873 return (error ? ERROR : OK);
877 void TextClass::readTitleType(Lexer & lexrc)
879 LexerKeyword titleTypeTags[] = {
880 { "commandafter", TITLE_COMMAND_AFTER },
881 { "environment", TITLE_ENVIRONMENT }
884 PushPopHelper pph(lexrc, titleTypeTags);
886 int le = lexrc.lex();
888 case Lexer::LEX_UNDEF:
889 lexrc.printError("Unknown output type `$$Token'");
891 case TITLE_COMMAND_AFTER:
892 case TITLE_ENVIRONMENT:
893 titletype_ = static_cast<TitleLatexType>(le);
896 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
902 void TextClass::readOutputType(Lexer & lexrc)
904 LexerKeyword outputTypeTags[] = {
905 { "docbook", DOCBOOK },
907 { "literate", LITERATE }
910 PushPopHelper pph(lexrc, outputTypeTags);
912 int le = lexrc.lex();
914 case Lexer::LEX_UNDEF:
915 lexrc.printError("Unknown output type `$$Token'");
920 outputType_ = static_cast<OutputType>(le);
923 LYXERR0("Unhandled value " << le);
929 void TextClass::readClassOptions(Lexer & lexrc)
939 LexerKeyword classOptionsTags[] = {
941 {"fontsize", CO_FONTSIZE },
942 {"header", CO_HEADER },
943 {"other", CO_OTHER },
944 {"pagestyle", CO_PAGESTYLE }
947 lexrc.pushTable(classOptionsTags);
949 while (!getout && lexrc.isOK()) {
950 int le = lexrc.lex();
952 case Lexer::LEX_UNDEF:
953 lexrc.printError("Unknown ClassOption tag `$$Token'");
961 opt_fontsize_ = rtrim(lexrc.getString());
965 opt_pagestyle_ = rtrim(lexrc.getString());
969 if (options_.empty())
970 options_ = lexrc.getString();
972 options_ += ',' + lexrc.getString();
976 class_header_ = subst(lexrc.getString(), """, "\"");
987 bool TextClass::readCiteEngine(Lexer & lexrc)
989 int const type = readCiteEngineType(lexrc);
990 if (type & ENGINE_TYPE_AUTHORYEAR)
991 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
992 if (type & ENGINE_TYPE_NUMERICAL)
993 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
994 if (type & ENGINE_TYPE_DEFAULT)
995 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
998 while (!getout && lexrc.isOK()) {
1000 def = lexrc.getString();
1001 def = subst(def, " ", "");
1002 def = subst(def, "\t", "");
1003 if (compare_ascii_no_case(def, "end") == 0) {
1009 char ichar = def[0];
1013 cs.forceUpperCase = true;
1017 size_t const n = def.size();
1018 for (size_t i = 0; i != n; ++i) {
1021 cs.fullAuthorList = true;
1022 else if (ichar == '[' && cs.textAfter)
1023 cs.textBefore = true;
1024 else if (ichar == '[')
1025 cs.textAfter = true;
1026 else if (ichar != ']')
1031 if (type & ENGINE_TYPE_AUTHORYEAR)
1032 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1033 if (type & ENGINE_TYPE_NUMERICAL)
1034 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1035 if (type & ENGINE_TYPE_DEFAULT)
1036 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1042 int TextClass::readCiteEngineType(Lexer & lexrc) const
1044 LATTEST(ENGINE_TYPE_DEFAULT ==
1045 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1046 if (!lexrc.next()) {
1047 lexrc.printError("No cite engine type given for token: `$$Token'.");
1048 return ENGINE_TYPE_DEFAULT;
1050 string const type = rtrim(lexrc.getString());
1051 if (compare_ascii_no_case(type, "authoryear") == 0)
1052 return ENGINE_TYPE_AUTHORYEAR;
1053 else if (compare_ascii_no_case(type, "numerical") == 0)
1054 return ENGINE_TYPE_NUMERICAL;
1055 else if (compare_ascii_no_case(type, "default") != 0) {
1056 string const s = "Unknown cite engine type `" + type
1057 + "' given for token: `$$Token',";
1058 lexrc.printError(s);
1060 return ENGINE_TYPE_DEFAULT;
1064 bool TextClass::readCiteFormat(Lexer & lexrc)
1066 int const type = readCiteEngineType(lexrc);
1069 while (lexrc.isOK()) {
1071 etype = lexrc.getString();
1072 if (compare_ascii_no_case(etype, "end") == 0)
1077 definition = lexrc.getString();
1078 char initchar = etype[0];
1079 if (initchar == '#')
1081 if (initchar == '!' || initchar == '_') {
1082 if (type & ENGINE_TYPE_AUTHORYEAR)
1083 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1084 if (type & ENGINE_TYPE_NUMERICAL)
1085 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1086 if (type & ENGINE_TYPE_DEFAULT)
1087 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1089 if (type & ENGINE_TYPE_AUTHORYEAR)
1090 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1091 if (type & ENGINE_TYPE_NUMERICAL)
1092 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1093 if (type & ENGINE_TYPE_DEFAULT)
1094 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1101 bool TextClass::readFloat(Lexer & lexrc)
1118 FT_ALLOWED_PLACEMENT,
1124 LexerKeyword floatTags[] = {
1125 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1126 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1127 { "allowswide", FT_ALLOWS_WIDE },
1129 { "extension", FT_EXT },
1130 { "guiname", FT_NAME },
1131 { "htmlattr", FT_HTMLATTR },
1132 { "htmlstyle", FT_HTMLSTYLE },
1133 { "htmltag", FT_HTMLTAG },
1134 { "ispredefined", FT_PREDEFINED },
1135 { "listcommand", FT_LISTCOMMAND },
1136 { "listname", FT_LISTNAME },
1137 { "numberwithin", FT_WITHIN },
1138 { "placement", FT_PLACEMENT },
1139 { "refprefix", FT_REFPREFIX },
1140 { "style", FT_STYLE },
1141 { "type", FT_TYPE },
1142 { "usesfloatpkg", FT_USESFLOAT }
1145 lexrc.pushTable(floatTags);
1155 string allowed_placement = "!htbpH";
1160 bool usesfloat = true;
1161 bool ispredefined = false;
1162 bool allowswide = true;
1163 bool allowssideways = true;
1165 bool getout = false;
1166 while (!getout && lexrc.isOK()) {
1167 int le = lexrc.lex();
1169 case Lexer::LEX_UNDEF:
1170 lexrc.printError("Unknown float tag `$$Token'");
1178 type = lexrc.getString();
1179 if (floatlist_.typeExist(type)) {
1180 Floating const & fl = floatlist_.getType(type);
1181 placement = fl.placement();
1183 within = fl.within();
1186 listname = fl.listName();
1187 usesfloat = fl.usesFloatPkg();
1188 ispredefined = fl.isPredefined();
1189 listcommand = fl.listCommand();
1190 refprefix = fl.refPrefix();
1195 name = lexrc.getString();
1199 placement = lexrc.getString();
1201 case FT_ALLOWED_PLACEMENT:
1203 allowed_placement = lexrc.getString();
1207 ext = lexrc.getString();
1211 within = lexrc.getString();
1212 if (within == "none")
1217 style = lexrc.getString();
1219 case FT_LISTCOMMAND:
1221 listcommand = lexrc.getString();
1225 refprefix = lexrc.getString();
1229 listname = lexrc.getString();
1233 usesfloat = lexrc.getBool();
1237 ispredefined = lexrc.getBool();
1239 case FT_ALLOWS_SIDEWAYS:
1241 allowssideways = lexrc.getBool();
1243 case FT_ALLOWS_WIDE:
1245 allowswide = lexrc.getBool();
1249 htmlattr = lexrc.getString();
1253 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1257 htmltag = lexrc.getString();
1267 // Here we have a full float if getout == true
1269 if (!usesfloat && listcommand.empty()) {
1270 // if this float uses the same auxfile as an existing one,
1271 // there is no need for it to provide a list command.
1272 FloatList::const_iterator it = floatlist_.begin();
1273 FloatList::const_iterator en = floatlist_.end();
1274 bool found_ext = false;
1275 for (; it != en; ++it) {
1276 if (it->second.ext() == ext) {
1282 LYXERR0("The layout does not provide a list command " <<
1283 "for the float `" << type << "'. LyX will " <<
1284 "not be able to produce a float list.");
1286 Floating fl(type, placement, ext, within, style, name,
1287 listname, listcommand, refprefix, allowed_placement,
1288 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1289 allowswide, allowssideways);
1290 floatlist_.newFloat(fl);
1291 // each float has its own counter
1292 counters_.newCounter(from_ascii(type), from_ascii(within),
1293 docstring(), docstring());
1294 // also define sub-float counters
1295 docstring const subtype = "sub-" + from_ascii(type);
1296 counters_.newCounter(subtype, from_ascii(type),
1297 "\\alph{" + subtype + "}", docstring());
1303 bool TextClass::readOutlinerName(Lexer & lexrc)
1308 type = lexrc.getString();
1310 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1314 name = lexrc.getDocString();
1316 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1319 outliner_names_[type] = name;
1324 docstring TextClass::outlinerName(std::string const & type) const
1326 std::map<std::string,docstring>::const_iterator const it
1327 = outliner_names_.find(type);
1328 if (it == outliner_names_.end()) {
1329 LYXERR0("Missing OutlinerName for " << type << "!");
1330 return from_utf8(type);
1336 string const & TextClass::prerequisites(string const & sep) const
1338 if (contains(prerequisites_, ',')) {
1339 vector<string> const pres = getVectorFromString(prerequisites_);
1340 prerequisites_ = getStringFromVector(pres, sep);
1342 return prerequisites_;
1346 bool TextClass::hasLayout(docstring const & n) const
1348 docstring const name = n.empty() ? defaultLayoutName() : n;
1350 return find_if(layoutlist_.begin(), layoutlist_.end(),
1351 LayoutNamesEqual(name))
1352 != layoutlist_.end();
1356 bool TextClass::hasInsetLayout(docstring const & n) const
1360 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1361 return it != insetlayoutlist_.end();
1365 Layout const & TextClass::operator[](docstring const & name) const
1367 LATTEST(!name.empty());
1370 find_if(begin(), end(), LayoutNamesEqual(name));
1373 LYXERR0("We failed to find the layout '" << name
1374 << "' in the layout list. You MUST investigate!");
1375 for (const_iterator cit = begin(); cit != end(); ++cit)
1376 lyxerr << " " << to_utf8(cit->name()) << endl;
1378 // We require the name to exist
1379 static const Layout dummy;
1380 LASSERT(false, return dummy);
1387 Layout & TextClass::operator[](docstring const & name)
1389 LATTEST(!name.empty());
1390 // Safe to continue, given what we do below.
1392 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1395 LYXERR0("We failed to find the layout '" << to_utf8(name)
1396 << "' in the layout list. You MUST investigate!");
1397 for (const_iterator cit = begin(); cit != end(); ++cit)
1398 LYXERR0(" " << to_utf8(cit->name()));
1400 // we require the name to exist
1402 // we are here only in release mode
1403 layoutlist_.push_back(createBasicLayout(name, true));
1404 it = find_if(begin(), end(), LayoutNamesEqual(name));
1411 bool TextClass::deleteLayout(docstring const & name)
1413 if (name == defaultLayoutName() || name == plainLayoutName())
1416 LayoutList::iterator it =
1417 remove_if(layoutlist_.begin(), layoutlist_.end(),
1418 LayoutNamesEqual(name));
1420 LayoutList::iterator end = layoutlist_.end();
1421 bool const ret = (it != end);
1422 layoutlist_.erase(it, end);
1427 bool TextClass::deleteInsetLayout(docstring const & name)
1429 return insetlayoutlist_.erase(name);
1433 // Load textclass info if not loaded yet
1434 bool TextClass::load(string const & path) const
1439 // Read style-file, provided path is searched before the system ones
1440 // If path is a file, it is loaded directly.
1441 FileName layout_file(path);
1442 if (!path.empty() && !layout_file.isReadableFile())
1443 layout_file = FileName(addName(path, name_ + ".layout"));
1444 if (layout_file.empty() || !layout_file.exists())
1445 layout_file = libFileSearch("layouts", name_, "layout");
1446 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1449 lyxerr << "Error reading `"
1450 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1451 << "'\n(Check `" << name_
1452 << "')\nCheck your installation and "
1453 "try Options/Reconfigure..."
1461 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1466 layoutlist_.push_back(createBasicLayout(n, true));
1471 string DocumentClass::forcedLayouts() const
1475 const_iterator const e = end();
1476 for (const_iterator i = begin(); i != e; ++i) {
1477 if (i->forcelocal > 0) {
1479 os << "Format " << LAYOUT_FORMAT << '\n';
1489 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1491 // FIXME The fix for the InsetLayout part of 4812 would be here:
1492 // Add the InsetLayout to the document class if it is not found.
1494 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1495 while (!n.empty()) {
1496 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1497 if (cit != cen && cit->first == n) {
1498 if (cit->second.obsoleted_by().empty())
1500 n = cit->second.obsoleted_by();
1501 return insetLayout(n);
1503 // If we have a generic prefix (e.g., "Note:"),
1504 // try if this one alone is found.
1505 size_t i = n.find(':');
1506 if (i == string::npos)
1510 // Layout "name" not found.
1511 return plainInsetLayout();
1515 InsetLayout const & DocumentClass::plainInsetLayout() {
1516 static const InsetLayout plain_insetlayout_;
1517 return plain_insetlayout_;
1521 docstring const & TextClass::defaultLayoutName() const
1523 return defaultlayout_;
1527 Layout const & TextClass::defaultLayout() const
1529 return operator[](defaultLayoutName());
1533 bool TextClass::isDefaultLayout(Layout const & layout) const
1535 return layout.name() == defaultLayoutName();
1539 bool TextClass::isPlainLayout(Layout const & layout) const
1541 return layout.name() == plainLayoutName();
1545 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1547 static Layout * defaultLayout = NULL;
1549 if (defaultLayout) {
1550 defaultLayout->setUnknown(unknown);
1551 defaultLayout->setName(name);
1552 return *defaultLayout;
1555 static char const * s = "Margin Static\n"
1556 "LatexType Paragraph\n"
1559 "AlignPossible Left, Right, Center\n"
1560 "LabelType No_Label\n"
1562 istringstream ss(s);
1563 Lexer lex(textClassTags);
1565 defaultLayout = new Layout;
1566 defaultLayout->setUnknown(unknown);
1567 defaultLayout->setName(name);
1568 if (!readStyle(lex, *defaultLayout)) {
1569 // The only way this happens is because the hardcoded layout above
1573 return *defaultLayout;
1577 DocumentClassPtr getDocumentClass(
1578 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1581 DocumentClassPtr doc_class =
1582 DocumentClassPtr(new DocumentClass(baseClass));
1583 LayoutModuleList::const_iterator it = modlist.begin();
1584 LayoutModuleList::const_iterator en = modlist.end();
1585 for (; it != en; ++it) {
1586 string const modName = *it;
1587 LyXModule * lm = theModuleList[modName];
1589 docstring const msg =
1590 bformat(_("The module %1$s has been requested by\n"
1591 "this document but has not been found in the list of\n"
1592 "available modules. If you recently installed it, you\n"
1593 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1595 frontend::Alert::warning(_("Module not available"), msg);
1598 if (!lm->isAvailable() && !clone) {
1599 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1600 docstring const msg =
1601 bformat(_("The module %1$s requires a package that is not\n"
1602 "available in your LaTeX installation, or a converter that\n"
1603 "you have not installed. LaTeX output may not be possible.\n"
1604 "Missing prerequisites:\n"
1606 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1607 from_utf8(modName), prereqs);
1608 frontend::Alert::warning(_("Package not available"), msg, true);
1610 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1611 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1612 docstring const msg =
1613 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1614 frontend::Alert::warning(_("Read Error"), msg);
1621 /////////////////////////////////////////////////////////////////////////
1625 /////////////////////////////////////////////////////////////////////////
1627 DocumentClass::DocumentClass(LayoutFile const & tc)
1632 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1634 LayoutList::const_iterator it = layoutlist_.begin();
1635 LayoutList::const_iterator end = layoutlist_.end();
1636 for (; it != end; ++it)
1637 if (it->latexname() == lay)
1643 bool DocumentClass::provides(string const & p) const
1645 return provides_.find(p) != provides_.end();
1649 bool DocumentClass::hasTocLevels() const
1651 return min_toclevel_ != Layout::NOT_IN_TOC;
1655 Layout const & DocumentClass::getTOCLayout() const
1657 // we're going to look for the layout with the minimum toclevel
1658 TextClass::LayoutList::const_iterator lit = begin();
1659 TextClass::LayoutList::const_iterator const len = end();
1660 int minlevel = 1000;
1661 Layout const * lay = NULL;
1662 for (; lit != len; ++lit) {
1663 int const level = lit->toclevel;
1664 // we don't want Part or unnumbered sections
1665 if (level == Layout::NOT_IN_TOC || level < 0
1666 || level >= minlevel || lit->counter.empty())
1673 // hmm. that is very odd, so we'll do our best.
1674 return operator[](defaultLayoutName());
1678 Layout const & DocumentClass::htmlTOCLayout() const
1680 if (html_toc_section_.empty())
1681 html_toc_section_ = getTOCLayout().name();
1682 return operator[](html_toc_section_);
1686 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1687 string const & entry, string const & fallback) const
1689 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1691 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1692 if (itype == cite_formats_.end())
1693 return default_format;
1694 map<string, string>::const_iterator it = itype->second.find(entry);
1695 if (it == itype->second.end() && !fallback.empty())
1696 it = itype->second.find(fallback);
1697 if (it == itype->second.end())
1698 return default_format;
1703 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1704 string const & macro) const
1706 static string empty;
1707 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1708 if (itype == cite_macros_.end())
1710 map<string, string>::const_iterator it = itype->second.find(macro);
1711 if (it == itype->second.end())
1717 vector<string> const DocumentClass::citeCommands(
1718 CiteEngineType const & type) const
1720 vector<CitationStyle> const styles = citeStyles(type);
1721 vector<CitationStyle>::const_iterator it = styles.begin();
1722 vector<CitationStyle>::const_iterator end = styles.end();
1723 vector<string> cmds;
1724 for (; it != end; ++it) {
1725 CitationStyle const cite = *it;
1726 cmds.push_back(cite.cmd);
1732 vector<CitationStyle> const & DocumentClass::citeStyles(
1733 CiteEngineType const & type) const
1735 static vector<CitationStyle> empty;
1736 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1737 if (it == cite_styles_.end())
1743 /////////////////////////////////////////////////////////////////////////
1747 /////////////////////////////////////////////////////////////////////////
1749 ostream & operator<<(ostream & os, PageSides p)