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
451 string const inc = lexrc.getString();
452 if (!path().empty() && (prefixIs(inc, "./") ||
453 prefixIs(inc, "../")))
454 tmp = fileSearch(path(), inc, "layout");
456 tmp = libFileSearch("layouts", inc,
460 lexrc.printError("Could not find input file: " + inc);
462 } else if (!read(tmp, MERGE)) {
463 lexrc.printError("Error reading input file: " + tmp.absFileName());
469 case TC_DEFAULTSTYLE:
471 docstring const name = from_utf8(subst(lexrc.getString(),
473 defaultlayout_ = name;
480 case TC_PROVIDESTYLE:
481 // if modifystyle is true, then we got here by falling through
482 // so we are not in an ProvideStyle block
488 lexrc.printError("No name given for style: `$$Token'.");
492 docstring const name = from_utf8(subst(lexrc.getString(),
495 string s = "Could not read name for style: `$$Token' "
496 + lexrc.getString() + " is probably not valid UTF-8!";
499 // Since we couldn't read the name, we just scan the rest
500 // of the style and discard it.
501 error = !readStyle(lexrc, lay);
505 bool const have_layout = hasLayout(name);
507 // If the layout already exists, then we want to add it to
508 // the existing layout, as long as we are not in an ProvideStyle
510 if (have_layout && !providestyle) {
511 Layout & lay = operator[](name);
512 error = !readStyle(lexrc, lay);
514 // If the layout does not exist, then we want to create a new
515 // one, but not if we are in a ModifyStyle block.
516 else if (!have_layout && !modifystyle) {
518 layout.setName(name);
519 error = !readStyle(lexrc, layout);
521 layoutlist_.push_back(layout);
523 if (defaultlayout_.empty()) {
524 // We do not have a default layout yet, so we choose
525 // the first layout we encounter.
526 defaultlayout_ = name;
529 // There are two ways to get here:
530 // (i) The layout exists but we are in an ProvideStyle block
531 // (ii) The layout doesn't exist, but we are in an ModifyStyle
533 // Either way, we just scan the rest and discard it
536 readStyle(lexrc, lay);
543 docstring const style = from_utf8(subst(lexrc.getString(),
545 if (!deleteLayout(style))
546 lyxerr << "Cannot delete style `"
547 << to_utf8(style) << '\'' << endl;
551 case TC_NOINSETLAYOUT:
553 docstring const style = from_utf8(subst(lexrc.getString(),
555 if (!deleteInsetLayout(style))
556 LYXERR0("Style `" << style << "' cannot be removed\n"
557 "because it was not found!");
563 columns_ = lexrc.getInteger();
568 switch (lexrc.getInteger()) {
569 case 1: sides_ = OneSide; break;
570 case 2: sides_ = TwoSides; break;
572 lyxerr << "Impossible number of page"
573 " sides, setting to one."
583 pagestyle_ = rtrim(lexrc.getString());
587 defaultfont_ = lyxRead(lexrc);
588 if (!defaultfont_.resolved()) {
589 lexrc.printError("Warning: defaultfont should "
590 "be fully instantiated!");
591 defaultfont_.realize(sane_font);
597 secnumdepth_ = lexrc.getInteger();
602 tocdepth_ = lexrc.getInteger();
605 // First step to support options
606 case TC_CLASSOPTIONS:
607 readClassOptions(lexrc);
611 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
614 case TC_HTMLPREAMBLE:
615 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
619 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
622 case TC_HTMLTOCSECTION:
623 html_toc_section_ = from_utf8(trim(lexrc.getString()));
626 case TC_ADDTOPREAMBLE:
627 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
630 case TC_ADDTOHTMLPREAMBLE:
631 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
634 case TC_ADDTOHTMLSTYLES:
635 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
640 string const feature = lexrc.getString();
642 if (lexrc.getInteger())
643 provides_.insert(feature);
645 provides_.erase(feature);
651 vector<string> const req
652 = getVectorFromString(lexrc.getString());
653 requires_.insert(req.begin(), req.end());
659 string const pkg = lexrc.getString();
661 string const options = lexrc.getString();
662 package_options_[pkg] = options;
666 case TC_DEFAULTMODULE: {
668 string const module = lexrc.getString();
669 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
670 default_modules_.push_back(module);
674 case TC_PROVIDESMODULE: {
676 string const module = lexrc.getString();
677 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
678 provided_modules_.push_back(module);
682 case TC_EXCLUDESMODULE: {
684 string const module = lexrc.getString();
685 // modules already have their own way to exclude other modules
687 LYXERR0("ExcludesModule tag cannot be used in a module!");
690 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
691 excluded_modules_.push_back(module);
695 case TC_LEFTMARGIN: // left margin type
697 leftmargin_ = lexrc.getDocString();
700 case TC_RIGHTMARGIN: // right margin type
702 rightmargin_ = lexrc.getDocString();
705 case TC_INSETLAYOUT: {
707 lexrc.printError("No name given for InsetLayout: `$$Token'.");
711 docstring const name = subst(lexrc.getDocString(), '_', ' ');
713 string s = "Could not read name for InsetLayout: `$$Token' "
714 + lexrc.getString() + " is probably not valid UTF-8!";
717 // Since we couldn't read the name, we just scan the rest
718 // of the style and discard it.
719 il.read(lexrc, *this);
720 // Let's try to continue rather than abort.
722 } else if (hasInsetLayout(name)) {
723 InsetLayout & il = insetlayoutlist_[name];
724 error = !il.read(lexrc, *this);
728 error = !il.read(lexrc, *this);
730 insetlayoutlist_[name] = il;
736 error = !readFloat(lexrc);
740 error = !readCiteEngine(lexrc);
743 case TC_CITEENGINETYPE:
745 opt_enginetype_ = rtrim(lexrc.getString());
749 error = !readCiteFormat(lexrc);
752 case TC_DEFAULTBIBLIO:
754 cite_default_biblio_style_ = rtrim(lexrc.getString());
757 case TC_FULLAUTHORLIST:
759 cite_full_author_list_ &= lexrc.getBool();
764 docstring const cnt = lexrc.getDocString();
765 if (!counters_.remove(cnt))
766 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
775 docstring const name = lexrc.getDocString();
777 string s = "Could not read name for counter: `$$Token' "
778 + lexrc.getString() + " is probably not valid UTF-8!";
779 lexrc.printError(s.c_str());
781 // Since we couldn't read the name, we just scan the rest
785 error = !counters_.read(lexrc, name, !ifcounter);
788 lexrc.printError("No name given for style: `$$Token'.");
793 case TC_TITLELATEXTYPE:
794 readTitleType(lexrc);
797 case TC_TITLELATEXNAME:
799 titlename_ = lexrc.getString();
804 string const nofloat = lexrc.getString();
805 floatlist_.erase(nofloat);
809 case TC_OUTLINERNAME:
810 error = !readOutlinerName(lexrc);
814 // Note that this is triggered the first time through the loop unless
815 // we hit a format tag.
816 if (format != LAYOUT_FORMAT)
817 return FORMAT_MISMATCH;
820 // at present, we abort if we encounter an error,
821 // so there is no point continuing.
828 if (defaultlayout_.empty()) {
829 LYXERR0("Error: Textclass '" << name_
830 << "' is missing a defaultstyle.");
834 // Try to erase "stdinsets" from the provides_ set.
836 // Provides stdinsets 1
837 // declaration simply tells us that the standard insets have been
838 // defined. (It's found in stdinsets.inc but could also be used in
839 // user-defined files.) There isn't really any such package. So we
840 // might as well go ahead and erase it.
841 // If we do not succeed, then it was not there, which means that
842 // the textclass did not provide the definitions of the standard
843 // insets. So we need to try to load them.
844 int erased = provides_.erase("stdinsets");
846 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
849 frontend::Alert::warning(_("Missing File"),
850 _("Could not find stdinsets.inc! This may lead to data loss!"));
852 } else if (!read(tmp, MERGE)) {
853 frontend::Alert::warning(_("Corrupt File"),
854 _("Could not read stdinsets.inc! This may lead to data loss!"));
859 min_toclevel_ = Layout::NOT_IN_TOC;
860 max_toclevel_ = Layout::NOT_IN_TOC;
861 const_iterator lit = begin();
862 const_iterator len = end();
863 for (; lit != len; ++lit) {
864 int const toclevel = lit->toclevel;
865 if (toclevel != Layout::NOT_IN_TOC) {
866 if (min_toclevel_ == Layout::NOT_IN_TOC)
867 min_toclevel_ = toclevel;
869 min_toclevel_ = min(min_toclevel_, toclevel);
870 max_toclevel_ = max(max_toclevel_, toclevel);
873 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
874 << ", maximum is " << max_toclevel_);
876 return (error ? ERROR : OK);
880 void TextClass::readTitleType(Lexer & lexrc)
882 LexerKeyword titleTypeTags[] = {
883 { "commandafter", TITLE_COMMAND_AFTER },
884 { "environment", TITLE_ENVIRONMENT }
887 PushPopHelper pph(lexrc, titleTypeTags);
889 int le = lexrc.lex();
891 case Lexer::LEX_UNDEF:
892 lexrc.printError("Unknown output type `$$Token'");
894 case TITLE_COMMAND_AFTER:
895 case TITLE_ENVIRONMENT:
896 titletype_ = static_cast<TitleLatexType>(le);
899 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
905 void TextClass::readOutputType(Lexer & lexrc)
907 LexerKeyword outputTypeTags[] = {
908 { "docbook", DOCBOOK },
910 { "literate", LITERATE }
913 PushPopHelper pph(lexrc, outputTypeTags);
915 int le = lexrc.lex();
917 case Lexer::LEX_UNDEF:
918 lexrc.printError("Unknown output type `$$Token'");
923 outputType_ = static_cast<OutputType>(le);
926 LYXERR0("Unhandled value " << le);
932 void TextClass::readClassOptions(Lexer & lexrc)
942 LexerKeyword classOptionsTags[] = {
944 {"fontsize", CO_FONTSIZE },
945 {"header", CO_HEADER },
946 {"other", CO_OTHER },
947 {"pagestyle", CO_PAGESTYLE }
950 lexrc.pushTable(classOptionsTags);
952 while (!getout && lexrc.isOK()) {
953 int le = lexrc.lex();
955 case Lexer::LEX_UNDEF:
956 lexrc.printError("Unknown ClassOption tag `$$Token'");
964 opt_fontsize_ = rtrim(lexrc.getString());
968 opt_pagestyle_ = rtrim(lexrc.getString());
972 if (options_.empty())
973 options_ = lexrc.getString();
975 options_ += ',' + lexrc.getString();
979 class_header_ = subst(lexrc.getString(), """, "\"");
990 bool TextClass::readCiteEngine(Lexer & lexrc)
992 int const type = readCiteEngineType(lexrc);
993 if (type & ENGINE_TYPE_AUTHORYEAR)
994 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
995 if (type & ENGINE_TYPE_NUMERICAL)
996 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
997 if (type & ENGINE_TYPE_DEFAULT)
998 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1000 bool getout = false;
1001 while (!getout && lexrc.isOK()) {
1003 def = lexrc.getString();
1004 def = subst(def, " ", "");
1005 def = subst(def, "\t", "");
1006 if (compare_ascii_no_case(def, "end") == 0) {
1012 char ichar = def[0];
1016 cs.forceUpperCase = true;
1020 size_t const n = def.size();
1021 for (size_t i = 0; i != n; ++i) {
1024 cs.fullAuthorList = true;
1025 else if (ichar == '[' && cs.textAfter)
1026 cs.textBefore = true;
1027 else if (ichar == '[')
1028 cs.textAfter = true;
1029 else if (ichar != ']')
1034 if (type & ENGINE_TYPE_AUTHORYEAR)
1035 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1036 if (type & ENGINE_TYPE_NUMERICAL)
1037 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1038 if (type & ENGINE_TYPE_DEFAULT)
1039 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1045 int TextClass::readCiteEngineType(Lexer & lexrc) const
1047 LATTEST(ENGINE_TYPE_DEFAULT ==
1048 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1049 if (!lexrc.next()) {
1050 lexrc.printError("No cite engine type given for token: `$$Token'.");
1051 return ENGINE_TYPE_DEFAULT;
1053 string const type = rtrim(lexrc.getString());
1054 if (compare_ascii_no_case(type, "authoryear") == 0)
1055 return ENGINE_TYPE_AUTHORYEAR;
1056 else if (compare_ascii_no_case(type, "numerical") == 0)
1057 return ENGINE_TYPE_NUMERICAL;
1058 else if (compare_ascii_no_case(type, "default") != 0) {
1059 string const s = "Unknown cite engine type `" + type
1060 + "' given for token: `$$Token',";
1061 lexrc.printError(s);
1063 return ENGINE_TYPE_DEFAULT;
1067 bool TextClass::readCiteFormat(Lexer & lexrc)
1069 int const type = readCiteEngineType(lexrc);
1072 while (lexrc.isOK()) {
1074 etype = lexrc.getString();
1075 if (compare_ascii_no_case(etype, "end") == 0)
1080 definition = lexrc.getString();
1081 char initchar = etype[0];
1082 if (initchar == '#')
1084 if (initchar == '!' || initchar == '_') {
1085 if (type & ENGINE_TYPE_AUTHORYEAR)
1086 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1087 if (type & ENGINE_TYPE_NUMERICAL)
1088 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1089 if (type & ENGINE_TYPE_DEFAULT)
1090 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1092 if (type & ENGINE_TYPE_AUTHORYEAR)
1093 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1094 if (type & ENGINE_TYPE_NUMERICAL)
1095 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1096 if (type & ENGINE_TYPE_DEFAULT)
1097 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1104 bool TextClass::readFloat(Lexer & lexrc)
1121 FT_ALLOWED_PLACEMENT,
1127 LexerKeyword floatTags[] = {
1128 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1129 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1130 { "allowswide", FT_ALLOWS_WIDE },
1132 { "extension", FT_EXT },
1133 { "guiname", FT_NAME },
1134 { "htmlattr", FT_HTMLATTR },
1135 { "htmlstyle", FT_HTMLSTYLE },
1136 { "htmltag", FT_HTMLTAG },
1137 { "ispredefined", FT_PREDEFINED },
1138 { "listcommand", FT_LISTCOMMAND },
1139 { "listname", FT_LISTNAME },
1140 { "numberwithin", FT_WITHIN },
1141 { "placement", FT_PLACEMENT },
1142 { "refprefix", FT_REFPREFIX },
1143 { "style", FT_STYLE },
1144 { "type", FT_TYPE },
1145 { "usesfloatpkg", FT_USESFLOAT }
1148 lexrc.pushTable(floatTags);
1158 string allowed_placement = "!htbpH";
1163 bool usesfloat = true;
1164 bool ispredefined = false;
1165 bool allowswide = true;
1166 bool allowssideways = true;
1168 bool getout = false;
1169 while (!getout && lexrc.isOK()) {
1170 int le = lexrc.lex();
1172 case Lexer::LEX_UNDEF:
1173 lexrc.printError("Unknown float tag `$$Token'");
1181 type = lexrc.getString();
1182 if (floatlist_.typeExist(type)) {
1183 Floating const & fl = floatlist_.getType(type);
1184 placement = fl.placement();
1186 within = fl.within();
1189 listname = fl.listName();
1190 usesfloat = fl.usesFloatPkg();
1191 ispredefined = fl.isPredefined();
1192 listcommand = fl.listCommand();
1193 refprefix = fl.refPrefix();
1198 name = lexrc.getString();
1202 placement = lexrc.getString();
1204 case FT_ALLOWED_PLACEMENT:
1206 allowed_placement = lexrc.getString();
1210 ext = lexrc.getString();
1214 within = lexrc.getString();
1215 if (within == "none")
1220 style = lexrc.getString();
1222 case FT_LISTCOMMAND:
1224 listcommand = lexrc.getString();
1228 refprefix = lexrc.getString();
1232 listname = lexrc.getString();
1236 usesfloat = lexrc.getBool();
1240 ispredefined = lexrc.getBool();
1242 case FT_ALLOWS_SIDEWAYS:
1244 allowssideways = lexrc.getBool();
1246 case FT_ALLOWS_WIDE:
1248 allowswide = lexrc.getBool();
1252 htmlattr = lexrc.getString();
1256 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1260 htmltag = lexrc.getString();
1270 // Here we have a full float if getout == true
1272 if (!usesfloat && listcommand.empty()) {
1273 // if this float uses the same auxfile as an existing one,
1274 // there is no need for it to provide a list command.
1275 FloatList::const_iterator it = floatlist_.begin();
1276 FloatList::const_iterator en = floatlist_.end();
1277 bool found_ext = false;
1278 for (; it != en; ++it) {
1279 if (it->second.ext() == ext) {
1285 LYXERR0("The layout does not provide a list command " <<
1286 "for the float `" << type << "'. LyX will " <<
1287 "not be able to produce a float list.");
1289 Floating fl(type, placement, ext, within, style, name,
1290 listname, listcommand, refprefix, allowed_placement,
1291 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1292 allowswide, allowssideways);
1293 floatlist_.newFloat(fl);
1294 // each float has its own counter
1295 counters_.newCounter(from_ascii(type), from_ascii(within),
1296 docstring(), docstring());
1297 // also define sub-float counters
1298 docstring const subtype = "sub-" + from_ascii(type);
1299 counters_.newCounter(subtype, from_ascii(type),
1300 "\\alph{" + subtype + "}", docstring());
1306 bool TextClass::readOutlinerName(Lexer & lexrc)
1311 type = lexrc.getString();
1313 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1317 name = lexrc.getDocString();
1319 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1322 outliner_names_[type] = name;
1327 docstring TextClass::outlinerName(std::string const & type) const
1329 std::map<std::string,docstring>::const_iterator const it
1330 = outliner_names_.find(type);
1331 if (it == outliner_names_.end()) {
1332 LYXERR0("Missing OutlinerName for " << type << "!");
1333 return from_utf8(type);
1339 string const & TextClass::prerequisites(string const & sep) const
1341 if (contains(prerequisites_, ',')) {
1342 vector<string> const pres = getVectorFromString(prerequisites_);
1343 prerequisites_ = getStringFromVector(pres, sep);
1345 return prerequisites_;
1349 bool TextClass::hasLayout(docstring const & n) const
1351 docstring const name = n.empty() ? defaultLayoutName() : n;
1353 return find_if(layoutlist_.begin(), layoutlist_.end(),
1354 LayoutNamesEqual(name))
1355 != layoutlist_.end();
1359 bool TextClass::hasInsetLayout(docstring const & n) const
1363 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1364 return it != insetlayoutlist_.end();
1368 Layout const & TextClass::operator[](docstring const & name) const
1370 LATTEST(!name.empty());
1373 find_if(begin(), end(), LayoutNamesEqual(name));
1376 LYXERR0("We failed to find the layout '" << name
1377 << "' in the layout list. You MUST investigate!");
1378 for (const_iterator cit = begin(); cit != end(); ++cit)
1379 lyxerr << " " << to_utf8(cit->name()) << endl;
1381 // We require the name to exist
1382 static const Layout dummy;
1383 LASSERT(false, return dummy);
1390 Layout & TextClass::operator[](docstring const & name)
1392 LATTEST(!name.empty());
1393 // Safe to continue, given what we do below.
1395 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1398 LYXERR0("We failed to find the layout '" << to_utf8(name)
1399 << "' in the layout list. You MUST investigate!");
1400 for (const_iterator cit = begin(); cit != end(); ++cit)
1401 LYXERR0(" " << to_utf8(cit->name()));
1403 // we require the name to exist
1405 // we are here only in release mode
1406 layoutlist_.push_back(createBasicLayout(name, true));
1407 it = find_if(begin(), end(), LayoutNamesEqual(name));
1414 bool TextClass::deleteLayout(docstring const & name)
1416 if (name == defaultLayoutName() || name == plainLayoutName())
1419 LayoutList::iterator it =
1420 remove_if(layoutlist_.begin(), layoutlist_.end(),
1421 LayoutNamesEqual(name));
1423 LayoutList::iterator end = layoutlist_.end();
1424 bool const ret = (it != end);
1425 layoutlist_.erase(it, end);
1430 bool TextClass::deleteInsetLayout(docstring const & name)
1432 return insetlayoutlist_.erase(name);
1436 // Load textclass info if not loaded yet
1437 bool TextClass::load(string const & path) const
1442 // Read style-file, provided path is searched before the system ones
1443 // If path is a file, it is loaded directly.
1444 FileName layout_file(path);
1445 if (!path.empty() && !layout_file.isReadableFile())
1446 layout_file = FileName(addName(path, name_ + ".layout"));
1447 if (layout_file.empty() || !layout_file.exists())
1448 layout_file = libFileSearch("layouts", name_, "layout");
1449 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1452 lyxerr << "Error reading `"
1453 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1454 << "'\n(Check `" << name_
1455 << "')\nCheck your installation and "
1456 "try Options/Reconfigure..."
1464 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1469 layoutlist_.push_back(createBasicLayout(n, true));
1474 string DocumentClass::forcedLayouts() const
1478 const_iterator const e = end();
1479 for (const_iterator i = begin(); i != e; ++i) {
1480 if (i->forcelocal > 0) {
1482 os << "Format " << LAYOUT_FORMAT << '\n';
1492 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1494 // FIXME The fix for the InsetLayout part of 4812 would be here:
1495 // Add the InsetLayout to the document class if it is not found.
1497 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1498 while (!n.empty()) {
1499 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1500 if (cit != cen && cit->first == n) {
1501 if (cit->second.obsoleted_by().empty())
1503 n = cit->second.obsoleted_by();
1504 return insetLayout(n);
1506 // If we have a generic prefix (e.g., "Note:"),
1507 // try if this one alone is found.
1508 size_t i = n.find(':');
1509 if (i == string::npos)
1513 // Layout "name" not found.
1514 return plainInsetLayout();
1518 InsetLayout const & DocumentClass::plainInsetLayout() {
1519 static const InsetLayout plain_insetlayout_;
1520 return plain_insetlayout_;
1524 docstring const & TextClass::defaultLayoutName() const
1526 return defaultlayout_;
1530 Layout const & TextClass::defaultLayout() const
1532 return operator[](defaultLayoutName());
1536 bool TextClass::isDefaultLayout(Layout const & layout) const
1538 return layout.name() == defaultLayoutName();
1542 bool TextClass::isPlainLayout(Layout const & layout) const
1544 return layout.name() == plainLayoutName();
1548 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1550 static Layout * defaultLayout = NULL;
1552 if (defaultLayout) {
1553 defaultLayout->setUnknown(unknown);
1554 defaultLayout->setName(name);
1555 return *defaultLayout;
1558 static char const * s = "Margin Static\n"
1559 "LatexType Paragraph\n"
1562 "AlignPossible Left, Right, Center\n"
1563 "LabelType No_Label\n"
1565 istringstream ss(s);
1566 Lexer lex(textClassTags);
1568 defaultLayout = new Layout;
1569 defaultLayout->setUnknown(unknown);
1570 defaultLayout->setName(name);
1571 if (!readStyle(lex, *defaultLayout)) {
1572 // The only way this happens is because the hardcoded layout above
1576 return *defaultLayout;
1580 DocumentClassPtr getDocumentClass(
1581 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1584 DocumentClassPtr doc_class =
1585 DocumentClassPtr(new DocumentClass(baseClass));
1586 LayoutModuleList::const_iterator it = modlist.begin();
1587 LayoutModuleList::const_iterator en = modlist.end();
1588 for (; it != en; ++it) {
1589 string const modName = *it;
1590 LyXModule * lm = theModuleList[modName];
1592 docstring const msg =
1593 bformat(_("The module %1$s has been requested by\n"
1594 "this document but has not been found in the list of\n"
1595 "available modules. If you recently installed it, you\n"
1596 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1598 frontend::Alert::warning(_("Module not available"), msg);
1601 if (!lm->isAvailable() && !clone) {
1602 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1603 docstring const msg =
1604 bformat(_("The module %1$s requires a package that is not\n"
1605 "available in your LaTeX installation, or a converter that\n"
1606 "you have not installed. LaTeX output may not be possible.\n"
1607 "Missing prerequisites:\n"
1609 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1610 from_utf8(modName), prereqs);
1611 frontend::Alert::warning(_("Package not available"), msg, true);
1613 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1614 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1615 docstring const msg =
1616 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1617 frontend::Alert::warning(_("Read Error"), msg);
1624 /////////////////////////////////////////////////////////////////////////
1628 /////////////////////////////////////////////////////////////////////////
1630 DocumentClass::DocumentClass(LayoutFile const & tc)
1635 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1637 LayoutList::const_iterator it = layoutlist_.begin();
1638 LayoutList::const_iterator end = layoutlist_.end();
1639 for (; it != end; ++it)
1640 if (it->latexname() == lay)
1646 bool DocumentClass::provides(string const & p) const
1648 return provides_.find(p) != provides_.end();
1652 bool DocumentClass::hasTocLevels() const
1654 return min_toclevel_ != Layout::NOT_IN_TOC;
1658 Layout const & DocumentClass::getTOCLayout() const
1660 // we're going to look for the layout with the minimum toclevel
1661 TextClass::LayoutList::const_iterator lit = begin();
1662 TextClass::LayoutList::const_iterator const len = end();
1663 int minlevel = 1000;
1664 Layout const * lay = NULL;
1665 for (; lit != len; ++lit) {
1666 int const level = lit->toclevel;
1667 // we don't want Part or unnumbered sections
1668 if (level == Layout::NOT_IN_TOC || level < 0
1669 || level >= minlevel || lit->counter.empty())
1676 // hmm. that is very odd, so we'll do our best.
1677 return operator[](defaultLayoutName());
1681 Layout const & DocumentClass::htmlTOCLayout() const
1683 if (html_toc_section_.empty())
1684 html_toc_section_ = getTOCLayout().name();
1685 return operator[](html_toc_section_);
1689 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1690 string const & entry, string const & fallback) const
1692 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1694 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1695 if (itype == cite_formats_.end())
1696 return default_format;
1697 map<string, string>::const_iterator it = itype->second.find(entry);
1698 if (it == itype->second.end() && !fallback.empty())
1699 it = itype->second.find(fallback);
1700 if (it == itype->second.end())
1701 return default_format;
1706 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1707 string const & macro) const
1709 static string empty;
1710 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1711 if (itype == cite_macros_.end())
1713 map<string, string>::const_iterator it = itype->second.find(macro);
1714 if (it == itype->second.end())
1720 vector<string> const DocumentClass::citeCommands(
1721 CiteEngineType const & type) const
1723 vector<CitationStyle> const styles = citeStyles(type);
1724 vector<CitationStyle>::const_iterator it = styles.begin();
1725 vector<CitationStyle>::const_iterator end = styles.end();
1726 vector<string> cmds;
1727 for (; it != end; ++it) {
1728 CitationStyle const cite = *it;
1729 cmds.push_back(cite.cmd);
1735 vector<CitationStyle> const & DocumentClass::citeStyles(
1736 CiteEngineType const & type) const
1738 static vector<CitationStyle> empty;
1739 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1740 if (it == cite_styles_.end())
1746 /////////////////////////////////////////////////////////////////////////
1750 /////////////////////////////////////////////////////////////////////////
1752 ostream & operator<<(ostream & os, PageSides p)