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 = 22;
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,
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)
866 LexerKeyword floatTags[] = {
868 { "extension", FT_EXT },
869 { "guiname", FT_NAME },
870 { "htmlattr", FT_HTMLATTR },
871 { "htmlstyle", FT_HTMLSTYLE },
872 { "htmltag", FT_HTMLTAG },
873 { "latexbuiltin", FT_BUILTIN },
874 { "listname", FT_LISTNAME },
875 { "numberwithin", FT_WITHIN },
876 { "placement", FT_PLACEMENT },
877 { "style", FT_STYLE },
881 lexrc.pushTable(floatTags);
893 bool builtin = false;
896 while (!getout && lexrc.isOK()) {
897 int le = lexrc.lex();
899 case Lexer::LEX_UNDEF:
900 lexrc.printError("Unknown float tag `$$Token'");
907 type = lexrc.getString();
908 if (floatlist_.typeExist(type)) {
909 Floating const & fl = floatlist_.getType(type);
910 placement = fl.placement();
912 within = fl.within();
915 listName = fl.listName();
916 builtin = fl.builtin();
921 name = lexrc.getString();
925 placement = lexrc.getString();
929 ext = lexrc.getString();
933 within = lexrc.getString();
934 if (within == "none")
939 style = lexrc.getString();
943 listName = lexrc.getString();
947 builtin = lexrc.getBool();
951 htmlattr = lexrc.getString();
955 htmlstyle = lexrc.getLongString("EndHTMLStyle");
959 htmltag = lexrc.getString();
967 // Here if have a full float if getout == true
969 Floating fl(type, placement, ext, within, style, name,
970 listName, htmltag, htmlattr, htmlstyle, builtin);
971 floatlist_.newFloat(fl);
972 // each float has its own counter
973 counters_.newCounter(from_ascii(type), from_ascii(within),
974 docstring(), docstring());
975 // also define sub-float counters
976 docstring const subtype = "sub-" + from_ascii(type);
977 counters_.newCounter(subtype, from_ascii(type),
978 "\\alph{" + subtype + "}", docstring());
985 bool TextClass::hasLayout(docstring const & n) const
987 docstring const name = n.empty() ? defaultLayoutName() : n;
989 return find_if(layoutlist_.begin(), layoutlist_.end(),
990 LayoutNamesEqual(name))
991 != layoutlist_.end();
995 bool TextClass::hasInsetLayout(docstring const & n) const
999 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1000 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1001 for (; it != en; ++it)
1008 Layout const & TextClass::operator[](docstring const & name) const
1010 LASSERT(!name.empty(), /**/);
1013 find_if(begin(), end(), LayoutNamesEqual(name));
1016 lyxerr << "We failed to find the layout '" << to_utf8(name)
1017 << "' in the layout list. You MUST investigate!"
1019 for (const_iterator cit = begin(); cit != end(); ++cit)
1020 lyxerr << " " << to_utf8(cit->name()) << endl;
1022 // we require the name to exist
1023 LASSERT(false, /**/);
1030 Layout & TextClass::operator[](docstring const & name)
1032 LASSERT(!name.empty(), /**/);
1034 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1037 LYXERR0("We failed to find the layout '" << to_utf8(name)
1038 << "' in the layout list. You MUST investigate!");
1039 for (const_iterator cit = begin(); cit != end(); ++cit)
1040 LYXERR0(" " << to_utf8(cit->name()));
1042 // we require the name to exist
1043 LASSERT(false, /**/);
1050 bool TextClass::deleteLayout(docstring const & name)
1052 if (name == defaultLayoutName() || name == plainLayoutName())
1055 LayoutList::iterator it =
1056 remove_if(layoutlist_.begin(), layoutlist_.end(),
1057 LayoutNamesEqual(name));
1059 LayoutList::iterator end = layoutlist_.end();
1060 bool const ret = (it != end);
1061 layoutlist_.erase(it, end);
1066 // Load textclass info if not loaded yet
1067 bool TextClass::load(string const & path) const
1072 // Read style-file, provided path is searched before the system ones
1073 // If path is a file, it is loaded directly.
1074 FileName layout_file(path);
1075 if (!path.empty() && !layout_file.isReadableFile())
1076 layout_file = FileName(addName(path, name_ + ".layout"));
1077 if (layout_file.empty() || !layout_file.exists())
1078 layout_file = libFileSearch("layouts", name_, "layout");
1079 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1082 lyxerr << "Error reading `"
1083 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1084 << "'\n(Check `" << name_
1085 << "')\nCheck your installation and "
1086 "try Options/Reconfigure..." << endl;
1093 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1096 layoutlist_.push_back(createBasicLayout(n, true));
1100 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1102 // FIXME The fix for the InsetLayout part of 4812 would be here:
1103 // Add the InsetLayout to the document class if it is not found.
1105 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1106 while (!n.empty()) {
1107 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1108 if (cit != cen && cit->first == n)
1110 size_t i = n.find(':');
1111 if (i == string::npos)
1115 return plain_insetlayout_;
1119 docstring const & TextClass::defaultLayoutName() const
1121 // This really should come from the actual layout... (Lgb)
1122 return defaultlayout_;
1126 Layout const & TextClass::defaultLayout() const
1128 return operator[](defaultLayoutName());
1132 bool TextClass::isDefaultLayout(Layout const & layout) const
1134 return layout.name() == defaultLayoutName();
1138 bool TextClass::isPlainLayout(Layout const & layout) const
1140 return layout.name() == plainLayoutName();
1144 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1146 static Layout * defaultLayout = NULL;
1148 if (defaultLayout) {
1149 defaultLayout->setUnknown(unknown);
1150 defaultLayout->setName(name);
1151 return *defaultLayout;
1154 static char const * s = "Margin Static\n"
1155 "LatexType Paragraph\n"
1158 "AlignPossible Left, Right, Center\n"
1159 "LabelType No_Label\n"
1161 istringstream ss(s);
1162 Lexer lex(textClassTags);
1164 defaultLayout = new Layout;
1165 defaultLayout->setUnknown(unknown);
1166 defaultLayout->setName(name);
1167 if (!readStyle(lex, *defaultLayout)) {
1168 // The only way this happens is because the hardcoded layout above
1170 LASSERT(false, /**/);
1172 return *defaultLayout;
1176 /////////////////////////////////////////////////////////////////////////
1178 // DocumentClassBundle
1180 /////////////////////////////////////////////////////////////////////////
1182 DocumentClassBundle::~DocumentClassBundle()
1184 for (size_t i = 0; i != documentClasses_.size(); ++i)
1185 delete documentClasses_[i];
1186 documentClasses_.clear();
1189 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1191 DocumentClass * dc = new DocumentClass(baseClass);
1192 documentClasses_.push_back(dc);
1193 return *documentClasses_.back();
1197 DocumentClassBundle & DocumentClassBundle::get()
1199 static DocumentClassBundle singleton;
1204 DocumentClass & DocumentClassBundle::makeDocumentClass(
1205 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1207 DocumentClass & doc_class = newClass(baseClass);
1208 LayoutModuleList::const_iterator it = modlist.begin();
1209 LayoutModuleList::const_iterator en = modlist.end();
1210 for (; it != en; it++) {
1211 string const modName = *it;
1212 LyXModule * lm = theModuleList[modName];
1214 docstring const msg =
1215 bformat(_("The module %1$s has been requested by\n"
1216 "this document but has not been found in the list of\n"
1217 "available modules. If you recently installed it, you\n"
1218 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1219 ExceptionMessage(WarningException,_("Module not available"),
1220 msg + _("Some layouts may not be available."));
1223 if (!lm->isAvailable()) {
1224 docstring const msg =
1225 bformat(_("The module %1$s requires a package that is\n"
1226 "not available in your LaTeX installation. LaTeX output\n"
1227 "may not be possible.\n"), from_utf8(modName));
1228 ExceptionMessage(WarningException, _("Package not available"), msg);
1230 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1231 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1232 docstring const msg =
1233 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1234 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1241 /////////////////////////////////////////////////////////////////////////
1245 /////////////////////////////////////////////////////////////////////////
1247 DocumentClass::DocumentClass(LayoutFile const & tc)
1252 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1254 LayoutList::const_iterator it = layoutlist_.begin();
1255 LayoutList::const_iterator end = layoutlist_.end();
1256 for (; it != end; ++it)
1257 if (it->latexname() == lay)
1263 bool DocumentClass::provides(string const & p) const
1265 return provides_.find(p) != provides_.end();
1269 bool DocumentClass::hasTocLevels() const
1271 return min_toclevel_ != Layout::NOT_IN_TOC;
1275 Layout const & DocumentClass::htmlTOCLayout() const
1277 if (html_toc_section_.empty()) {
1278 // we're going to look for the layout with the minimum toclevel
1279 TextClass::LayoutList::const_iterator lit = begin();
1280 TextClass::LayoutList::const_iterator const len = end();
1281 int minlevel = 1000;
1282 Layout const * lay = NULL;
1283 for (; lit != len; ++lit) {
1284 int const level = lit->toclevel;
1285 // we don't want Part
1286 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1292 html_toc_section_ = lay->name();
1294 // hmm. that is very odd, so we'll do our best
1295 html_toc_section_ = defaultLayoutName();
1297 return operator[](html_toc_section_);
1301 /////////////////////////////////////////////////////////////////////////
1305 /////////////////////////////////////////////////////////////////////////
1307 ostream & operator<<(ostream & os, PageSides p)