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"
49 using namespace lyx::support;
55 class LayoutNamesEqual : public unary_function<Layout, bool> {
57 LayoutNamesEqual(docstring const & name)
60 bool operator()(Layout const & c) const
62 return c.name() == name_;
68 // Keep the changes documented in the Customization manual.
69 int const FORMAT = 26;
72 bool layout2layout(FileName const & filename, FileName const & tempfile)
74 FileName const script = libFileSearch("scripts", "layout2layout.py");
76 LYXERR0("Could not find layout conversion "
77 "script layout2layout.py.");
81 ostringstream command;
82 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
83 << ' ' << quoteName(filename.toFilesystemEncoding())
84 << ' ' << quoteName(tempfile.toFilesystemEncoding());
85 string const command_str = command.str();
87 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
89 cmd_ret const ret = runCommand(command_str);
91 LYXERR0("Could not run layout conversion script layout2layout.py.");
98 string translateRT(TextClass::ReadType rt)
101 case TextClass::BASECLASS:
103 case TextClass::MERGE:
105 case TextClass::MODULE:
106 return "module file";
107 case TextClass::VALIDATION:
117 // This string should not be translated here,
118 // because it is a layout identifier.
119 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
122 InsetLayout DocumentClass::plain_insetlayout_;
125 /////////////////////////////////////////////////////////////////////////
129 /////////////////////////////////////////////////////////////////////////
131 TextClass::TextClass()
134 outputFormat_ = "latex";
139 pagestyle_ = "default";
140 defaultfont_ = sane_font;
141 opt_fontsize_ = "10|11|12";
142 opt_pagestyle_ = "empty|plain|headings|fancy";
143 titletype_ = TITLE_COMMAND_AFTER;
144 titlename_ = "maketitle";
146 _("Plain Layout"); // a hack to make this translatable
150 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
152 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
153 if (!lay.read(lexrc, *this)) {
154 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
158 lay.resfont = lay.font;
159 lay.resfont.realize(defaultfont_);
160 lay.reslabelfont = lay.labelfont;
161 lay.reslabelfont.realize(defaultfont_);
162 return true; // no errors
196 TC_ADDTOHTMLPREAMBLE,
207 LexerKeyword textClassTags[] = {
208 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
209 { "addtopreamble", TC_ADDTOPREAMBLE },
210 { "citeformat", TC_CITEFORMAT },
211 { "classoptions", TC_CLASSOPTIONS },
212 { "columns", TC_COLUMNS },
213 { "counter", TC_COUNTER },
214 { "defaultfont", TC_DEFAULTFONT },
215 { "defaultmodule", TC_DEFAULTMODULE },
216 { "defaultstyle", TC_DEFAULTSTYLE },
217 { "excludesmodule", TC_EXCLUDESMODULE },
218 { "float", TC_FLOAT },
219 { "format", TC_FORMAT },
220 { "htmlpreamble", TC_HTMLPREAMBLE },
221 { "htmltocsection", TC_HTMLTOCSECTION },
222 { "ifcounter", TC_IFCOUNTER },
223 { "ifstyle", TC_IFSTYLE },
224 { "input", TC_INPUT },
225 { "insetlayout", TC_INSETLAYOUT },
226 { "leftmargin", TC_LEFTMARGIN },
227 { "nofloat", TC_NOFLOAT },
228 { "nostyle", TC_NOSTYLE },
229 { "outputformat", TC_OUTPUTFORMAT },
230 { "outputtype", TC_OUTPUTTYPE },
231 { "pagestyle", TC_PAGESTYLE },
232 { "preamble", TC_PREAMBLE },
233 { "provides", TC_PROVIDES },
234 { "providesmodule", TC_PROVIDESMODULE },
235 { "requires", TC_REQUIRES },
236 { "rightmargin", TC_RIGHTMARGIN },
237 { "secnumdepth", TC_SECNUMDEPTH },
238 { "sides", TC_SIDES },
239 { "style", TC_STYLE },
240 { "titlelatexname", TC_TITLELATEXNAME },
241 { "titlelatextype", TC_TITLELATEXTYPE },
242 { "tocdepth", TC_TOCDEPTH }
248 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
250 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
251 FileName const tempfile = FileName::tempName("convert_layout");
252 bool success = layout2layout(filename, tempfile);
254 success = readWithoutConv(tempfile, rt) == OK;
255 tempfile.removeFile();
260 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
262 if (!filename.isReadableFile()) {
263 lyxerr << "Cannot read layout file `" << filename << "'."
268 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
269 to_utf8(makeDisplayPath(filename.absFilename())));
271 // Define the plain layout used in table cells, ert, etc. Note that
272 // we do this before loading any layout file, so that classes can
273 // override features of this layout if they should choose to do so.
274 if (rt == BASECLASS && !hasLayout(plain_layout_))
275 layoutlist_.push_back(createBasicLayout(plain_layout_));
277 Lexer lexrc(textClassTags);
278 lexrc.setFile(filename);
279 ReturnValues retval = read(lexrc, rt);
281 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
282 to_utf8(makeDisplayPath(filename.absFilename())));
288 bool TextClass::read(FileName const & filename, ReadType rt)
290 ReturnValues const retval = readWithoutConv(filename, rt);
291 if (retval != FORMAT_MISMATCH)
294 bool const worx = convertLayoutFormat(filename, rt);
296 LYXERR0 ("Unable to convert " << filename <<
297 " to format " << FORMAT);
304 bool TextClass::validate(std::string const & str)
307 return tc.read(str, VALIDATION);
311 bool TextClass::read(std::string const & str, ReadType rt)
313 Lexer lexrc(textClassTags);
314 istringstream is(str);
316 ReturnValues retval = read(lexrc, rt);
318 if (retval != FORMAT_MISMATCH)
321 // write the layout string to a temporary file
322 FileName const tempfile = FileName::tempName("TextClass_read");
323 ofstream os(tempfile.toFilesystemEncoding().c_str());
325 LYXERR0("Unable to create temporary file");
331 // now try to convert it
332 bool const worx = convertLayoutFormat(tempfile, rt);
334 LYXERR0("Unable to convert internal layout information to format "
337 tempfile.removeFile();
342 // Reads a textclass structure from file.
343 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
345 bool error = !lexrc.isOK();
347 // Format of files before the 'Format' tag was introduced
351 while (lexrc.isOK() && !error) {
352 int le = lexrc.lex();
355 case Lexer::LEX_FEOF:
358 case Lexer::LEX_UNDEF:
359 lexrc.printError("Unknown TextClass tag `$$Token'");
367 // used below to track whether we are in an IfStyle or IfCounter tag.
368 bool ifstyle = false;
369 bool ifcounter = false;
371 switch (static_cast<TextClassTags>(le)) {
375 format = lexrc.getInteger();
378 case TC_OUTPUTFORMAT:
380 outputFormat_ = lexrc.getString();
384 readOutputType(lexrc);
385 switch(outputType_) {
387 outputFormat_ = "latex";
390 outputFormat_ = "docbook";
393 outputFormat_ = "literate";
398 case TC_INPUT: // Include file
400 string const inc = lexrc.getString();
401 FileName tmp = libFileSearch("layouts", inc,
405 lexrc.printError("Could not find input file: " + inc);
407 } else if (!read(tmp, MERGE)) {
408 lexrc.printError("Error reading input"
409 "file: " + tmp.absFilename());
415 case TC_DEFAULTSTYLE:
417 docstring const name = from_utf8(subst(lexrc.getString(),
419 defaultlayout_ = name;
428 lexrc.printError("No name given for style: `$$Token'.");
432 docstring const name = from_utf8(subst(lexrc.getString(),
435 string s = "Could not read name for style: `$$Token' "
436 + lexrc.getString() + " is probably not valid UTF-8!";
437 lexrc.printError(s.c_str());
439 // Since we couldn't read the name, we just scan the rest
440 // of the style and discard it.
441 error = !readStyle(lexrc, lay);
442 } else if (hasLayout(name)) {
443 Layout & lay = operator[](name);
444 error = !readStyle(lexrc, lay);
445 } else if (!ifstyle) {
447 layout.setName(name);
448 error = !readStyle(lexrc, layout);
450 layoutlist_.push_back(layout);
452 if (defaultlayout_.empty()) {
453 // We do not have a default layout yet, so we choose
454 // the first layout we encounter.
455 defaultlayout_ = name;
459 // scan the rest and discard it
461 readStyle(lexrc, lay);
472 docstring const style = from_utf8(subst(lexrc.getString(),
474 if (!deleteLayout(style))
475 lyxerr << "Cannot delete style `"
476 << to_utf8(style) << '\'' << endl;
482 columns_ = lexrc.getInteger();
487 switch (lexrc.getInteger()) {
488 case 1: sides_ = OneSide; break;
489 case 2: sides_ = TwoSides; break;
491 lyxerr << "Impossible number of page"
492 " sides, setting to one."
502 pagestyle_ = rtrim(lexrc.getString());
506 defaultfont_ = lyxRead(lexrc);
507 if (!defaultfont_.resolved()) {
508 lexrc.printError("Warning: defaultfont should "
509 "be fully instantiated!");
510 defaultfont_.realize(sane_font);
516 secnumdepth_ = lexrc.getInteger();
521 tocdepth_ = lexrc.getInteger();
524 // First step to support options
525 case TC_CLASSOPTIONS:
526 readClassOptions(lexrc);
530 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
533 case TC_HTMLPREAMBLE:
534 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
537 case TC_HTMLTOCSECTION:
538 html_toc_section_ = from_utf8(trim(lexrc.getString()));
541 case TC_ADDTOPREAMBLE:
542 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
545 case TC_ADDTOHTMLPREAMBLE:
546 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
551 string const feature = lexrc.getString();
553 if (lexrc.getInteger())
554 provides_.insert(feature);
556 provides_.erase(feature);
562 vector<string> const req
563 = getVectorFromString(lexrc.getString());
564 requires_.insert(req.begin(), req.end());
568 case TC_DEFAULTMODULE: {
570 string const module = lexrc.getString();
571 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
572 default_modules_.push_back(module);
576 case TC_PROVIDESMODULE: {
578 string const module = lexrc.getString();
579 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
580 provided_modules_.push_back(module);
584 case TC_EXCLUDESMODULE: {
586 string const module = lexrc.getString();
587 // modules already have their own way to exclude other modules
589 LYXERR0("ExcludesModule tag cannot be used in a module!");
592 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
593 excluded_modules_.push_back(module);
597 case TC_LEFTMARGIN: // left margin type
599 leftmargin_ = lexrc.getDocString();
602 case TC_RIGHTMARGIN: // right margin type
604 rightmargin_ = lexrc.getDocString();
607 case TC_INSETLAYOUT: {
609 lexrc.printError("No name given for InsetLayout: `$$Token'.");
613 docstring const name = subst(lexrc.getDocString(), '_', ' ');
615 string s = "Could not read name for InsetLayout: `$$Token' "
616 + lexrc.getString() + " is probably not valid UTF-8!";
617 lexrc.printError(s.c_str());
619 // Since we couldn't read the name, we just scan the rest
620 // of the style and discard it.
621 il.read(lexrc, *this);
623 } else if (hasInsetLayout(name)) {
624 InsetLayout & il = insetlayoutlist_[name];
625 error = !il.read(lexrc, *this);
629 error = !il.read(lexrc, *this);
631 insetlayoutlist_[name] = il;
641 readCiteFormat(lexrc);
648 docstring const name = lexrc.getDocString();
650 string s = "Could not read name for counter: `$$Token' "
651 + lexrc.getString() + " is probably not valid UTF-8!";
652 lexrc.printError(s.c_str());
654 // Since we couldn't read the name, we just scan the rest
658 error = !counters_.read(lexrc, name, !ifcounter);
661 lexrc.printError("No name given for style: `$$Token'.");
668 case TC_TITLELATEXTYPE:
669 readTitleType(lexrc);
672 case TC_TITLELATEXNAME:
674 titlename_ = lexrc.getString();
679 string const nofloat = lexrc.getString();
680 floatlist_.erase(nofloat);
685 //Note that this is triggered the first time through the loop unless
686 //we hit a format tag.
687 if (format != FORMAT)
691 if (format != FORMAT)
692 return FORMAT_MISMATCH;
695 return (error ? ERROR : OK);
697 if (defaultlayout_.empty()) {
698 LYXERR0("Error: Textclass '" << name_
699 << "' is missing a defaultstyle.");
703 // Try to erase "stdinsets" from the provides_ set.
705 // Provides stdinsets 1
706 // declaration simply tells us that the standard insets have been
707 // defined. (It's found in stdinsets.inc but could also be used in
708 // user-defined files.) There isn't really any such package. So we
709 // might as well go ahead and erase it.
710 // If we do not succeed, then it was not there, which means that
711 // the textclass did not provide the definitions of the standard
712 // insets. So we need to try to load them.
713 int erased = provides_.erase("stdinsets");
715 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
718 throw ExceptionMessage(WarningException, _("Missing File"),
719 _("Could not find stdinsets.inc! This may lead to data loss!"));
721 } else if (!read(tmp, MERGE)) {
722 throw ExceptionMessage(WarningException, _("Corrupt File"),
723 _("Could not read stdinsets.inc! This may lead to data loss!"));
728 min_toclevel_ = Layout::NOT_IN_TOC;
729 max_toclevel_ = Layout::NOT_IN_TOC;
730 const_iterator lit = begin();
731 const_iterator len = end();
732 for (; lit != len; ++lit) {
733 int const toclevel = lit->toclevel;
734 if (toclevel != Layout::NOT_IN_TOC) {
735 if (min_toclevel_ == Layout::NOT_IN_TOC)
736 min_toclevel_ = toclevel;
738 min_toclevel_ = min(min_toclevel_, toclevel);
739 max_toclevel_ = max(max_toclevel_, toclevel);
742 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
743 << ", maximum is " << max_toclevel_);
745 return (error ? ERROR : OK);
749 void TextClass::readTitleType(Lexer & lexrc)
751 LexerKeyword titleTypeTags[] = {
752 { "commandafter", TITLE_COMMAND_AFTER },
753 { "environment", TITLE_ENVIRONMENT }
756 PushPopHelper pph(lexrc, titleTypeTags);
758 int le = lexrc.lex();
760 case Lexer::LEX_UNDEF:
761 lexrc.printError("Unknown output type `$$Token'");
763 case TITLE_COMMAND_AFTER:
764 case TITLE_ENVIRONMENT:
765 titletype_ = static_cast<TitleLatexType>(le);
768 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
774 void TextClass::readOutputType(Lexer & lexrc)
776 LexerKeyword outputTypeTags[] = {
777 { "docbook", DOCBOOK },
779 { "literate", LITERATE }
782 PushPopHelper pph(lexrc, outputTypeTags);
784 int le = lexrc.lex();
786 case Lexer::LEX_UNDEF:
787 lexrc.printError("Unknown output type `$$Token'");
792 outputType_ = static_cast<OutputType>(le);
795 LYXERR0("Unhandled value " << le);
801 void TextClass::readClassOptions(Lexer & lexrc)
811 LexerKeyword classOptionsTags[] = {
813 {"fontsize", CO_FONTSIZE },
814 {"header", CO_HEADER },
815 {"other", CO_OTHER },
816 {"pagestyle", CO_PAGESTYLE }
819 lexrc.pushTable(classOptionsTags);
821 while (!getout && lexrc.isOK()) {
822 int le = lexrc.lex();
824 case Lexer::LEX_UNDEF:
825 lexrc.printError("Unknown ClassOption tag `$$Token'");
832 opt_fontsize_ = rtrim(lexrc.getString());
836 opt_pagestyle_ = rtrim(lexrc.getString());
840 options_ = lexrc.getString();
844 class_header_ = subst(lexrc.getString(), """, "\"");
855 void TextClass::readCiteFormat(Lexer & lexrc)
859 while (lexrc.isOK()) {
861 etype = lexrc.getString();
862 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
865 definition = lexrc.getString();
866 char initchar = etype[0];
869 if (initchar == '!' || initchar == '_')
870 cite_macros_[etype] = definition;
872 cite_formats_[etype] = definition;
877 void TextClass::readFloat(Lexer & lexrc)
896 LexerKeyword floatTags[] = {
898 { "extension", FT_EXT },
899 { "guiname", FT_NAME },
900 { "htmlattr", FT_HTMLATTR },
901 { "htmlstyle", FT_HTMLSTYLE },
902 { "htmltag", FT_HTMLTAG },
903 { "listcommand", FT_LISTCOMMAND },
904 { "listname", FT_LISTNAME },
905 { "needsfloatpkg", FT_NEEDSFLOAT },
906 { "numberwithin", FT_WITHIN },
907 { "placement", FT_PLACEMENT },
908 { "refprefix", FT_REFPREFIX },
909 { "style", FT_STYLE },
913 lexrc.pushTable(floatTags);
927 bool needsfloat = true;
930 while (!getout && lexrc.isOK()) {
931 int le = lexrc.lex();
933 case Lexer::LEX_UNDEF:
934 lexrc.printError("Unknown float tag `$$Token'");
941 type = lexrc.getString();
942 if (floatlist_.typeExist(type)) {
943 Floating const & fl = floatlist_.getType(type);
944 placement = fl.placement();
946 within = fl.within();
949 listname = fl.listName();
950 needsfloat = fl.needsFloatPkg();
951 listcommand = fl.listCommand();
952 refprefix = fl.refPrefix();
957 name = lexrc.getString();
961 placement = lexrc.getString();
965 ext = lexrc.getString();
969 within = lexrc.getString();
970 if (within == "none")
975 style = lexrc.getString();
979 listcommand = lexrc.getString();
983 refprefix = lexrc.getString();
987 listname = lexrc.getString();
991 needsfloat = lexrc.getBool();
995 htmlattr = lexrc.getString();
999 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1003 htmltag = lexrc.getString();
1011 // Here we have a full float if getout == true
1013 if (!needsfloat && listcommand.empty())
1014 LYXERR0("The layout does not provide a list command " <<
1015 "for the builtin float `" << type << "'. LyX will " <<
1016 "not be able to produce a float list.");
1017 Floating fl(type, placement, ext, within, style, name,
1018 listname, listcommand, refprefix,
1019 htmltag, htmlattr, htmlstyle, needsfloat);
1020 floatlist_.newFloat(fl);
1021 // each float has its own counter
1022 counters_.newCounter(from_ascii(type), from_ascii(within),
1023 docstring(), docstring());
1024 // also define sub-float counters
1025 docstring const subtype = "sub-" + from_ascii(type);
1026 counters_.newCounter(subtype, from_ascii(type),
1027 "\\alph{" + subtype + "}", docstring());
1034 string const & TextClass::prerequisites() const
1036 if (contains(prerequisites_, ',')) {
1037 vector<string> const pres = getVectorFromString(prerequisites_);
1038 prerequisites_ = getStringFromVector(pres, "\n\t");
1040 return prerequisites_;
1043 bool TextClass::hasLayout(docstring const & n) const
1045 docstring const name = n.empty() ? defaultLayoutName() : n;
1047 return find_if(layoutlist_.begin(), layoutlist_.end(),
1048 LayoutNamesEqual(name))
1049 != layoutlist_.end();
1053 bool TextClass::hasInsetLayout(docstring const & n) const
1057 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1058 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1059 for (; it != en; ++it)
1066 Layout const & TextClass::operator[](docstring const & name) const
1068 LASSERT(!name.empty(), /**/);
1071 find_if(begin(), end(), LayoutNamesEqual(name));
1074 lyxerr << "We failed to find the layout '" << to_utf8(name)
1075 << "' in the layout list. You MUST investigate!"
1077 for (const_iterator cit = begin(); cit != end(); ++cit)
1078 lyxerr << " " << to_utf8(cit->name()) << endl;
1080 // we require the name to exist
1081 LASSERT(false, /**/);
1088 Layout & TextClass::operator[](docstring const & name)
1090 LASSERT(!name.empty(), /**/);
1092 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1095 LYXERR0("We failed to find the layout '" << to_utf8(name)
1096 << "' in the layout list. You MUST investigate!");
1097 for (const_iterator cit = begin(); cit != end(); ++cit)
1098 LYXERR0(" " << to_utf8(cit->name()));
1100 // we require the name to exist
1101 LASSERT(false, /**/);
1108 bool TextClass::deleteLayout(docstring const & name)
1110 if (name == defaultLayoutName() || name == plainLayoutName())
1113 LayoutList::iterator it =
1114 remove_if(layoutlist_.begin(), layoutlist_.end(),
1115 LayoutNamesEqual(name));
1117 LayoutList::iterator end = layoutlist_.end();
1118 bool const ret = (it != end);
1119 layoutlist_.erase(it, end);
1124 // Load textclass info if not loaded yet
1125 bool TextClass::load(string const & path) const
1130 // Read style-file, provided path is searched before the system ones
1131 // If path is a file, it is loaded directly.
1132 FileName layout_file(path);
1133 if (!path.empty() && !layout_file.isReadableFile())
1134 layout_file = FileName(addName(path, name_ + ".layout"));
1135 if (layout_file.empty() || !layout_file.exists())
1136 layout_file = libFileSearch("layouts", name_, "layout");
1137 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1140 lyxerr << "Error reading `"
1141 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1142 << "'\n(Check `" << name_
1143 << "')\nCheck your installation and "
1144 "try Options/Reconfigure..." << endl;
1151 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1154 layoutlist_.push_back(createBasicLayout(n, true));
1158 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1160 // FIXME The fix for the InsetLayout part of 4812 would be here:
1161 // Add the InsetLayout to the document class if it is not found.
1163 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1164 while (!n.empty()) {
1165 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1166 if (cit != cen && cit->first == n)
1168 size_t i = n.find(':');
1169 if (i == string::npos)
1173 return plain_insetlayout_;
1177 docstring const & TextClass::defaultLayoutName() const
1179 // This really should come from the actual layout... (Lgb)
1180 return defaultlayout_;
1184 Layout const & TextClass::defaultLayout() const
1186 return operator[](defaultLayoutName());
1190 bool TextClass::isDefaultLayout(Layout const & layout) const
1192 return layout.name() == defaultLayoutName();
1196 bool TextClass::isPlainLayout(Layout const & layout) const
1198 return layout.name() == plainLayoutName();
1202 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1204 static Layout * defaultLayout = NULL;
1206 if (defaultLayout) {
1207 defaultLayout->setUnknown(unknown);
1208 defaultLayout->setName(name);
1209 return *defaultLayout;
1212 static char const * s = "Margin Static\n"
1213 "LatexType Paragraph\n"
1216 "AlignPossible Left, Right, Center\n"
1217 "LabelType No_Label\n"
1219 istringstream ss(s);
1220 Lexer lex(textClassTags);
1222 defaultLayout = new Layout;
1223 defaultLayout->setUnknown(unknown);
1224 defaultLayout->setName(name);
1225 if (!readStyle(lex, *defaultLayout)) {
1226 // The only way this happens is because the hardcoded layout above
1228 LASSERT(false, /**/);
1230 return *defaultLayout;
1234 /////////////////////////////////////////////////////////////////////////
1236 // DocumentClassBundle
1238 /////////////////////////////////////////////////////////////////////////
1240 DocumentClassBundle::~DocumentClassBundle()
1242 for (size_t i = 0; i != documentClasses_.size(); ++i)
1243 delete documentClasses_[i];
1244 documentClasses_.clear();
1247 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1249 DocumentClass * dc = new DocumentClass(baseClass);
1250 documentClasses_.push_back(dc);
1251 return *documentClasses_.back();
1255 DocumentClassBundle & DocumentClassBundle::get()
1257 static DocumentClassBundle singleton;
1262 DocumentClass & DocumentClassBundle::makeDocumentClass(
1263 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1265 DocumentClass & doc_class = newClass(baseClass);
1266 LayoutModuleList::const_iterator it = modlist.begin();
1267 LayoutModuleList::const_iterator en = modlist.end();
1268 for (; it != en; it++) {
1269 string const modName = *it;
1270 LyXModule * lm = theModuleList[modName];
1272 docstring const msg =
1273 bformat(_("The module %1$s has been requested by\n"
1274 "this document but has not been found in the list of\n"
1275 "available modules. If you recently installed it, you\n"
1276 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1277 ExceptionMessage(WarningException,_("Module not available"),
1278 msg + _("Some layouts may not be available."));
1281 if (!lm->isAvailable()) {
1282 docstring const msg =
1283 bformat(_("The module %1$s requires a package that is\n"
1284 "not available in your LaTeX installation. LaTeX output\n"
1285 "may not be possible.\n"), from_utf8(modName));
1286 ExceptionMessage(WarningException, _("Package not available"), msg);
1288 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1289 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1290 docstring const msg =
1291 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1292 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1299 /////////////////////////////////////////////////////////////////////////
1303 /////////////////////////////////////////////////////////////////////////
1305 DocumentClass::DocumentClass(LayoutFile const & tc)
1310 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1312 LayoutList::const_iterator it = layoutlist_.begin();
1313 LayoutList::const_iterator end = layoutlist_.end();
1314 for (; it != end; ++it)
1315 if (it->latexname() == lay)
1321 bool DocumentClass::provides(string const & p) const
1323 return provides_.find(p) != provides_.end();
1327 bool DocumentClass::hasTocLevels() const
1329 return min_toclevel_ != Layout::NOT_IN_TOC;
1333 Layout const & DocumentClass::htmlTOCLayout() const
1335 if (html_toc_section_.empty()) {
1336 // we're going to look for the layout with the minimum toclevel
1337 TextClass::LayoutList::const_iterator lit = begin();
1338 TextClass::LayoutList::const_iterator const len = end();
1339 int minlevel = 1000;
1340 Layout const * lay = NULL;
1341 for (; lit != len; ++lit) {
1342 int const level = lit->toclevel;
1343 // we don't want Part
1344 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1350 html_toc_section_ = lay->name();
1352 // hmm. that is very odd, so we'll do our best
1353 html_toc_section_ = defaultLayoutName();
1355 return operator[](html_toc_section_);
1359 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1361 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1363 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1364 if (it != cite_formats_.end())
1366 return default_format;
1370 string const & DocumentClass::getCiteMacro(string const & macro) const
1372 static string empty;
1373 map<string, string>::const_iterator it = cite_macros_.find(macro);
1374 if (it != cite_macros_.end())
1380 /////////////////////////////////////////////////////////////////////////
1384 /////////////////////////////////////////////////////////////////////////
1386 ostream & operator<<(ostream & os, PageSides p)