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/FileName.h"
34 #include "support/filetools.h"
35 #include "support/gettext.h"
36 #include "support/lstrings.h"
37 #include "support/os.h"
48 using namespace lyx::support;
52 // Keep the changes documented in the Customization manual.
54 // If you change this format, then you MUST also make sure that
55 // your changes do not invalidate the hardcoded layout file in
56 // LayoutFile.cpp. Additions will never do so, but syntax changes
57 // could. See LayoutFileList::addEmptyClass() and, especially, the
58 // definition of the layoutpost string.
59 // You should also run (or ask someone who has bash to run) the
60 // development/tools/updatelayouts.sh script, to update the format of
61 // all of our layout files.
63 int const LAYOUT_FORMAT = 38; //gb : LangPreamble and BabelPreamble for InsetLayout
67 class LayoutNamesEqual : public unary_function<Layout, bool> {
69 LayoutNamesEqual(docstring const & name)
72 bool operator()(Layout const & c) const
74 return c.name() == name_;
81 bool layout2layout(FileName const & filename, FileName const & tempfile)
83 FileName const script = libFileSearch("scripts", "layout2layout.py");
85 LYXERR0("Could not find layout conversion "
86 "script layout2layout.py.");
90 ostringstream command;
91 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
92 << ' ' << quoteName(filename.toFilesystemEncoding())
93 << ' ' << quoteName(tempfile.toFilesystemEncoding());
94 string const command_str = command.str();
96 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
98 cmd_ret const ret = runCommand(command_str);
100 LYXERR0("Could not run layout conversion script layout2layout.py.");
107 string translateReadType(TextClass::ReadType rt)
110 case TextClass::BASECLASS:
112 case TextClass::MERGE:
114 case TextClass::MODULE:
115 return "module file";
116 case TextClass::VALIDATION:
126 // This string should not be translated here,
127 // because it is a layout identifier.
128 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
131 InsetLayout DocumentClass::plain_insetlayout_;
134 /////////////////////////////////////////////////////////////////////////
138 /////////////////////////////////////////////////////////////////////////
140 TextClass::TextClass()
143 outputFormat_ = "latex";
148 pagestyle_ = "default";
149 defaultfont_ = sane_font;
150 opt_enginetype_ = "authoryear|numerical";
151 opt_fontsize_ = "10|11|12";
152 opt_pagestyle_ = "empty|plain|headings|fancy";
153 cite_full_author_list_ = true;
154 titletype_ = TITLE_COMMAND_AFTER;
155 titlename_ = "maketitle";
157 _("Plain Layout"); // a hack to make this translatable
161 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
163 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
164 if (!lay.read(lexrc, *this)) {
165 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
169 lay.resfont = lay.font;
170 lay.resfont.realize(defaultfont_);
171 lay.reslabelfont = lay.labelfont;
172 lay.reslabelfont.realize(defaultfont_);
173 return true; // no errors
209 TC_ADDTOHTMLPREAMBLE,
225 LexerKeyword textClassTags[] = {
226 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
227 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
228 { "addtopreamble", TC_ADDTOPREAMBLE },
229 { "citeengine", TC_CITEENGINE },
230 { "citeenginetype", TC_CITEENGINETYPE },
231 { "citeformat", TC_CITEFORMAT },
232 { "classoptions", TC_CLASSOPTIONS },
233 { "columns", TC_COLUMNS },
234 { "counter", TC_COUNTER },
235 { "defaultbiblio", TC_DEFAULTBIBLIO },
236 { "defaultfont", TC_DEFAULTFONT },
237 { "defaultmodule", TC_DEFAULTMODULE },
238 { "defaultstyle", TC_DEFAULTSTYLE },
239 { "excludesmodule", TC_EXCLUDESMODULE },
240 { "float", TC_FLOAT },
241 { "format", TC_FORMAT },
242 { "fullauthorlist", TC_FULLAUTHORLIST },
243 { "htmlpreamble", TC_HTMLPREAMBLE },
244 { "htmlstyles", TC_HTMLSTYLES },
245 { "htmltocsection", TC_HTMLTOCSECTION },
246 { "ifcounter", TC_IFCOUNTER },
247 { "ifstyle", TC_IFSTYLE },
248 { "input", TC_INPUT },
249 { "insetlayout", TC_INSETLAYOUT },
250 { "leftmargin", TC_LEFTMARGIN },
251 { "nocounter", TC_NOCOUNTER },
252 { "nofloat", TC_NOFLOAT },
253 { "nostyle", TC_NOSTYLE },
254 { "outputformat", TC_OUTPUTFORMAT },
255 { "outputtype", TC_OUTPUTTYPE },
256 { "pagestyle", TC_PAGESTYLE },
257 { "preamble", TC_PREAMBLE },
258 { "provides", TC_PROVIDES },
259 { "providesmodule", TC_PROVIDESMODULE },
260 { "requires", TC_REQUIRES },
261 { "rightmargin", TC_RIGHTMARGIN },
262 { "secnumdepth", TC_SECNUMDEPTH },
263 { "sides", TC_SIDES },
264 { "style", TC_STYLE },
265 { "titlelatexname", TC_TITLELATEXNAME },
266 { "titlelatextype", TC_TITLELATEXTYPE },
267 { "tocdepth", TC_TOCDEPTH }
273 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
275 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
276 FileName const tempfile = FileName::tempName("convert_layout");
277 bool success = layout2layout(filename, tempfile);
279 success = readWithoutConv(tempfile, rt) == OK;
280 tempfile.removeFile();
285 std::string TextClass::convert(std::string const & str)
287 FileName const fn = FileName::tempName("locallayout");
288 ofstream os(fn.toFilesystemEncoding().c_str());
291 FileName const tempfile = FileName::tempName("convert_locallayout");
292 bool success = layout2layout(fn, tempfile);
295 ifstream is(tempfile.toFilesystemEncoding().c_str());
303 tempfile.removeFile();
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 FileName const tempfile = FileName::tempName("TextClass_read");
369 ofstream os(tempfile.toFilesystemEncoding().c_str());
371 LYXERR0("Unable to create temporary file");
377 // now try to convert it
378 bool const worx = convertLayoutFormat(tempfile, rt);
380 LYXERR0("Unable to convert internal layout information to format "
384 tempfile.removeFile();
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 ifstyle = false;
418 bool ifcounter = false;
420 switch (static_cast<TextClassTags>(le)) {
424 format = lexrc.getInteger();
427 case TC_OUTPUTFORMAT:
429 outputFormat_ = lexrc.getString();
433 readOutputType(lexrc);
434 switch(outputType_) {
436 outputFormat_ = "latex";
439 outputFormat_ = "docbook";
442 outputFormat_ = "literate";
447 case TC_INPUT: // Include file
449 string const inc = lexrc.getString();
450 FileName tmp = libFileSearch("layouts", inc,
454 lexrc.printError("Could not find input file: " + inc);
456 } else if (!read(tmp, MERGE)) {
457 lexrc.printError("Error reading input file: " + tmp.absFileName());
463 case TC_DEFAULTSTYLE:
465 docstring const name = from_utf8(subst(lexrc.getString(),
467 defaultlayout_ = name;
476 lexrc.printError("No name given for style: `$$Token'.");
480 docstring const name = from_utf8(subst(lexrc.getString(),
483 string s = "Could not read name for style: `$$Token' "
484 + lexrc.getString() + " is probably not valid UTF-8!";
487 // Since we couldn't read the name, we just scan the rest
488 // of the style and discard it.
489 error = !readStyle(lexrc, lay);
490 } else if (hasLayout(name)) {
491 Layout & lay = operator[](name);
492 error = !readStyle(lexrc, lay);
493 } else if (!ifstyle) {
495 layout.setName(name);
496 error = !readStyle(lexrc, layout);
498 layoutlist_.push_back(layout);
500 if (defaultlayout_.empty()) {
501 // We do not have a default layout yet, so we choose
502 // the first layout we encounter.
503 defaultlayout_ = name;
507 // this was an ifstyle where we didn't have the style
508 // scan the rest and discard it
510 readStyle(lexrc, lay);
520 docstring const style = from_utf8(subst(lexrc.getString(),
522 if (!deleteLayout(style))
523 lyxerr << "Cannot delete style `"
524 << to_utf8(style) << '\'' << endl;
530 columns_ = lexrc.getInteger();
535 switch (lexrc.getInteger()) {
536 case 1: sides_ = OneSide; break;
537 case 2: sides_ = TwoSides; break;
539 lyxerr << "Impossible number of page"
540 " sides, setting to one."
550 pagestyle_ = rtrim(lexrc.getString());
554 defaultfont_ = lyxRead(lexrc);
555 if (!defaultfont_.resolved()) {
556 lexrc.printError("Warning: defaultfont should "
557 "be fully instantiated!");
558 defaultfont_.realize(sane_font);
564 secnumdepth_ = lexrc.getInteger();
569 tocdepth_ = lexrc.getInteger();
572 // First step to support options
573 case TC_CLASSOPTIONS:
574 readClassOptions(lexrc);
578 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
581 case TC_HTMLPREAMBLE:
582 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
586 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
589 case TC_HTMLTOCSECTION:
590 html_toc_section_ = from_utf8(trim(lexrc.getString()));
593 case TC_ADDTOPREAMBLE:
594 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
597 case TC_ADDTOHTMLPREAMBLE:
598 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
601 case TC_ADDTOHTMLSTYLES:
602 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
607 string const feature = lexrc.getString();
609 if (lexrc.getInteger())
610 provides_.insert(feature);
612 provides_.erase(feature);
618 vector<string> const req
619 = getVectorFromString(lexrc.getString());
620 requires_.insert(req.begin(), req.end());
624 case TC_DEFAULTMODULE: {
626 string const module = lexrc.getString();
627 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
628 default_modules_.push_back(module);
632 case TC_PROVIDESMODULE: {
634 string const module = lexrc.getString();
635 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
636 provided_modules_.push_back(module);
640 case TC_EXCLUDESMODULE: {
642 string const module = lexrc.getString();
643 // modules already have their own way to exclude other modules
645 LYXERR0("ExcludesModule tag cannot be used in a module!");
648 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
649 excluded_modules_.push_back(module);
653 case TC_LEFTMARGIN: // left margin type
655 leftmargin_ = lexrc.getDocString();
658 case TC_RIGHTMARGIN: // right margin type
660 rightmargin_ = lexrc.getDocString();
663 case TC_INSETLAYOUT: {
665 lexrc.printError("No name given for InsetLayout: `$$Token'.");
669 docstring const name = subst(lexrc.getDocString(), '_', ' ');
671 string s = "Could not read name for InsetLayout: `$$Token' "
672 + lexrc.getString() + " is probably not valid UTF-8!";
675 // Since we couldn't read the name, we just scan the rest
676 // of the style and discard it.
677 il.read(lexrc, *this);
678 // Let's try to continue rather than abort.
680 } else if (hasInsetLayout(name)) {
681 InsetLayout & il = insetlayoutlist_[name];
682 error = !il.read(lexrc, *this);
686 error = !il.read(lexrc, *this);
688 insetlayoutlist_[name] = il;
694 error = !readFloat(lexrc);
698 error = !readCiteEngine(lexrc);
701 case TC_CITEENGINETYPE:
703 opt_enginetype_ = rtrim(lexrc.getString());
707 error = !readCiteFormat(lexrc);
710 case TC_DEFAULTBIBLIO:
712 cite_default_biblio_style_ = rtrim(lexrc.getString());
715 case TC_FULLAUTHORLIST:
717 cite_full_author_list_ &= lexrc.getBool();
722 docstring const cnt = lexrc.getDocString();
723 if (!counters_.remove(cnt))
724 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
732 docstring const name = lexrc.getDocString();
734 string s = "Could not read name for counter: `$$Token' "
735 + lexrc.getString() + " is probably not valid UTF-8!";
736 lexrc.printError(s.c_str());
738 // Since we couldn't read the name, we just scan the rest
742 error = !counters_.read(lexrc, name, !ifcounter);
745 lexrc.printError("No name given for style: `$$Token'.");
752 case TC_TITLELATEXTYPE:
753 readTitleType(lexrc);
756 case TC_TITLELATEXNAME:
758 titlename_ = lexrc.getString();
763 string const nofloat = lexrc.getString();
764 floatlist_.erase(nofloat);
769 // Note that this is triggered the first time through the loop unless
770 // we hit a format tag.
771 if (format != LAYOUT_FORMAT)
772 return FORMAT_MISMATCH;
775 // at present, we abort if we encounter an error,
776 // so there is no point continuing.
781 return (error ? ERROR : OK);
783 if (defaultlayout_.empty()) {
784 LYXERR0("Error: Textclass '" << name_
785 << "' is missing a defaultstyle.");
789 // Try to erase "stdinsets" from the provides_ set.
791 // Provides stdinsets 1
792 // declaration simply tells us that the standard insets have been
793 // defined. (It's found in stdinsets.inc but could also be used in
794 // user-defined files.) There isn't really any such package. So we
795 // might as well go ahead and erase it.
796 // If we do not succeed, then it was not there, which means that
797 // the textclass did not provide the definitions of the standard
798 // insets. So we need to try to load them.
799 int erased = provides_.erase("stdinsets");
801 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
804 frontend::Alert::warning(_("Missing File"),
805 _("Could not find stdinsets.inc! This may lead to data loss!"));
807 } else if (!read(tmp, MERGE)) {
808 frontend::Alert::warning(_("Corrupt File"),
809 _("Could not read stdinsets.inc! This may lead to data loss!"));
814 min_toclevel_ = Layout::NOT_IN_TOC;
815 max_toclevel_ = Layout::NOT_IN_TOC;
816 const_iterator lit = begin();
817 const_iterator len = end();
818 for (; lit != len; ++lit) {
819 int const toclevel = lit->toclevel;
820 if (toclevel != Layout::NOT_IN_TOC) {
821 if (min_toclevel_ == Layout::NOT_IN_TOC)
822 min_toclevel_ = toclevel;
824 min_toclevel_ = min(min_toclevel_, toclevel);
825 max_toclevel_ = max(max_toclevel_, toclevel);
828 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
829 << ", maximum is " << max_toclevel_);
831 return (error ? ERROR : OK);
835 void TextClass::readTitleType(Lexer & lexrc)
837 LexerKeyword titleTypeTags[] = {
838 { "commandafter", TITLE_COMMAND_AFTER },
839 { "environment", TITLE_ENVIRONMENT }
842 PushPopHelper pph(lexrc, titleTypeTags);
844 int le = lexrc.lex();
846 case Lexer::LEX_UNDEF:
847 lexrc.printError("Unknown output type `$$Token'");
849 case TITLE_COMMAND_AFTER:
850 case TITLE_ENVIRONMENT:
851 titletype_ = static_cast<TitleLatexType>(le);
854 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
860 void TextClass::readOutputType(Lexer & lexrc)
862 LexerKeyword outputTypeTags[] = {
863 { "docbook", DOCBOOK },
865 { "literate", LITERATE }
868 PushPopHelper pph(lexrc, outputTypeTags);
870 int le = lexrc.lex();
872 case Lexer::LEX_UNDEF:
873 lexrc.printError("Unknown output type `$$Token'");
878 outputType_ = static_cast<OutputType>(le);
881 LYXERR0("Unhandled value " << le);
887 void TextClass::readClassOptions(Lexer & lexrc)
897 LexerKeyword classOptionsTags[] = {
899 {"fontsize", CO_FONTSIZE },
900 {"header", CO_HEADER },
901 {"other", CO_OTHER },
902 {"pagestyle", CO_PAGESTYLE }
905 lexrc.pushTable(classOptionsTags);
907 while (!getout && lexrc.isOK()) {
908 int le = lexrc.lex();
910 case Lexer::LEX_UNDEF:
911 lexrc.printError("Unknown ClassOption tag `$$Token'");
919 opt_fontsize_ = rtrim(lexrc.getString());
923 opt_pagestyle_ = rtrim(lexrc.getString());
927 if (options_.empty())
928 options_ = lexrc.getString();
930 options_ += ',' + lexrc.getString();
934 class_header_ = subst(lexrc.getString(), """, "\"");
945 bool TextClass::readCiteEngine(Lexer & lexrc)
947 int const type = readCiteEngineType(lexrc);
948 if (type & ENGINE_TYPE_AUTHORYEAR)
949 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
950 if (type & ENGINE_TYPE_NUMERICAL)
951 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
954 while (!getout && lexrc.isOK()) {
956 def = lexrc.getString();
957 def = subst(def, " ", "");
958 def = subst(def, "\t", "");
959 if (compare_ascii_no_case(def, "end") == 0) {
969 cs.forceUpperCase = true;
973 size_t const n = def.size();
974 for (size_t i = 0; i != n; ++i) {
977 cs.fullAuthorList = true;
978 else if (ichar == '[' && cs.textAfter)
979 cs.textBefore = true;
980 else if (ichar == '[')
982 else if (ichar != ']')
987 if (type & ENGINE_TYPE_AUTHORYEAR)
988 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
989 if (type & ENGINE_TYPE_NUMERICAL)
990 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
996 int TextClass::readCiteEngineType(Lexer & lexrc) const
998 int const ENGINE_TYPE_DEFAULT =
999 ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL;
1000 if (!lexrc.next()) {
1001 lexrc.printError("No cite engine type given for token: `$$Token'.");
1002 return ENGINE_TYPE_DEFAULT;
1004 string const type = rtrim(lexrc.getString());
1005 if (compare_ascii_no_case(type, "authoryear") == 0)
1006 return ENGINE_TYPE_AUTHORYEAR;
1007 else if (compare_ascii_no_case(type, "numerical") == 0)
1008 return ENGINE_TYPE_NUMERICAL;
1009 else if (compare_ascii_no_case(type, "default") != 0) {
1010 string const s = "Unknown cite engine type `" + type
1011 + "' given for token: `$$Token',";
1012 lexrc.printError(s);
1014 return ENGINE_TYPE_DEFAULT;
1018 bool TextClass::readCiteFormat(Lexer & lexrc)
1020 int const type = readCiteEngineType(lexrc);
1023 while (lexrc.isOK()) {
1025 etype = lexrc.getString();
1026 if (compare_ascii_no_case(etype, "end") == 0)
1031 definition = lexrc.getString();
1032 char initchar = etype[0];
1033 if (initchar == '#')
1035 if (initchar == '!' || initchar == '_') {
1036 if (type & ENGINE_TYPE_AUTHORYEAR)
1037 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1038 if (type & ENGINE_TYPE_NUMERICAL)
1039 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1041 if (type & ENGINE_TYPE_AUTHORYEAR)
1042 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1043 if (type & ENGINE_TYPE_NUMERICAL)
1044 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1051 bool TextClass::readFloat(Lexer & lexrc)
1071 LexerKeyword floatTags[] = {
1073 { "extension", FT_EXT },
1074 { "guiname", FT_NAME },
1075 { "htmlattr", FT_HTMLATTR },
1076 { "htmlstyle", FT_HTMLSTYLE },
1077 { "htmltag", FT_HTMLTAG },
1078 { "ispredefined", FT_PREDEFINED },
1079 { "listcommand", FT_LISTCOMMAND },
1080 { "listname", FT_LISTNAME },
1081 { "numberwithin", FT_WITHIN },
1082 { "placement", FT_PLACEMENT },
1083 { "refprefix", FT_REFPREFIX },
1084 { "style", FT_STYLE },
1085 { "type", FT_TYPE },
1086 { "usesfloatpkg", FT_USESFLOAT }
1089 lexrc.pushTable(floatTags);
1103 bool usesfloat = true;
1104 bool ispredefined = false;
1106 bool getout = false;
1107 while (!getout && lexrc.isOK()) {
1108 int le = lexrc.lex();
1110 case Lexer::LEX_UNDEF:
1111 lexrc.printError("Unknown float tag `$$Token'");
1119 type = lexrc.getString();
1120 if (floatlist_.typeExist(type)) {
1121 Floating const & fl = floatlist_.getType(type);
1122 placement = fl.placement();
1124 within = fl.within();
1127 listname = fl.listName();
1128 usesfloat = fl.usesFloatPkg();
1129 ispredefined = fl.isPredefined();
1130 listcommand = fl.listCommand();
1131 refprefix = fl.refPrefix();
1136 name = lexrc.getString();
1140 placement = lexrc.getString();
1144 ext = lexrc.getString();
1148 within = lexrc.getString();
1149 if (within == "none")
1154 style = lexrc.getString();
1156 case FT_LISTCOMMAND:
1158 listcommand = lexrc.getString();
1162 refprefix = lexrc.getString();
1166 listname = lexrc.getString();
1170 usesfloat = lexrc.getBool();
1174 ispredefined = lexrc.getBool();
1178 htmlattr = lexrc.getString();
1182 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1186 htmltag = lexrc.getString();
1196 // Here we have a full float if getout == true
1198 if (!usesfloat && listcommand.empty()) {
1199 // if this float uses the same auxfile as an existing one,
1200 // there is no need for it to provide a list command.
1201 FloatList::const_iterator it = floatlist_.begin();
1202 FloatList::const_iterator en = floatlist_.end();
1203 bool found_ext = false;
1204 for (; it != en; ++it) {
1205 if (it->second.ext() == ext) {
1211 LYXERR0("The layout does not provide a list command " <<
1212 "for the float `" << type << "'. LyX will " <<
1213 "not be able to produce a float list.");
1215 Floating fl(type, placement, ext, within, style, name,
1216 listname, listcommand, refprefix,
1217 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1218 floatlist_.newFloat(fl);
1219 // each float has its own counter
1220 counters_.newCounter(from_ascii(type), from_ascii(within),
1221 docstring(), docstring());
1222 // also define sub-float counters
1223 docstring const subtype = "sub-" + from_ascii(type);
1224 counters_.newCounter(subtype, from_ascii(type),
1225 "\\alph{" + subtype + "}", docstring());
1231 string const & TextClass::prerequisites() const
1233 if (contains(prerequisites_, ',')) {
1234 vector<string> const pres = getVectorFromString(prerequisites_);
1235 prerequisites_ = getStringFromVector(pres, "\n\t");
1237 return prerequisites_;
1240 bool TextClass::hasLayout(docstring const & n) const
1242 docstring const name = n.empty() ? defaultLayoutName() : n;
1244 return find_if(layoutlist_.begin(), layoutlist_.end(),
1245 LayoutNamesEqual(name))
1246 != layoutlist_.end();
1250 bool TextClass::hasInsetLayout(docstring const & n) const
1254 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1255 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1256 for (; it != en; ++it)
1263 Layout const & TextClass::operator[](docstring const & name) const
1265 LASSERT(!name.empty(), /**/);
1268 find_if(begin(), end(), LayoutNamesEqual(name));
1271 lyxerr << "We failed to find the layout '" << to_utf8(name)
1272 << "' in the layout list. You MUST investigate!"
1274 for (const_iterator cit = begin(); cit != end(); ++cit)
1275 lyxerr << " " << to_utf8(cit->name()) << endl;
1277 // we require the name to exist
1278 LASSERT(false, /**/);
1285 Layout & TextClass::operator[](docstring const & name)
1287 LASSERT(!name.empty(), /**/);
1289 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1292 LYXERR0("We failed to find the layout '" << to_utf8(name)
1293 << "' in the layout list. You MUST investigate!");
1294 for (const_iterator cit = begin(); cit != end(); ++cit)
1295 LYXERR0(" " << to_utf8(cit->name()));
1297 // we require the name to exist
1298 LASSERT(false, /**/);
1305 bool TextClass::deleteLayout(docstring const & name)
1307 if (name == defaultLayoutName() || name == plainLayoutName())
1310 LayoutList::iterator it =
1311 remove_if(layoutlist_.begin(), layoutlist_.end(),
1312 LayoutNamesEqual(name));
1314 LayoutList::iterator end = layoutlist_.end();
1315 bool const ret = (it != end);
1316 layoutlist_.erase(it, end);
1321 // Load textclass info if not loaded yet
1322 bool TextClass::load(string const & path) const
1327 // Read style-file, provided path is searched before the system ones
1328 // If path is a file, it is loaded directly.
1329 FileName layout_file(path);
1330 if (!path.empty() && !layout_file.isReadableFile())
1331 layout_file = FileName(addName(path, name_ + ".layout"));
1332 if (layout_file.empty() || !layout_file.exists())
1333 layout_file = libFileSearch("layouts", name_, "layout");
1334 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1337 lyxerr << "Error reading `"
1338 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1339 << "'\n(Check `" << name_
1340 << "')\nCheck your installation and "
1341 "try Options/Reconfigure..."
1349 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1354 layoutlist_.push_back(createBasicLayout(n, true));
1359 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1361 // FIXME The fix for the InsetLayout part of 4812 would be here:
1362 // Add the InsetLayout to the document class if it is not found.
1364 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1365 while (!n.empty()) {
1366 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1367 if (cit != cen && cit->first == n)
1369 size_t i = n.find(':');
1370 if (i == string::npos)
1374 return plain_insetlayout_;
1378 docstring const & TextClass::defaultLayoutName() const
1380 return defaultlayout_;
1384 Layout const & TextClass::defaultLayout() const
1386 return operator[](defaultLayoutName());
1390 bool TextClass::isDefaultLayout(Layout const & layout) const
1392 return layout.name() == defaultLayoutName();
1396 bool TextClass::isPlainLayout(Layout const & layout) const
1398 return layout.name() == plainLayoutName();
1402 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1404 static Layout * defaultLayout = NULL;
1406 if (defaultLayout) {
1407 defaultLayout->setUnknown(unknown);
1408 defaultLayout->setName(name);
1409 return *defaultLayout;
1412 static char const * s = "Margin Static\n"
1413 "LatexType Paragraph\n"
1416 "AlignPossible Left, Right, Center\n"
1417 "LabelType No_Label\n"
1419 istringstream ss(s);
1420 Lexer lex(textClassTags);
1422 defaultLayout = new Layout;
1423 defaultLayout->setUnknown(unknown);
1424 defaultLayout->setName(name);
1425 if (!readStyle(lex, *defaultLayout)) {
1426 // The only way this happens is because the hardcoded layout above
1428 LASSERT(false, /**/);
1430 return *defaultLayout;
1434 /////////////////////////////////////////////////////////////////////////
1436 // DocumentClassBundle
1438 /////////////////////////////////////////////////////////////////////////
1440 DocumentClassBundle::~DocumentClassBundle()
1442 for (size_t i = 0; i != documentClasses_.size(); ++i)
1443 delete documentClasses_[i];
1444 documentClasses_.clear();
1447 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1449 DocumentClass * dc = new DocumentClass(baseClass);
1450 documentClasses_.push_back(dc);
1451 return *documentClasses_.back();
1455 DocumentClassBundle & DocumentClassBundle::get()
1457 static DocumentClassBundle singleton;
1462 DocumentClass & DocumentClassBundle::makeDocumentClass(
1463 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1465 DocumentClass & doc_class = newClass(baseClass);
1466 LayoutModuleList::const_iterator it = modlist.begin();
1467 LayoutModuleList::const_iterator en = modlist.end();
1468 for (; it != en; it++) {
1469 string const modName = *it;
1470 LyXModule * lm = theModuleList[modName];
1472 docstring const msg =
1473 bformat(_("The module %1$s has been requested by\n"
1474 "this document but has not been found in the list of\n"
1475 "available modules. If you recently installed it, you\n"
1476 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1477 frontend::Alert::warning(_("Module not available"), msg);
1480 if (!lm->isAvailable()) {
1481 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1482 docstring const msg =
1483 bformat(_("The module %1$s requires a package that is not\n"
1484 "available in your LaTeX installation, or a converter that\n"
1485 "you have not installed. LaTeX output may not be possible.\n"
1486 "Missing prerequisites:\n"
1488 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1489 from_utf8(modName), prereqs);
1490 frontend::Alert::warning(_("Package not available"), msg, true);
1492 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1493 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1494 docstring const msg =
1495 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1496 frontend::Alert::warning(_("Read Error"), msg);
1503 /////////////////////////////////////////////////////////////////////////
1507 /////////////////////////////////////////////////////////////////////////
1509 DocumentClass::DocumentClass(LayoutFile const & tc)
1514 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1516 LayoutList::const_iterator it = layoutlist_.begin();
1517 LayoutList::const_iterator end = layoutlist_.end();
1518 for (; it != end; ++it)
1519 if (it->latexname() == lay)
1525 bool DocumentClass::provides(string const & p) const
1527 return provides_.find(p) != provides_.end();
1531 bool DocumentClass::hasTocLevels() const
1533 return min_toclevel_ != Layout::NOT_IN_TOC;
1537 Layout const & DocumentClass::htmlTOCLayout() const
1539 if (html_toc_section_.empty()) {
1540 // we're going to look for the layout with the minimum toclevel
1541 TextClass::LayoutList::const_iterator lit = begin();
1542 TextClass::LayoutList::const_iterator const len = end();
1543 int minlevel = 1000;
1544 Layout const * lay = NULL;
1545 for (; lit != len; ++lit) {
1546 int const level = lit->toclevel;
1547 // we don't want Part
1548 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1554 html_toc_section_ = lay->name();
1556 // hmm. that is very odd, so we'll do our best
1557 html_toc_section_ = defaultLayoutName();
1559 return operator[](html_toc_section_);
1563 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1564 string const & entry, string const & fallback) const
1566 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1568 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1569 if (itype == cite_formats_.end())
1570 return default_format;
1571 map<string, string>::const_iterator it = itype->second.find(entry);
1572 if (it == itype->second.end() && !fallback.empty())
1573 it = itype->second.find(fallback);
1574 if (it == itype->second.end())
1575 return default_format;
1580 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1581 string const & macro) const
1583 static string empty;
1584 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1585 if (itype == cite_macros_.end())
1587 map<string, string>::const_iterator it = itype->second.find(macro);
1588 if (it == itype->second.end())
1594 vector<string> const DocumentClass::citeCommands(
1595 CiteEngineType const & type) const
1597 vector<CitationStyle> const styles = citeStyles(type);
1598 vector<CitationStyle>::const_iterator it = styles.begin();
1599 vector<CitationStyle>::const_iterator end = styles.end();
1600 vector<string> cmds;
1601 for (; it != end; ++it) {
1602 CitationStyle const cite = *it;
1603 cmds.push_back(cite.cmd);
1609 vector<CitationStyle> const & DocumentClass::citeStyles(
1610 CiteEngineType const & type) const
1612 static vector<CitationStyle> empty;
1613 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1614 if (it == cite_styles_.end())
1620 /////////////////////////////////////////////////////////////////////////
1624 /////////////////////////////////////////////////////////////////////////
1626 ostream & operator<<(ostream & os, PageSides p)