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 = 20;
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 std::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,
205 LexerKeyword textClassTags[] = {
206 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
207 { "addtopreamble", TC_ADDTOPREAMBLE },
208 { "classoptions", TC_CLASSOPTIONS },
209 { "columns", TC_COLUMNS },
210 { "counter", TC_COUNTER },
211 { "defaultfont", TC_DEFAULTFONT },
212 { "defaultmodule", TC_DEFAULTMODULE },
213 { "defaultstyle", TC_DEFAULTSTYLE },
214 { "excludesmodule", TC_EXCLUDESMODULE },
215 { "float", TC_FLOAT },
216 { "format", TC_FORMAT },
217 { "htmlpreamble", TC_HTMLPREAMBLE },
218 { "ifcounter", TC_IFCOUNTER },
219 { "ifstyle", TC_IFSTYLE },
220 { "input", TC_INPUT },
221 { "insetlayout", TC_INSETLAYOUT },
222 { "leftmargin", TC_LEFTMARGIN },
223 { "nofloat", TC_NOFLOAT },
224 { "nostyle", TC_NOSTYLE },
225 { "outputformat", TC_OUTPUTFORMAT },
226 { "outputtype", TC_OUTPUTTYPE },
227 { "pagestyle", TC_PAGESTYLE },
228 { "preamble", TC_PREAMBLE },
229 { "provides", TC_PROVIDES },
230 { "providesmodule", TC_PROVIDESMODULE },
231 { "requires", TC_REQUIRES },
232 { "rightmargin", TC_RIGHTMARGIN },
233 { "secnumdepth", TC_SECNUMDEPTH },
234 { "sides", TC_SIDES },
235 { "style", TC_STYLE },
236 { "titlelatexname", TC_TITLELATEXNAME },
237 { "titlelatextype", TC_TITLELATEXTYPE },
238 { "tocdepth", TC_TOCDEPTH }
244 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
246 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
247 FileName const tempfile = FileName::tempName("convert_layout");
248 bool success = layout2layout(filename, tempfile);
250 success = readWithoutConv(tempfile, rt) == OK;
251 tempfile.removeFile();
256 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
258 if (!filename.isReadableFile()) {
259 lyxerr << "Cannot read layout file `" << filename << "'."
264 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
265 to_utf8(makeDisplayPath(filename.absFilename())));
267 // Define the plain layout used in table cells, ert, etc. Note that
268 // we do this before loading any layout file, so that classes can
269 // override features of this layout if they should choose to do so.
270 if (rt == BASECLASS && !hasLayout(plain_layout_))
271 layoutlist_.push_back(createBasicLayout(plain_layout_));
273 Lexer lexrc(textClassTags);
274 lexrc.setFile(filename);
275 ReturnValues retval = read(lexrc, rt);
277 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
278 to_utf8(makeDisplayPath(filename.absFilename())));
284 bool TextClass::read(FileName const & filename, ReadType rt)
286 ReturnValues const retval = readWithoutConv(filename, rt);
287 if (retval != FORMAT_MISMATCH)
290 bool const worx = convertLayoutFormat(filename, rt);
292 LYXERR0 ("Unable to convert " << filename <<
293 " to format " << FORMAT);
300 bool TextClass::validate(std::string const & str)
303 return tc.read(str, VALIDATION);
307 bool TextClass::read(std::string const & str, ReadType rt)
309 Lexer lexrc(textClassTags);
310 istringstream is(str);
312 ReturnValues retval = read(lexrc, rt);
314 if (retval != FORMAT_MISMATCH)
317 // write the layout string to a temporary file
318 FileName const tempfile = FileName::tempName("TextClass_read");
319 ofstream os(tempfile.toFilesystemEncoding().c_str());
321 LYXERR0("Unable to create temporary file");
327 // now try to convert it
328 bool const worx = convertLayoutFormat(tempfile, rt);
330 LYXERR0("Unable to convert internal layout information to format "
333 tempfile.removeFile();
338 // Reads a textclass structure from file.
339 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
341 bool error = !lexrc.isOK();
343 // Format of files before the 'Format' tag was introduced
347 while (lexrc.isOK() && !error) {
348 int le = lexrc.lex();
351 case Lexer::LEX_FEOF:
354 case Lexer::LEX_UNDEF:
355 lexrc.printError("Unknown TextClass tag `$$Token'");
363 // used below to track whether we are in an IfStyle or IfCounter tag.
364 bool ifstyle = false;
365 bool ifcounter = false;
367 switch (static_cast<TextClassTags>(le)) {
371 format = lexrc.getInteger();
374 case TC_OUTPUTFORMAT:
376 outputFormat_ = lexrc.getString();
380 readOutputType(lexrc);
381 switch(outputType_) {
383 outputFormat_ = "latex";
386 outputFormat_ = "docbook";
389 outputFormat_ = "literate";
394 case TC_INPUT: // Include file
396 string const inc = lexrc.getString();
397 FileName tmp = libFileSearch("layouts", inc,
401 lexrc.printError("Could not find input file: " + inc);
403 } else if (!read(tmp, MERGE)) {
404 lexrc.printError("Error reading input"
405 "file: " + tmp.absFilename());
411 case TC_DEFAULTSTYLE:
413 docstring const name = from_utf8(subst(lexrc.getString(),
415 defaultlayout_ = name;
424 lexrc.printError("No name given for style: `$$Token'.");
428 docstring const name = from_utf8(subst(lexrc.getString(),
431 string s = "Could not read name for style: `$$Token' "
432 + lexrc.getString() + " is probably not valid UTF-8!";
433 lexrc.printError(s.c_str());
435 // Since we couldn't read the name, we just scan the rest
436 // of the style and discard it.
437 error = !readStyle(lexrc, lay);
438 } else if (hasLayout(name)) {
439 Layout & lay = operator[](name);
440 error = !readStyle(lexrc, lay);
441 } else if (!ifstyle) {
443 layout.setName(name);
444 error = !readStyle(lexrc, layout);
446 layoutlist_.push_back(layout);
448 if (defaultlayout_.empty()) {
449 // We do not have a default layout yet, so we choose
450 // the first layout we encounter.
451 defaultlayout_ = name;
455 // scan the rest and discard it
457 readStyle(lexrc, lay);
468 docstring const style = from_utf8(subst(lexrc.getString(),
470 if (!deleteLayout(style))
471 lyxerr << "Cannot delete style `"
472 << to_utf8(style) << '\'' << endl;
478 columns_ = lexrc.getInteger();
483 switch (lexrc.getInteger()) {
484 case 1: sides_ = OneSide; break;
485 case 2: sides_ = TwoSides; break;
487 lyxerr << "Impossible number of page"
488 " sides, setting to one."
498 pagestyle_ = rtrim(lexrc.getString());
502 defaultfont_ = lyxRead(lexrc);
503 if (!defaultfont_.resolved()) {
504 lexrc.printError("Warning: defaultfont should "
505 "be fully instantiated!");
506 defaultfont_.realize(sane_font);
512 secnumdepth_ = lexrc.getInteger();
517 tocdepth_ = lexrc.getInteger();
520 // First step to support options
521 case TC_CLASSOPTIONS:
522 readClassOptions(lexrc);
526 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
529 case TC_HTMLPREAMBLE:
530 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
533 case TC_ADDTOPREAMBLE:
534 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
537 case TC_ADDTOHTMLPREAMBLE:
538 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
543 string const feature = lexrc.getString();
545 if (lexrc.getInteger())
546 provides_.insert(feature);
548 provides_.erase(feature);
554 vector<string> const req
555 = getVectorFromString(lexrc.getString());
556 requires_.insert(req.begin(), req.end());
560 case TC_DEFAULTMODULE: {
562 string const module = lexrc.getString();
563 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
564 default_modules_.push_back(module);
568 case TC_PROVIDESMODULE: {
570 string const module = lexrc.getString();
571 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
572 provided_modules_.push_back(module);
576 case TC_EXCLUDESMODULE: {
578 string const module = lexrc.getString();
579 // modules already have their own way to exclude other modules
581 LYXERR0("ExcludesModule tag cannot be used in a module!");
584 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
585 excluded_modules_.push_back(module);
589 case TC_LEFTMARGIN: // left margin type
591 leftmargin_ = lexrc.getDocString();
594 case TC_RIGHTMARGIN: // right margin type
596 rightmargin_ = lexrc.getDocString();
599 case TC_INSETLAYOUT: {
601 lexrc.printError("No name given for InsetLayout: `$$Token'.");
605 docstring const name = subst(lexrc.getDocString(), '_', ' ');
607 string s = "Could not read name for InsetLayout: `$$Token' "
608 + lexrc.getString() + " is probably not valid UTF-8!";
609 lexrc.printError(s.c_str());
611 // Since we couldn't read the name, we just scan the rest
612 // of the style and discard it.
613 il.read(lexrc, *this);
615 } else if (hasInsetLayout(name)) {
616 InsetLayout & il = insetlayoutlist_[name];
617 error = !il.read(lexrc, *this);
621 error = !il.read(lexrc, *this);
623 insetlayoutlist_[name] = il;
636 docstring const name = lexrc.getDocString();
638 string s = "Could not read name for counter: `$$Token' "
639 + lexrc.getString() + " is probably not valid UTF-8!";
640 lexrc.printError(s.c_str());
642 // Since we couldn't read the name, we just scan the rest
646 error = !counters_.read(lexrc, name, !ifcounter);
649 lexrc.printError("No name given for style: `$$Token'.");
656 case TC_TITLELATEXTYPE:
657 readTitleType(lexrc);
660 case TC_TITLELATEXNAME:
662 titlename_ = lexrc.getString();
667 string const nofloat = lexrc.getString();
668 floatlist_.erase(nofloat);
673 //Note that this is triggered the first time through the loop unless
674 //we hit a format tag.
675 if (format != FORMAT)
679 if (format != FORMAT)
680 return FORMAT_MISMATCH;
683 return (error ? ERROR : OK);
685 if (defaultlayout_.empty()) {
686 LYXERR0("Error: Textclass '" << name_
687 << "' is missing a defaultstyle.");
691 // Try to erase "stdinsets" from the provides_ set.
693 // Provides stdinsets 1
694 // declaration simply tells us that the standard insets have been
695 // defined. (It's found in stdinsets.inc but could also be used in
696 // user-defined files.) There isn't really any such package. So we
697 // might as well go ahead and erase it.
698 // If we do not succeed, then it was not there, which means that
699 // the textclass did not provide the definitions of the standard
700 // insets. So we need to try to load them.
701 int erased = provides_.erase("stdinsets");
703 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
706 throw ExceptionMessage(WarningException, _("Missing File"),
707 _("Could not find stdinsets.inc! This may lead to data loss!"));
709 } else if (!read(tmp, MERGE)) {
710 throw ExceptionMessage(WarningException, _("Corrupt File"),
711 _("Could not read stdinsets.inc! This may lead to data loss!"));
716 min_toclevel_ = Layout::NOT_IN_TOC;
717 max_toclevel_ = Layout::NOT_IN_TOC;
718 const_iterator lit = begin();
719 const_iterator len = end();
720 for (; lit != len; ++lit) {
721 int const toclevel = lit->toclevel;
722 if (toclevel != Layout::NOT_IN_TOC) {
723 if (min_toclevel_ == Layout::NOT_IN_TOC)
724 min_toclevel_ = toclevel;
726 min_toclevel_ = min(min_toclevel_, toclevel);
727 max_toclevel_ = max(max_toclevel_, toclevel);
730 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
731 << ", maximum is " << max_toclevel_);
733 return (error ? ERROR : OK);
737 void TextClass::readTitleType(Lexer & lexrc)
739 LexerKeyword titleTypeTags[] = {
740 { "commandafter", TITLE_COMMAND_AFTER },
741 { "environment", TITLE_ENVIRONMENT }
744 PushPopHelper pph(lexrc, titleTypeTags);
746 int le = lexrc.lex();
748 case Lexer::LEX_UNDEF:
749 lexrc.printError("Unknown output type `$$Token'");
751 case TITLE_COMMAND_AFTER:
752 case TITLE_ENVIRONMENT:
753 titletype_ = static_cast<TitleLatexType>(le);
756 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
762 void TextClass::readOutputType(Lexer & lexrc)
764 LexerKeyword outputTypeTags[] = {
765 { "docbook", DOCBOOK },
767 { "literate", LITERATE }
770 PushPopHelper pph(lexrc, outputTypeTags);
772 int le = lexrc.lex();
774 case Lexer::LEX_UNDEF:
775 lexrc.printError("Unknown output type `$$Token'");
780 outputType_ = static_cast<OutputType>(le);
783 LYXERR0("Unhandled value " << le);
789 void TextClass::readClassOptions(Lexer & lexrc)
799 LexerKeyword classOptionsTags[] = {
801 {"fontsize", CO_FONTSIZE },
802 {"header", CO_HEADER },
803 {"other", CO_OTHER },
804 {"pagestyle", CO_PAGESTYLE }
807 lexrc.pushTable(classOptionsTags);
809 while (!getout && lexrc.isOK()) {
810 int le = lexrc.lex();
812 case Lexer::LEX_UNDEF:
813 lexrc.printError("Unknown ClassOption tag `$$Token'");
820 opt_fontsize_ = rtrim(lexrc.getString());
824 opt_pagestyle_ = rtrim(lexrc.getString());
828 options_ = lexrc.getString();
832 class_header_ = subst(lexrc.getString(), """, "\"");
843 void TextClass::readFloat(Lexer & lexrc)
860 LexerKeyword floatTags[] = {
862 { "extension", FT_EXT },
863 { "guiname", FT_NAME },
864 { "htmlattr", FT_HTMLATTR },
865 { "htmlstyle", FT_HTMLSTYLE },
866 { "htmltag", FT_HTMLTAG },
867 { "latexbuiltin", FT_BUILTIN },
868 { "listname", FT_LISTNAME },
869 { "numberwithin", FT_WITHIN },
870 { "placement", FT_PLACEMENT },
871 { "style", FT_STYLE },
875 lexrc.pushTable(floatTags);
887 bool builtin = false;
890 while (!getout && lexrc.isOK()) {
891 int le = lexrc.lex();
893 case Lexer::LEX_UNDEF:
894 lexrc.printError("Unknown float tag `$$Token'");
901 type = lexrc.getString();
902 if (floatlist_.typeExist(type)) {
903 Floating const & fl = floatlist_.getType(type);
904 placement = fl.placement();
906 within = fl.within();
909 listName = fl.listName();
910 builtin = fl.builtin();
915 name = lexrc.getString();
919 placement = lexrc.getString();
923 ext = lexrc.getString();
927 within = lexrc.getString();
928 if (within == "none")
933 style = lexrc.getString();
937 listName = lexrc.getString();
941 builtin = lexrc.getBool();
945 htmlattr = lexrc.getString();
949 htmlstyle = lexrc.getLongString("EndHTMLStyle");
953 htmltag = lexrc.getString();
961 // Here if have a full float if getout == true
963 Floating fl(type, placement, ext, within, style, name,
964 listName, htmltag, htmlattr, htmlstyle, builtin);
965 floatlist_.newFloat(fl);
966 // each float has its own counter
967 counters_.newCounter(from_ascii(type), from_ascii(within),
968 docstring(), docstring());
969 // also define sub-float counters
970 docstring const subtype = "sub-" + from_ascii(type);
971 counters_.newCounter(subtype, from_ascii(type),
972 "\\alph{" + subtype + "}", docstring());
979 bool TextClass::hasLayout(docstring const & n) const
981 docstring const name = n.empty() ? defaultLayoutName() : n;
983 return find_if(layoutlist_.begin(), layoutlist_.end(),
984 LayoutNamesEqual(name))
985 != layoutlist_.end();
989 bool TextClass::hasInsetLayout(docstring const & n) const
993 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
994 InsetLayouts::const_iterator en = insetlayoutlist_.end();
995 for (; it != en; ++it)
1002 Layout const & TextClass::operator[](docstring const & name) const
1004 LASSERT(!name.empty(), /**/);
1007 find_if(begin(), end(), LayoutNamesEqual(name));
1010 lyxerr << "We failed to find the layout '" << to_utf8(name)
1011 << "' in the layout list. You MUST investigate!"
1013 for (const_iterator cit = begin(); cit != end(); ++cit)
1014 lyxerr << " " << to_utf8(cit->name()) << endl;
1016 // we require the name to exist
1017 LASSERT(false, /**/);
1024 Layout & TextClass::operator[](docstring const & name)
1026 LASSERT(!name.empty(), /**/);
1028 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1031 LYXERR0("We failed to find the layout '" << to_utf8(name)
1032 << "' in the layout list. You MUST investigate!");
1033 for (const_iterator cit = begin(); cit != end(); ++cit)
1034 LYXERR0(" " << to_utf8(cit->name()));
1036 // we require the name to exist
1037 LASSERT(false, /**/);
1044 bool TextClass::deleteLayout(docstring const & name)
1046 if (name == defaultLayoutName() || name == plainLayoutName())
1049 LayoutList::iterator it =
1050 remove_if(layoutlist_.begin(), layoutlist_.end(),
1051 LayoutNamesEqual(name));
1053 LayoutList::iterator end = layoutlist_.end();
1054 bool const ret = (it != end);
1055 layoutlist_.erase(it, end);
1060 // Load textclass info if not loaded yet
1061 bool TextClass::load(string const & path) const
1066 // Read style-file, provided path is searched before the system ones
1067 // If path is a file, it is loaded directly.
1068 FileName layout_file(path);
1069 if (!path.empty() && !layout_file.isReadableFile())
1070 layout_file = FileName(addName(path, name_ + ".layout"));
1071 if (layout_file.empty() || !layout_file.exists())
1072 layout_file = libFileSearch("layouts", name_, "layout");
1073 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1076 lyxerr << "Error reading `"
1077 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1078 << "'\n(Check `" << name_
1079 << "')\nCheck your installation and "
1080 "try Options/Reconfigure..." << endl;
1087 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1090 layoutlist_.push_back(createBasicLayout(n, true));
1094 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1096 // FIXME The fix for the InsetLayout part of 4812 would be here:
1097 // Add the InsetLayout to the document class if it is not found.
1099 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1100 while (!n.empty()) {
1101 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1102 if (cit != cen && cit->first == n)
1104 size_t i = n.find(':');
1105 if (i == string::npos)
1109 return plain_insetlayout_;
1113 docstring const & TextClass::defaultLayoutName() const
1115 // This really should come from the actual layout... (Lgb)
1116 return defaultlayout_;
1120 Layout const & TextClass::defaultLayout() const
1122 return operator[](defaultLayoutName());
1126 bool TextClass::isDefaultLayout(Layout const & layout) const
1128 return layout.name() == defaultLayoutName();
1132 bool TextClass::isPlainLayout(Layout const & layout) const
1134 return layout.name() == plainLayoutName();
1138 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1140 static Layout * defaultLayout = NULL;
1142 if (defaultLayout) {
1143 defaultLayout->setUnknown(unknown);
1144 defaultLayout->setName(name);
1145 return *defaultLayout;
1148 static char const * s = "Margin Static\n"
1149 "LatexType Paragraph\n"
1152 "AlignPossible Left, Right, Center\n"
1153 "LabelType No_Label\n"
1155 istringstream ss(s);
1156 Lexer lex(textClassTags);
1158 defaultLayout = new Layout;
1159 defaultLayout->setUnknown(unknown);
1160 defaultLayout->setName(name);
1161 if (!readStyle(lex, *defaultLayout)) {
1162 // The only way this happens is because the hardcoded layout above
1164 LASSERT(false, /**/);
1166 return *defaultLayout;
1169 /////////////////////////////////////////////////////////////////////////
1171 // DocumentClassBundle
1173 /////////////////////////////////////////////////////////////////////////
1175 DocumentClassBundle::~DocumentClassBundle()
1177 for (size_t i = 0; i != documentClasses_.size(); ++i)
1178 delete documentClasses_[i];
1179 documentClasses_.clear();
1182 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1184 DocumentClass * dc = new DocumentClass(baseClass);
1185 documentClasses_.push_back(dc);
1186 return *documentClasses_.back();
1190 DocumentClassBundle & DocumentClassBundle::get()
1192 static DocumentClassBundle singleton;
1197 DocumentClass & DocumentClassBundle::makeDocumentClass(
1198 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1200 DocumentClass & doc_class = newClass(baseClass);
1201 LayoutModuleList::const_iterator it = modlist.begin();
1202 LayoutModuleList::const_iterator en = modlist.end();
1203 for (; it != en; it++) {
1204 string const modName = *it;
1205 LyXModule * lm = theModuleList[modName];
1207 docstring const msg =
1208 bformat(_("The module %1$s has been requested by\n"
1209 "this document but has not been found in the list of\n"
1210 "available modules. If you recently installed it, you\n"
1211 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1212 ExceptionMessage(WarningException,_("Module not available"),
1213 msg + _("Some layouts may not be available."));
1216 if (!lm->isAvailable()) {
1217 docstring const msg =
1218 bformat(_("The module %1$s requires a package that is\n"
1219 "not available in your LaTeX installation. LaTeX output\n"
1220 "may not be possible.\n"), from_utf8(modName));
1221 ExceptionMessage(WarningException, _("Package not available"), msg);
1223 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1224 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1225 docstring const msg =
1226 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1227 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1234 /////////////////////////////////////////////////////////////////////////
1238 /////////////////////////////////////////////////////////////////////////
1240 DocumentClass::DocumentClass(LayoutFile const & tc)
1245 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1247 LayoutList::const_iterator it = layoutlist_.begin();
1248 LayoutList::const_iterator end = layoutlist_.end();
1249 for (; it != end; ++it)
1250 if (it->latexname() == lay)
1256 bool DocumentClass::provides(string const & p) const
1258 return provides_.find(p) != provides_.end();
1262 bool DocumentClass::hasTocLevels() const
1264 return min_toclevel_ != Layout::NOT_IN_TOC;
1268 /////////////////////////////////////////////////////////////////////////
1272 /////////////////////////////////////////////////////////////////////////
1274 ostream & operator<<(ostream & os, PageSides p)