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 = 24;
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,
206 LexerKeyword textClassTags[] = {
207 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
208 { "addtopreamble", TC_ADDTOPREAMBLE },
209 { "classoptions", TC_CLASSOPTIONS },
210 { "columns", TC_COLUMNS },
211 { "counter", TC_COUNTER },
212 { "defaultfont", TC_DEFAULTFONT },
213 { "defaultmodule", TC_DEFAULTMODULE },
214 { "defaultstyle", TC_DEFAULTSTYLE },
215 { "excludesmodule", TC_EXCLUDESMODULE },
216 { "float", TC_FLOAT },
217 { "format", TC_FORMAT },
218 { "htmlpreamble", TC_HTMLPREAMBLE },
219 { "htmltocsection", TC_HTMLTOCSECTION },
220 { "ifcounter", TC_IFCOUNTER },
221 { "ifstyle", TC_IFSTYLE },
222 { "input", TC_INPUT },
223 { "insetlayout", TC_INSETLAYOUT },
224 { "leftmargin", TC_LEFTMARGIN },
225 { "nofloat", TC_NOFLOAT },
226 { "nostyle", TC_NOSTYLE },
227 { "outputformat", TC_OUTPUTFORMAT },
228 { "outputtype", TC_OUTPUTTYPE },
229 { "pagestyle", TC_PAGESTYLE },
230 { "preamble", TC_PREAMBLE },
231 { "provides", TC_PROVIDES },
232 { "providesmodule", TC_PROVIDESMODULE },
233 { "requires", TC_REQUIRES },
234 { "rightmargin", TC_RIGHTMARGIN },
235 { "secnumdepth", TC_SECNUMDEPTH },
236 { "sides", TC_SIDES },
237 { "style", TC_STYLE },
238 { "titlelatexname", TC_TITLELATEXNAME },
239 { "titlelatextype", TC_TITLELATEXTYPE },
240 { "tocdepth", TC_TOCDEPTH }
246 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
248 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
249 FileName const tempfile = FileName::tempName("convert_layout");
250 bool success = layout2layout(filename, tempfile);
252 success = readWithoutConv(tempfile, rt) == OK;
253 tempfile.removeFile();
258 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
260 if (!filename.isReadableFile()) {
261 lyxerr << "Cannot read layout file `" << filename << "'."
266 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
267 to_utf8(makeDisplayPath(filename.absFilename())));
269 // Define the plain layout used in table cells, ert, etc. Note that
270 // we do this before loading any layout file, so that classes can
271 // override features of this layout if they should choose to do so.
272 if (rt == BASECLASS && !hasLayout(plain_layout_))
273 layoutlist_.push_back(createBasicLayout(plain_layout_));
275 Lexer lexrc(textClassTags);
276 lexrc.setFile(filename);
277 ReturnValues retval = read(lexrc, rt);
279 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
280 to_utf8(makeDisplayPath(filename.absFilename())));
286 bool TextClass::read(FileName const & filename, ReadType rt)
288 ReturnValues const retval = readWithoutConv(filename, rt);
289 if (retval != FORMAT_MISMATCH)
292 bool const worx = convertLayoutFormat(filename, rt);
294 LYXERR0 ("Unable to convert " << filename <<
295 " to format " << FORMAT);
302 bool TextClass::validate(std::string const & str)
305 return tc.read(str, VALIDATION);
309 bool TextClass::read(std::string const & str, ReadType rt)
311 Lexer lexrc(textClassTags);
312 istringstream is(str);
314 ReturnValues retval = read(lexrc, rt);
316 if (retval != FORMAT_MISMATCH)
319 // write the layout string to a temporary file
320 FileName const tempfile = FileName::tempName("TextClass_read");
321 ofstream os(tempfile.toFilesystemEncoding().c_str());
323 LYXERR0("Unable to create temporary file");
329 // now try to convert it
330 bool const worx = convertLayoutFormat(tempfile, rt);
332 LYXERR0("Unable to convert internal layout information to format "
335 tempfile.removeFile();
340 // Reads a textclass structure from file.
341 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
343 bool error = !lexrc.isOK();
345 // Format of files before the 'Format' tag was introduced
349 while (lexrc.isOK() && !error) {
350 int le = lexrc.lex();
353 case Lexer::LEX_FEOF:
356 case Lexer::LEX_UNDEF:
357 lexrc.printError("Unknown TextClass tag `$$Token'");
365 // used below to track whether we are in an IfStyle or IfCounter tag.
366 bool ifstyle = false;
367 bool ifcounter = false;
369 switch (static_cast<TextClassTags>(le)) {
373 format = lexrc.getInteger();
376 case TC_OUTPUTFORMAT:
378 outputFormat_ = lexrc.getString();
382 readOutputType(lexrc);
383 switch(outputType_) {
385 outputFormat_ = "latex";
388 outputFormat_ = "docbook";
391 outputFormat_ = "literate";
396 case TC_INPUT: // Include file
398 string const inc = lexrc.getString();
399 FileName tmp = libFileSearch("layouts", inc,
403 lexrc.printError("Could not find input file: " + inc);
405 } else if (!read(tmp, MERGE)) {
406 lexrc.printError("Error reading input"
407 "file: " + tmp.absFilename());
413 case TC_DEFAULTSTYLE:
415 docstring const name = from_utf8(subst(lexrc.getString(),
417 defaultlayout_ = name;
426 lexrc.printError("No name given for style: `$$Token'.");
430 docstring const name = from_utf8(subst(lexrc.getString(),
433 string s = "Could not read name for style: `$$Token' "
434 + lexrc.getString() + " is probably not valid UTF-8!";
435 lexrc.printError(s.c_str());
437 // Since we couldn't read the name, we just scan the rest
438 // of the style and discard it.
439 error = !readStyle(lexrc, lay);
440 } else if (hasLayout(name)) {
441 Layout & lay = operator[](name);
442 error = !readStyle(lexrc, lay);
443 } else if (!ifstyle) {
445 layout.setName(name);
446 error = !readStyle(lexrc, layout);
448 layoutlist_.push_back(layout);
450 if (defaultlayout_.empty()) {
451 // We do not have a default layout yet, so we choose
452 // the first layout we encounter.
453 defaultlayout_ = name;
457 // scan the rest and discard it
459 readStyle(lexrc, lay);
470 docstring const style = from_utf8(subst(lexrc.getString(),
472 if (!deleteLayout(style))
473 lyxerr << "Cannot delete style `"
474 << to_utf8(style) << '\'' << endl;
480 columns_ = lexrc.getInteger();
485 switch (lexrc.getInteger()) {
486 case 1: sides_ = OneSide; break;
487 case 2: sides_ = TwoSides; break;
489 lyxerr << "Impossible number of page"
490 " sides, setting to one."
500 pagestyle_ = rtrim(lexrc.getString());
504 defaultfont_ = lyxRead(lexrc);
505 if (!defaultfont_.resolved()) {
506 lexrc.printError("Warning: defaultfont should "
507 "be fully instantiated!");
508 defaultfont_.realize(sane_font);
514 secnumdepth_ = lexrc.getInteger();
519 tocdepth_ = lexrc.getInteger();
522 // First step to support options
523 case TC_CLASSOPTIONS:
524 readClassOptions(lexrc);
528 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
531 case TC_HTMLPREAMBLE:
532 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
535 case TC_HTMLTOCSECTION:
536 html_toc_section_ = from_utf8(trim(lexrc.getString()));
539 case TC_ADDTOPREAMBLE:
540 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
543 case TC_ADDTOHTMLPREAMBLE:
544 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
549 string const feature = lexrc.getString();
551 if (lexrc.getInteger())
552 provides_.insert(feature);
554 provides_.erase(feature);
560 vector<string> const req
561 = getVectorFromString(lexrc.getString());
562 requires_.insert(req.begin(), req.end());
566 case TC_DEFAULTMODULE: {
568 string const module = lexrc.getString();
569 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
570 default_modules_.push_back(module);
574 case TC_PROVIDESMODULE: {
576 string const module = lexrc.getString();
577 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
578 provided_modules_.push_back(module);
582 case TC_EXCLUDESMODULE: {
584 string const module = lexrc.getString();
585 // modules already have their own way to exclude other modules
587 LYXERR0("ExcludesModule tag cannot be used in a module!");
590 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
591 excluded_modules_.push_back(module);
595 case TC_LEFTMARGIN: // left margin type
597 leftmargin_ = lexrc.getDocString();
600 case TC_RIGHTMARGIN: // right margin type
602 rightmargin_ = lexrc.getDocString();
605 case TC_INSETLAYOUT: {
607 lexrc.printError("No name given for InsetLayout: `$$Token'.");
611 docstring const name = subst(lexrc.getDocString(), '_', ' ');
613 string s = "Could not read name for InsetLayout: `$$Token' "
614 + lexrc.getString() + " is probably not valid UTF-8!";
615 lexrc.printError(s.c_str());
617 // Since we couldn't read the name, we just scan the rest
618 // of the style and discard it.
619 il.read(lexrc, *this);
621 } else if (hasInsetLayout(name)) {
622 InsetLayout & il = insetlayoutlist_[name];
623 error = !il.read(lexrc, *this);
627 error = !il.read(lexrc, *this);
629 insetlayoutlist_[name] = il;
642 docstring const name = lexrc.getDocString();
644 string s = "Could not read name for counter: `$$Token' "
645 + lexrc.getString() + " is probably not valid UTF-8!";
646 lexrc.printError(s.c_str());
648 // Since we couldn't read the name, we just scan the rest
652 error = !counters_.read(lexrc, name, !ifcounter);
655 lexrc.printError("No name given for style: `$$Token'.");
662 case TC_TITLELATEXTYPE:
663 readTitleType(lexrc);
666 case TC_TITLELATEXNAME:
668 titlename_ = lexrc.getString();
673 string const nofloat = lexrc.getString();
674 floatlist_.erase(nofloat);
679 //Note that this is triggered the first time through the loop unless
680 //we hit a format tag.
681 if (format != FORMAT)
685 if (format != FORMAT)
686 return FORMAT_MISMATCH;
689 return (error ? ERROR : OK);
691 if (defaultlayout_.empty()) {
692 LYXERR0("Error: Textclass '" << name_
693 << "' is missing a defaultstyle.");
697 // Try to erase "stdinsets" from the provides_ set.
699 // Provides stdinsets 1
700 // declaration simply tells us that the standard insets have been
701 // defined. (It's found in stdinsets.inc but could also be used in
702 // user-defined files.) There isn't really any such package. So we
703 // might as well go ahead and erase it.
704 // If we do not succeed, then it was not there, which means that
705 // the textclass did not provide the definitions of the standard
706 // insets. So we need to try to load them.
707 int erased = provides_.erase("stdinsets");
709 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
712 throw ExceptionMessage(WarningException, _("Missing File"),
713 _("Could not find stdinsets.inc! This may lead to data loss!"));
715 } else if (!read(tmp, MERGE)) {
716 throw ExceptionMessage(WarningException, _("Corrupt File"),
717 _("Could not read stdinsets.inc! This may lead to data loss!"));
722 min_toclevel_ = Layout::NOT_IN_TOC;
723 max_toclevel_ = Layout::NOT_IN_TOC;
724 const_iterator lit = begin();
725 const_iterator len = end();
726 for (; lit != len; ++lit) {
727 int const toclevel = lit->toclevel;
728 if (toclevel != Layout::NOT_IN_TOC) {
729 if (min_toclevel_ == Layout::NOT_IN_TOC)
730 min_toclevel_ = toclevel;
732 min_toclevel_ = min(min_toclevel_, toclevel);
733 max_toclevel_ = max(max_toclevel_, toclevel);
736 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
737 << ", maximum is " << max_toclevel_);
739 return (error ? ERROR : OK);
743 void TextClass::readTitleType(Lexer & lexrc)
745 LexerKeyword titleTypeTags[] = {
746 { "commandafter", TITLE_COMMAND_AFTER },
747 { "environment", TITLE_ENVIRONMENT }
750 PushPopHelper pph(lexrc, titleTypeTags);
752 int le = lexrc.lex();
754 case Lexer::LEX_UNDEF:
755 lexrc.printError("Unknown output type `$$Token'");
757 case TITLE_COMMAND_AFTER:
758 case TITLE_ENVIRONMENT:
759 titletype_ = static_cast<TitleLatexType>(le);
762 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
768 void TextClass::readOutputType(Lexer & lexrc)
770 LexerKeyword outputTypeTags[] = {
771 { "docbook", DOCBOOK },
773 { "literate", LITERATE }
776 PushPopHelper pph(lexrc, outputTypeTags);
778 int le = lexrc.lex();
780 case Lexer::LEX_UNDEF:
781 lexrc.printError("Unknown output type `$$Token'");
786 outputType_ = static_cast<OutputType>(le);
789 LYXERR0("Unhandled value " << le);
795 void TextClass::readClassOptions(Lexer & lexrc)
805 LexerKeyword classOptionsTags[] = {
807 {"fontsize", CO_FONTSIZE },
808 {"header", CO_HEADER },
809 {"other", CO_OTHER },
810 {"pagestyle", CO_PAGESTYLE }
813 lexrc.pushTable(classOptionsTags);
815 while (!getout && lexrc.isOK()) {
816 int le = lexrc.lex();
818 case Lexer::LEX_UNDEF:
819 lexrc.printError("Unknown ClassOption tag `$$Token'");
826 opt_fontsize_ = rtrim(lexrc.getString());
830 opt_pagestyle_ = rtrim(lexrc.getString());
834 options_ = lexrc.getString();
838 class_header_ = subst(lexrc.getString(), """, "\"");
849 void TextClass::readFloat(Lexer & lexrc)
867 LexerKeyword floatTags[] = {
869 { "extension", FT_EXT },
870 { "guiname", FT_NAME },
871 { "htmlattr", FT_HTMLATTR },
872 { "htmlstyle", FT_HTMLSTYLE },
873 { "htmltag", FT_HTMLTAG },
874 { "listcommand", FT_LISTCOMMAND },
875 { "listname", FT_LISTNAME },
876 { "needsfloatpkg", FT_NEEDSFLOAT },
877 { "numberwithin", FT_WITHIN },
878 { "placement", FT_PLACEMENT },
879 { "style", FT_STYLE },
883 lexrc.pushTable(floatTags);
896 bool needsfloat = true;
899 while (!getout && lexrc.isOK()) {
900 int le = lexrc.lex();
902 case Lexer::LEX_UNDEF:
903 lexrc.printError("Unknown float tag `$$Token'");
910 type = lexrc.getString();
911 if (floatlist_.typeExist(type)) {
912 Floating const & fl = floatlist_.getType(type);
913 placement = fl.placement();
915 within = fl.within();
918 listName = fl.listName();
919 needsfloat = fl.needsFloatPkg();
920 listCommand = fl.listCommand();
925 name = lexrc.getString();
929 placement = lexrc.getString();
933 ext = lexrc.getString();
937 within = lexrc.getString();
938 if (within == "none")
943 style = lexrc.getString();
947 listCommand = lexrc.getString();
951 listName = lexrc.getString();
955 needsfloat = lexrc.getBool();
959 htmlattr = lexrc.getString();
963 htmlstyle = lexrc.getLongString("EndHTMLStyle");
967 htmltag = lexrc.getString();
975 // Here we have a full float if getout == true
977 if (!needsfloat && listCommand.empty())
978 LYXERR0("The layout does not provide a list command " <<
979 "for the builtin float `" << type << "'. LyX will " <<
980 "not be able to produce a float list.");
981 Floating fl(type, placement, ext, within, style, name,
982 listName, listCommand, htmltag, htmlattr, htmlstyle,
984 floatlist_.newFloat(fl);
985 // each float has its own counter
986 counters_.newCounter(from_ascii(type), from_ascii(within),
987 docstring(), docstring());
988 // also define sub-float counters
989 docstring const subtype = "sub-" + from_ascii(type);
990 counters_.newCounter(subtype, from_ascii(type),
991 "\\alph{" + subtype + "}", docstring());
998 bool TextClass::hasLayout(docstring const & n) const
1000 docstring const name = n.empty() ? defaultLayoutName() : n;
1002 return find_if(layoutlist_.begin(), layoutlist_.end(),
1003 LayoutNamesEqual(name))
1004 != layoutlist_.end();
1008 bool TextClass::hasInsetLayout(docstring const & n) const
1012 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1013 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1014 for (; it != en; ++it)
1021 Layout const & TextClass::operator[](docstring const & name) const
1023 LASSERT(!name.empty(), /**/);
1026 find_if(begin(), end(), LayoutNamesEqual(name));
1029 lyxerr << "We failed to find the layout '" << to_utf8(name)
1030 << "' in the layout list. You MUST investigate!"
1032 for (const_iterator cit = begin(); cit != end(); ++cit)
1033 lyxerr << " " << to_utf8(cit->name()) << endl;
1035 // we require the name to exist
1036 LASSERT(false, /**/);
1043 Layout & TextClass::operator[](docstring const & name)
1045 LASSERT(!name.empty(), /**/);
1047 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1050 LYXERR0("We failed to find the layout '" << to_utf8(name)
1051 << "' in the layout list. You MUST investigate!");
1052 for (const_iterator cit = begin(); cit != end(); ++cit)
1053 LYXERR0(" " << to_utf8(cit->name()));
1055 // we require the name to exist
1056 LASSERT(false, /**/);
1063 bool TextClass::deleteLayout(docstring const & name)
1065 if (name == defaultLayoutName() || name == plainLayoutName())
1068 LayoutList::iterator it =
1069 remove_if(layoutlist_.begin(), layoutlist_.end(),
1070 LayoutNamesEqual(name));
1072 LayoutList::iterator end = layoutlist_.end();
1073 bool const ret = (it != end);
1074 layoutlist_.erase(it, end);
1079 // Load textclass info if not loaded yet
1080 bool TextClass::load(string const & path) const
1085 // Read style-file, provided path is searched before the system ones
1086 // If path is a file, it is loaded directly.
1087 FileName layout_file(path);
1088 if (!path.empty() && !layout_file.isReadableFile())
1089 layout_file = FileName(addName(path, name_ + ".layout"));
1090 if (layout_file.empty() || !layout_file.exists())
1091 layout_file = libFileSearch("layouts", name_, "layout");
1092 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1095 lyxerr << "Error reading `"
1096 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1097 << "'\n(Check `" << name_
1098 << "')\nCheck your installation and "
1099 "try Options/Reconfigure..." << endl;
1106 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1109 layoutlist_.push_back(createBasicLayout(n, true));
1113 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1115 // FIXME The fix for the InsetLayout part of 4812 would be here:
1116 // Add the InsetLayout to the document class if it is not found.
1118 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1119 while (!n.empty()) {
1120 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1121 if (cit != cen && cit->first == n)
1123 size_t i = n.find(':');
1124 if (i == string::npos)
1128 return plain_insetlayout_;
1132 docstring const & TextClass::defaultLayoutName() const
1134 // This really should come from the actual layout... (Lgb)
1135 return defaultlayout_;
1139 Layout const & TextClass::defaultLayout() const
1141 return operator[](defaultLayoutName());
1145 bool TextClass::isDefaultLayout(Layout const & layout) const
1147 return layout.name() == defaultLayoutName();
1151 bool TextClass::isPlainLayout(Layout const & layout) const
1153 return layout.name() == plainLayoutName();
1157 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1159 static Layout * defaultLayout = NULL;
1161 if (defaultLayout) {
1162 defaultLayout->setUnknown(unknown);
1163 defaultLayout->setName(name);
1164 return *defaultLayout;
1167 static char const * s = "Margin Static\n"
1168 "LatexType Paragraph\n"
1171 "AlignPossible Left, Right, Center\n"
1172 "LabelType No_Label\n"
1174 istringstream ss(s);
1175 Lexer lex(textClassTags);
1177 defaultLayout = new Layout;
1178 defaultLayout->setUnknown(unknown);
1179 defaultLayout->setName(name);
1180 if (!readStyle(lex, *defaultLayout)) {
1181 // The only way this happens is because the hardcoded layout above
1183 LASSERT(false, /**/);
1185 return *defaultLayout;
1189 /////////////////////////////////////////////////////////////////////////
1191 // DocumentClassBundle
1193 /////////////////////////////////////////////////////////////////////////
1195 DocumentClassBundle::~DocumentClassBundle()
1197 for (size_t i = 0; i != documentClasses_.size(); ++i)
1198 delete documentClasses_[i];
1199 documentClasses_.clear();
1202 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1204 DocumentClass * dc = new DocumentClass(baseClass);
1205 documentClasses_.push_back(dc);
1206 return *documentClasses_.back();
1210 DocumentClassBundle & DocumentClassBundle::get()
1212 static DocumentClassBundle singleton;
1217 DocumentClass & DocumentClassBundle::makeDocumentClass(
1218 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1220 DocumentClass & doc_class = newClass(baseClass);
1221 LayoutModuleList::const_iterator it = modlist.begin();
1222 LayoutModuleList::const_iterator en = modlist.end();
1223 for (; it != en; it++) {
1224 string const modName = *it;
1225 LyXModule * lm = theModuleList[modName];
1227 docstring const msg =
1228 bformat(_("The module %1$s has been requested by\n"
1229 "this document but has not been found in the list of\n"
1230 "available modules. If you recently installed it, you\n"
1231 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1232 ExceptionMessage(WarningException,_("Module not available"),
1233 msg + _("Some layouts may not be available."));
1236 if (!lm->isAvailable()) {
1237 docstring const msg =
1238 bformat(_("The module %1$s requires a package that is\n"
1239 "not available in your LaTeX installation. LaTeX output\n"
1240 "may not be possible.\n"), from_utf8(modName));
1241 ExceptionMessage(WarningException, _("Package not available"), msg);
1243 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1244 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1245 docstring const msg =
1246 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1247 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1254 /////////////////////////////////////////////////////////////////////////
1258 /////////////////////////////////////////////////////////////////////////
1260 DocumentClass::DocumentClass(LayoutFile const & tc)
1265 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1267 LayoutList::const_iterator it = layoutlist_.begin();
1268 LayoutList::const_iterator end = layoutlist_.end();
1269 for (; it != end; ++it)
1270 if (it->latexname() == lay)
1276 bool DocumentClass::provides(string const & p) const
1278 return provides_.find(p) != provides_.end();
1282 bool DocumentClass::hasTocLevels() const
1284 return min_toclevel_ != Layout::NOT_IN_TOC;
1288 Layout const & DocumentClass::htmlTOCLayout() const
1290 if (html_toc_section_.empty()) {
1291 // we're going to look for the layout with the minimum toclevel
1292 TextClass::LayoutList::const_iterator lit = begin();
1293 TextClass::LayoutList::const_iterator const len = end();
1294 int minlevel = 1000;
1295 Layout const * lay = NULL;
1296 for (; lit != len; ++lit) {
1297 int const level = lit->toclevel;
1298 // we don't want Part
1299 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1305 html_toc_section_ = lay->name();
1307 // hmm. that is very odd, so we'll do our best
1308 html_toc_section_ = defaultLayoutName();
1310 return operator[](html_toc_section_);
1314 /////////////////////////////////////////////////////////////////////////
1318 /////////////////////////////////////////////////////////////////////////
1320 ostream & operator<<(ostream & os, PageSides p)