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 = 59; //gm: OutlinerName, AddToToc, IsTocCaption
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 readStyle(lexrc, lay);
538 docstring const style = from_utf8(subst(lexrc.getString(),
540 if (!deleteLayout(style))
541 lyxerr << "Cannot delete style `"
542 << to_utf8(style) << '\'' << endl;
546 case TC_NOINSETLAYOUT:
548 docstring const style = from_utf8(subst(lexrc.getString(),
550 if (!deleteInsetLayout(style))
551 LYXERR0("Style `" << style << "' cannot be removed\n"
552 "because it was not found!");
558 columns_ = lexrc.getInteger();
563 switch (lexrc.getInteger()) {
564 case 1: sides_ = OneSide; break;
565 case 2: sides_ = TwoSides; break;
567 lyxerr << "Impossible number of page"
568 " sides, setting to one."
578 pagestyle_ = rtrim(lexrc.getString());
582 defaultfont_ = lyxRead(lexrc);
583 if (!defaultfont_.resolved()) {
584 lexrc.printError("Warning: defaultfont should "
585 "be fully instantiated!");
586 defaultfont_.realize(sane_font);
592 secnumdepth_ = lexrc.getInteger();
597 tocdepth_ = lexrc.getInteger();
600 // First step to support options
601 case TC_CLASSOPTIONS:
602 readClassOptions(lexrc);
606 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
609 case TC_HTMLPREAMBLE:
610 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
614 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
617 case TC_HTMLTOCSECTION:
618 html_toc_section_ = from_utf8(trim(lexrc.getString()));
621 case TC_ADDTOPREAMBLE:
622 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
625 case TC_ADDTOHTMLPREAMBLE:
626 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
629 case TC_ADDTOHTMLSTYLES:
630 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
635 string const feature = lexrc.getString();
637 if (lexrc.getInteger())
638 provides_.insert(feature);
640 provides_.erase(feature);
646 vector<string> const req
647 = getVectorFromString(lexrc.getString());
648 requires_.insert(req.begin(), req.end());
654 string const pkg = lexrc.getString();
656 string const options = lexrc.getString();
657 package_options_[pkg] = options;
661 case TC_DEFAULTMODULE: {
663 string const module = lexrc.getString();
664 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
665 default_modules_.push_back(module);
669 case TC_PROVIDESMODULE: {
671 string const module = lexrc.getString();
672 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
673 provided_modules_.push_back(module);
677 case TC_EXCLUDESMODULE: {
679 string const module = lexrc.getString();
680 // modules already have their own way to exclude other modules
682 LYXERR0("ExcludesModule tag cannot be used in a module!");
685 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
686 excluded_modules_.push_back(module);
690 case TC_LEFTMARGIN: // left margin type
692 leftmargin_ = lexrc.getDocString();
695 case TC_RIGHTMARGIN: // right margin type
697 rightmargin_ = lexrc.getDocString();
700 case TC_INSETLAYOUT: {
702 lexrc.printError("No name given for InsetLayout: `$$Token'.");
706 docstring const name = subst(lexrc.getDocString(), '_', ' ');
708 string s = "Could not read name for InsetLayout: `$$Token' "
709 + lexrc.getString() + " is probably not valid UTF-8!";
712 // Since we couldn't read the name, we just scan the rest
713 // of the style and discard it.
714 il.read(lexrc, *this);
715 // Let's try to continue rather than abort.
717 } else if (hasInsetLayout(name)) {
718 InsetLayout & il = insetlayoutlist_[name];
719 error = !il.read(lexrc, *this);
723 error = !il.read(lexrc, *this);
725 insetlayoutlist_[name] = il;
731 error = !readFloat(lexrc);
735 error = !readCiteEngine(lexrc);
738 case TC_CITEENGINETYPE:
740 opt_enginetype_ = rtrim(lexrc.getString());
744 error = !readCiteFormat(lexrc);
747 case TC_DEFAULTBIBLIO:
749 cite_default_biblio_style_ = rtrim(lexrc.getString());
752 case TC_FULLAUTHORLIST:
754 cite_full_author_list_ &= lexrc.getBool();
759 docstring const cnt = lexrc.getDocString();
760 if (!counters_.remove(cnt))
761 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
770 docstring const name = lexrc.getDocString();
772 string s = "Could not read name for counter: `$$Token' "
773 + lexrc.getString() + " is probably not valid UTF-8!";
774 lexrc.printError(s.c_str());
776 // Since we couldn't read the name, we just scan the rest
780 error = !counters_.read(lexrc, name, !ifcounter);
783 lexrc.printError("No name given for style: `$$Token'.");
788 case TC_TITLELATEXTYPE:
789 readTitleType(lexrc);
792 case TC_TITLELATEXNAME:
794 titlename_ = lexrc.getString();
799 string const nofloat = lexrc.getString();
800 floatlist_.erase(nofloat);
804 case TC_OUTLINERNAME:
805 error = !readOutlinerName(lexrc);
809 // Note that this is triggered the first time through the loop unless
810 // we hit a format tag.
811 if (format != LAYOUT_FORMAT)
812 return FORMAT_MISMATCH;
815 // at present, we abort if we encounter an error,
816 // so there is no point continuing.
823 if (defaultlayout_.empty()) {
824 LYXERR0("Error: Textclass '" << name_
825 << "' is missing a defaultstyle.");
829 // Try to erase "stdinsets" from the provides_ set.
831 // Provides stdinsets 1
832 // declaration simply tells us that the standard insets have been
833 // defined. (It's found in stdinsets.inc but could also be used in
834 // user-defined files.) There isn't really any such package. So we
835 // might as well go ahead and erase it.
836 // If we do not succeed, then it was not there, which means that
837 // the textclass did not provide the definitions of the standard
838 // insets. So we need to try to load them.
839 int erased = provides_.erase("stdinsets");
841 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
844 frontend::Alert::warning(_("Missing File"),
845 _("Could not find stdinsets.inc! This may lead to data loss!"));
847 } else if (!read(tmp, MERGE)) {
848 frontend::Alert::warning(_("Corrupt File"),
849 _("Could not read stdinsets.inc! This may lead to data loss!"));
854 min_toclevel_ = Layout::NOT_IN_TOC;
855 max_toclevel_ = Layout::NOT_IN_TOC;
856 const_iterator lit = begin();
857 const_iterator len = end();
858 for (; lit != len; ++lit) {
859 int const toclevel = lit->toclevel;
860 if (toclevel != Layout::NOT_IN_TOC) {
861 if (min_toclevel_ == Layout::NOT_IN_TOC)
862 min_toclevel_ = toclevel;
864 min_toclevel_ = min(min_toclevel_, toclevel);
865 max_toclevel_ = max(max_toclevel_, toclevel);
868 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
869 << ", maximum is " << max_toclevel_);
871 return (error ? ERROR : OK);
875 void TextClass::readTitleType(Lexer & lexrc)
877 LexerKeyword titleTypeTags[] = {
878 { "commandafter", TITLE_COMMAND_AFTER },
879 { "environment", TITLE_ENVIRONMENT }
882 PushPopHelper pph(lexrc, titleTypeTags);
884 int le = lexrc.lex();
886 case Lexer::LEX_UNDEF:
887 lexrc.printError("Unknown output type `$$Token'");
889 case TITLE_COMMAND_AFTER:
890 case TITLE_ENVIRONMENT:
891 titletype_ = static_cast<TitleLatexType>(le);
894 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
900 void TextClass::readOutputType(Lexer & lexrc)
902 LexerKeyword outputTypeTags[] = {
903 { "docbook", DOCBOOK },
905 { "literate", LITERATE }
908 PushPopHelper pph(lexrc, outputTypeTags);
910 int le = lexrc.lex();
912 case Lexer::LEX_UNDEF:
913 lexrc.printError("Unknown output type `$$Token'");
918 outputType_ = static_cast<OutputType>(le);
921 LYXERR0("Unhandled value " << le);
927 void TextClass::readClassOptions(Lexer & lexrc)
937 LexerKeyword classOptionsTags[] = {
939 {"fontsize", CO_FONTSIZE },
940 {"header", CO_HEADER },
941 {"other", CO_OTHER },
942 {"pagestyle", CO_PAGESTYLE }
945 lexrc.pushTable(classOptionsTags);
947 while (!getout && lexrc.isOK()) {
948 int le = lexrc.lex();
950 case Lexer::LEX_UNDEF:
951 lexrc.printError("Unknown ClassOption tag `$$Token'");
959 opt_fontsize_ = rtrim(lexrc.getString());
963 opt_pagestyle_ = rtrim(lexrc.getString());
967 if (options_.empty())
968 options_ = lexrc.getString();
970 options_ += ',' + lexrc.getString();
974 class_header_ = subst(lexrc.getString(), """, "\"");
985 bool TextClass::readCiteEngine(Lexer & lexrc)
987 int const type = readCiteEngineType(lexrc);
988 if (type & ENGINE_TYPE_AUTHORYEAR)
989 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
990 if (type & ENGINE_TYPE_NUMERICAL)
991 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
992 if (type & ENGINE_TYPE_DEFAULT)
993 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
996 while (!getout && lexrc.isOK()) {
998 def = lexrc.getString();
999 def = subst(def, " ", "");
1000 def = subst(def, "\t", "");
1001 if (compare_ascii_no_case(def, "end") == 0) {
1007 char ichar = def[0];
1011 cs.forceUpperCase = true;
1015 size_t const n = def.size();
1016 for (size_t i = 0; i != n; ++i) {
1019 cs.fullAuthorList = true;
1020 else if (ichar == '[' && cs.textAfter)
1021 cs.textBefore = true;
1022 else if (ichar == '[')
1023 cs.textAfter = true;
1024 else if (ichar != ']')
1029 if (type & ENGINE_TYPE_AUTHORYEAR)
1030 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1031 if (type & ENGINE_TYPE_NUMERICAL)
1032 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1033 if (type & ENGINE_TYPE_DEFAULT)
1034 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1040 int TextClass::readCiteEngineType(Lexer & lexrc) const
1042 LATTEST(ENGINE_TYPE_DEFAULT ==
1043 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1044 if (!lexrc.next()) {
1045 lexrc.printError("No cite engine type given for token: `$$Token'.");
1046 return ENGINE_TYPE_DEFAULT;
1048 string const type = rtrim(lexrc.getString());
1049 if (compare_ascii_no_case(type, "authoryear") == 0)
1050 return ENGINE_TYPE_AUTHORYEAR;
1051 else if (compare_ascii_no_case(type, "numerical") == 0)
1052 return ENGINE_TYPE_NUMERICAL;
1053 else if (compare_ascii_no_case(type, "default") != 0) {
1054 string const s = "Unknown cite engine type `" + type
1055 + "' given for token: `$$Token',";
1056 lexrc.printError(s);
1058 return ENGINE_TYPE_DEFAULT;
1062 bool TextClass::readCiteFormat(Lexer & lexrc)
1064 int const type = readCiteEngineType(lexrc);
1067 while (lexrc.isOK()) {
1069 etype = lexrc.getString();
1070 if (compare_ascii_no_case(etype, "end") == 0)
1075 definition = lexrc.getString();
1076 char initchar = etype[0];
1077 if (initchar == '#')
1079 if (initchar == '!' || initchar == '_') {
1080 if (type & ENGINE_TYPE_AUTHORYEAR)
1081 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1082 if (type & ENGINE_TYPE_NUMERICAL)
1083 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1084 if (type & ENGINE_TYPE_DEFAULT)
1085 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1087 if (type & ENGINE_TYPE_AUTHORYEAR)
1088 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1089 if (type & ENGINE_TYPE_NUMERICAL)
1090 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1091 if (type & ENGINE_TYPE_DEFAULT)
1092 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1099 bool TextClass::readFloat(Lexer & lexrc)
1116 FT_ALLOWED_PLACEMENT,
1122 LexerKeyword floatTags[] = {
1123 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1124 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1125 { "allowswide", FT_ALLOWS_WIDE },
1127 { "extension", FT_EXT },
1128 { "guiname", FT_NAME },
1129 { "htmlattr", FT_HTMLATTR },
1130 { "htmlstyle", FT_HTMLSTYLE },
1131 { "htmltag", FT_HTMLTAG },
1132 { "ispredefined", FT_PREDEFINED },
1133 { "listcommand", FT_LISTCOMMAND },
1134 { "listname", FT_LISTNAME },
1135 { "numberwithin", FT_WITHIN },
1136 { "placement", FT_PLACEMENT },
1137 { "refprefix", FT_REFPREFIX },
1138 { "style", FT_STYLE },
1139 { "type", FT_TYPE },
1140 { "usesfloatpkg", FT_USESFLOAT }
1143 lexrc.pushTable(floatTags);
1153 string allowed_placement = "!htbpH";
1158 bool usesfloat = true;
1159 bool ispredefined = false;
1160 bool allowswide = true;
1161 bool allowssideways = true;
1163 bool getout = false;
1164 while (!getout && lexrc.isOK()) {
1165 int le = lexrc.lex();
1167 case Lexer::LEX_UNDEF:
1168 lexrc.printError("Unknown float tag `$$Token'");
1176 type = lexrc.getString();
1177 if (floatlist_.typeExist(type)) {
1178 Floating const & fl = floatlist_.getType(type);
1179 placement = fl.placement();
1181 within = fl.within();
1184 listname = fl.listName();
1185 usesfloat = fl.usesFloatPkg();
1186 ispredefined = fl.isPredefined();
1187 listcommand = fl.listCommand();
1188 refprefix = fl.refPrefix();
1193 name = lexrc.getString();
1197 placement = lexrc.getString();
1199 case FT_ALLOWED_PLACEMENT:
1201 allowed_placement = lexrc.getString();
1205 ext = lexrc.getString();
1209 within = lexrc.getString();
1210 if (within == "none")
1215 style = lexrc.getString();
1217 case FT_LISTCOMMAND:
1219 listcommand = lexrc.getString();
1223 refprefix = lexrc.getString();
1227 listname = lexrc.getString();
1231 usesfloat = lexrc.getBool();
1235 ispredefined = lexrc.getBool();
1237 case FT_ALLOWS_SIDEWAYS:
1239 allowssideways = lexrc.getBool();
1241 case FT_ALLOWS_WIDE:
1243 allowswide = lexrc.getBool();
1247 htmlattr = lexrc.getString();
1251 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1255 htmltag = lexrc.getString();
1265 // Here we have a full float if getout == true
1267 if (!usesfloat && listcommand.empty()) {
1268 // if this float uses the same auxfile as an existing one,
1269 // there is no need for it to provide a list command.
1270 FloatList::const_iterator it = floatlist_.begin();
1271 FloatList::const_iterator en = floatlist_.end();
1272 bool found_ext = false;
1273 for (; it != en; ++it) {
1274 if (it->second.ext() == ext) {
1280 LYXERR0("The layout does not provide a list command " <<
1281 "for the float `" << type << "'. LyX will " <<
1282 "not be able to produce a float list.");
1284 Floating fl(type, placement, ext, within, style, name,
1285 listname, listcommand, refprefix, allowed_placement,
1286 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1287 allowswide, allowssideways);
1288 floatlist_.newFloat(fl);
1289 // each float has its own counter
1290 counters_.newCounter(from_ascii(type), from_ascii(within),
1291 docstring(), docstring());
1292 // also define sub-float counters
1293 docstring const subtype = "sub-" + from_ascii(type);
1294 counters_.newCounter(subtype, from_ascii(type),
1295 "\\alph{" + subtype + "}", docstring());
1301 bool TextClass::readOutlinerName(Lexer & lexrc)
1306 type = lexrc.getString();
1308 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1312 name = lexrc.getDocString();
1314 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1317 outliner_names_[type] = name;
1322 docstring TextClass::outlinerName(std::string const & type) const
1324 std::map<std::string,docstring>::const_iterator const it
1325 = outliner_names_.find(type);
1326 if (it == outliner_names_.end()) {
1327 LYXERR0("Missing OutlinerName for " << type << "!");
1328 return from_utf8(type);
1334 string const & TextClass::prerequisites(string const & sep) const
1336 if (contains(prerequisites_, ',')) {
1337 vector<string> const pres = getVectorFromString(prerequisites_);
1338 prerequisites_ = getStringFromVector(pres, sep);
1340 return prerequisites_;
1344 bool TextClass::hasLayout(docstring const & n) const
1346 docstring const name = n.empty() ? defaultLayoutName() : n;
1348 return find_if(layoutlist_.begin(), layoutlist_.end(),
1349 LayoutNamesEqual(name))
1350 != layoutlist_.end();
1354 bool TextClass::hasInsetLayout(docstring const & n) const
1358 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1359 return it != insetlayoutlist_.end();
1363 Layout const & TextClass::operator[](docstring const & name) const
1365 LATTEST(!name.empty());
1368 find_if(begin(), end(), LayoutNamesEqual(name));
1371 LYXERR0("We failed to find the layout '" << name
1372 << "' in the layout list. You MUST investigate!");
1373 for (const_iterator cit = begin(); cit != end(); ++cit)
1374 lyxerr << " " << to_utf8(cit->name()) << endl;
1376 // We require the name to exist
1377 static const Layout dummy;
1378 LASSERT(false, return dummy);
1385 Layout & TextClass::operator[](docstring const & name)
1387 LATTEST(!name.empty());
1388 // Safe to continue, given what we do below.
1390 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1393 LYXERR0("We failed to find the layout '" << to_utf8(name)
1394 << "' in the layout list. You MUST investigate!");
1395 for (const_iterator cit = begin(); cit != end(); ++cit)
1396 LYXERR0(" " << to_utf8(cit->name()));
1398 // we require the name to exist
1400 // we are here only in release mode
1401 layoutlist_.push_back(createBasicLayout(name, true));
1402 it = find_if(begin(), end(), LayoutNamesEqual(name));
1409 bool TextClass::deleteLayout(docstring const & name)
1411 if (name == defaultLayoutName() || name == plainLayoutName())
1414 LayoutList::iterator it =
1415 remove_if(layoutlist_.begin(), layoutlist_.end(),
1416 LayoutNamesEqual(name));
1418 LayoutList::iterator end = layoutlist_.end();
1419 bool const ret = (it != end);
1420 layoutlist_.erase(it, end);
1425 bool TextClass::deleteInsetLayout(docstring const & name)
1427 return insetlayoutlist_.erase(name);
1431 // Load textclass info if not loaded yet
1432 bool TextClass::load(string const & path) const
1437 // Read style-file, provided path is searched before the system ones
1438 // If path is a file, it is loaded directly.
1439 FileName layout_file(path);
1440 if (!path.empty() && !layout_file.isReadableFile())
1441 layout_file = FileName(addName(path, name_ + ".layout"));
1442 if (layout_file.empty() || !layout_file.exists())
1443 layout_file = libFileSearch("layouts", name_, "layout");
1444 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1447 lyxerr << "Error reading `"
1448 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1449 << "'\n(Check `" << name_
1450 << "')\nCheck your installation and "
1451 "try Options/Reconfigure..."
1459 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1464 layoutlist_.push_back(createBasicLayout(n, true));
1469 string DocumentClass::forcedLayouts() const
1473 const_iterator const e = end();
1474 for (const_iterator i = begin(); i != e; ++i) {
1475 if (i->forcelocal > 0) {
1477 os << "Format " << LAYOUT_FORMAT << '\n';
1487 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1489 // FIXME The fix for the InsetLayout part of 4812 would be here:
1490 // Add the InsetLayout to the document class if it is not found.
1492 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1493 while (!n.empty()) {
1494 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1495 if (cit != cen && cit->first == n) {
1496 if (cit->second.obsoleted_by().empty())
1498 n = cit->second.obsoleted_by();
1499 return insetLayout(n);
1501 // If we have a generic prefix (e.g., "Note:"),
1502 // try if this one alone is found.
1503 size_t i = n.find(':');
1504 if (i == string::npos)
1508 // Layout "name" not found.
1509 return plainInsetLayout();
1513 InsetLayout const & DocumentClass::plainInsetLayout() {
1514 static InsetLayout plain_insetlayout_;
1515 return plain_insetlayout_;
1519 docstring const & TextClass::defaultLayoutName() const
1521 return defaultlayout_;
1525 Layout const & TextClass::defaultLayout() const
1527 return operator[](defaultLayoutName());
1531 bool TextClass::isDefaultLayout(Layout const & layout) const
1533 return layout.name() == defaultLayoutName();
1537 bool TextClass::isPlainLayout(Layout const & layout) const
1539 return layout.name() == plainLayoutName();
1543 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1545 static Layout * defaultLayout = NULL;
1547 if (defaultLayout) {
1548 defaultLayout->setUnknown(unknown);
1549 defaultLayout->setName(name);
1550 return *defaultLayout;
1553 static char const * s = "Margin Static\n"
1554 "LatexType Paragraph\n"
1557 "AlignPossible Left, Right, Center\n"
1558 "LabelType No_Label\n"
1560 istringstream ss(s);
1561 Lexer lex(textClassTags);
1563 defaultLayout = new Layout;
1564 defaultLayout->setUnknown(unknown);
1565 defaultLayout->setName(name);
1566 if (!readStyle(lex, *defaultLayout)) {
1567 // The only way this happens is because the hardcoded layout above
1571 return *defaultLayout;
1575 DocumentClassPtr getDocumentClass(
1576 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1579 DocumentClassPtr doc_class =
1580 DocumentClassPtr(new DocumentClass(baseClass));
1581 LayoutModuleList::const_iterator it = modlist.begin();
1582 LayoutModuleList::const_iterator en = modlist.end();
1583 for (; it != en; ++it) {
1584 string const modName = *it;
1585 LyXModule * lm = theModuleList[modName];
1587 docstring const msg =
1588 bformat(_("The module %1$s has been requested by\n"
1589 "this document but has not been found in the list of\n"
1590 "available modules. If you recently installed it, you\n"
1591 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1593 frontend::Alert::warning(_("Module not available"), msg);
1596 if (!lm->isAvailable() && !clone) {
1597 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1598 docstring const msg =
1599 bformat(_("The module %1$s requires a package that is not\n"
1600 "available in your LaTeX installation, or a converter that\n"
1601 "you have not installed. LaTeX output may not be possible.\n"
1602 "Missing prerequisites:\n"
1604 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1605 from_utf8(modName), prereqs);
1606 frontend::Alert::warning(_("Package not available"), msg, true);
1608 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1609 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1610 docstring const msg =
1611 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1612 frontend::Alert::warning(_("Read Error"), msg);
1619 /////////////////////////////////////////////////////////////////////////
1623 /////////////////////////////////////////////////////////////////////////
1625 DocumentClass::DocumentClass(LayoutFile const & tc)
1630 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1632 LayoutList::const_iterator it = layoutlist_.begin();
1633 LayoutList::const_iterator end = layoutlist_.end();
1634 for (; it != end; ++it)
1635 if (it->latexname() == lay)
1641 bool DocumentClass::provides(string const & p) const
1643 return provides_.find(p) != provides_.end();
1647 bool DocumentClass::hasTocLevels() const
1649 return min_toclevel_ != Layout::NOT_IN_TOC;
1653 Layout const & DocumentClass::getTOCLayout() const
1655 // we're going to look for the layout with the minimum toclevel
1656 TextClass::LayoutList::const_iterator lit = begin();
1657 TextClass::LayoutList::const_iterator const len = end();
1658 int minlevel = 1000;
1659 Layout const * lay = NULL;
1660 for (; lit != len; ++lit) {
1661 int const level = lit->toclevel;
1662 // we don't want Part or unnumbered sections
1663 if (level == Layout::NOT_IN_TOC || level < 0
1664 || level >= minlevel || lit->counter.empty())
1671 // hmm. that is very odd, so we'll do our best.
1672 return operator[](defaultLayoutName());
1676 Layout const & DocumentClass::htmlTOCLayout() const
1678 if (html_toc_section_.empty())
1679 html_toc_section_ = getTOCLayout().name();
1680 return operator[](html_toc_section_);
1684 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1685 string const & entry, string const & fallback) const
1687 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1689 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1690 if (itype == cite_formats_.end())
1691 return default_format;
1692 map<string, string>::const_iterator it = itype->second.find(entry);
1693 if (it == itype->second.end() && !fallback.empty())
1694 it = itype->second.find(fallback);
1695 if (it == itype->second.end())
1696 return default_format;
1701 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1702 string const & macro) const
1704 static string empty;
1705 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1706 if (itype == cite_macros_.end())
1708 map<string, string>::const_iterator it = itype->second.find(macro);
1709 if (it == itype->second.end())
1715 vector<string> const DocumentClass::citeCommands(
1716 CiteEngineType const & type) const
1718 vector<CitationStyle> const styles = citeStyles(type);
1719 vector<CitationStyle>::const_iterator it = styles.begin();
1720 vector<CitationStyle>::const_iterator end = styles.end();
1721 vector<string> cmds;
1722 for (; it != end; ++it) {
1723 CitationStyle const cite = *it;
1724 cmds.push_back(cite.cmd);
1730 vector<CitationStyle> const & DocumentClass::citeStyles(
1731 CiteEngineType const & type) const
1733 static vector<CitationStyle> empty;
1734 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1735 if (it == cite_styles_.end())
1741 /////////////////////////////////////////////////////////////////////////
1745 /////////////////////////////////////////////////////////////////////////
1747 ostream & operator<<(ostream & os, PageSides p)