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"
45 using namespace lyx::support;
51 class LayoutNamesEqual : public unary_function<Layout, bool> {
53 LayoutNamesEqual(docstring const & name)
56 bool operator()(Layout const & c) const
58 return c.name() == name_;
64 // Keep the changes documented in the Customization manual.
65 int const FORMAT = 16;
68 bool layout2layout(FileName const & filename, FileName const & tempfile)
70 FileName const script = libFileSearch("scripts", "layout2layout.py");
72 LYXERR0("Could not find layout conversion "
73 "script layout2layout.py.");
77 ostringstream command;
78 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
79 << ' ' << quoteName(filename.toFilesystemEncoding())
80 << ' ' << quoteName(tempfile.toFilesystemEncoding());
81 string const command_str = command.str();
83 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
85 cmd_ret const ret = runCommand(command_str);
87 LYXERR0("Could not run layout conversion script layout2layout.py.");
94 std::string translateRT(TextClass::ReadType rt)
97 case TextClass::BASECLASS:
99 case TextClass::MERGE:
101 case TextClass::MODULE:
102 return "module file";
103 case TextClass::VALIDATION:
113 // This string should not be translated here,
114 // because it is a layout identifier.
115 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
118 InsetLayout DocumentClass::plain_insetlayout_;
121 /////////////////////////////////////////////////////////////////////////
125 /////////////////////////////////////////////////////////////////////////
127 TextClass::TextClass()
130 outputFormat_ = "latex";
135 pagestyle_ = "default";
136 defaultfont_ = sane_font;
137 opt_fontsize_ = "10|11|12";
138 opt_pagestyle_ = "empty|plain|headings|fancy";
139 titletype_ = TITLE_COMMAND_AFTER;
140 titlename_ = "maketitle";
142 _("Plain Layout"); // a hack to make this translatable
146 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
148 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
149 if (!lay.read(lexrc, *this)) {
150 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
154 lay.resfont = lay.font;
155 lay.resfont.realize(defaultfont_);
156 lay.reslabelfont = lay.labelfont;
157 lay.reslabelfont.realize(defaultfont_);
158 return true; // no errors
198 LexerKeyword textClassTags[] = {
199 { "addtopreamble", TC_ADDTOPREAMBLE },
200 { "classoptions", TC_CLASSOPTIONS },
201 { "columns", TC_COLUMNS },
202 { "counter", TC_COUNTER },
203 { "defaultfont", TC_DEFAULTFONT },
204 { "defaultmodule", TC_DEFAULTMODULE },
205 { "defaultstyle", TC_DEFAULTSTYLE },
206 { "excludesmodule", TC_EXCLUDESMODULE },
207 { "float", TC_FLOAT },
208 { "format", TC_FORMAT },
209 { "htmlpreamble", TC_HTMLPREAMBLE },
210 { "input", TC_INPUT },
211 { "insetlayout", TC_INSETLAYOUT },
212 { "leftmargin", TC_LEFTMARGIN },
213 { "nofloat", TC_NOFLOAT },
214 { "nostyle", TC_NOSTYLE },
215 { "outputformat", TC_OUTPUTFORMAT },
216 { "outputtype", TC_OUTPUTTYPE },
217 { "pagestyle", TC_PAGESTYLE },
218 { "preamble", TC_PREAMBLE },
219 { "provides", TC_PROVIDES },
220 { "providesmodule", TC_PROVIDESMODULE },
221 { "requires", TC_REQUIRES },
222 { "rightmargin", TC_RIGHTMARGIN },
223 { "secnumdepth", TC_SECNUMDEPTH },
224 { "sides", TC_SIDES },
225 { "style", TC_STYLE },
226 { "titlelatexname", TC_TITLELATEXNAME },
227 { "titlelatextype", TC_TITLELATEXTYPE },
228 { "tocdepth", TC_TOCDEPTH }
234 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
236 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
237 FileName const tempfile = FileName::tempName("convert_layout");
238 bool success = layout2layout(filename, tempfile);
240 success = readWithoutConv(tempfile, rt) == OK;
241 tempfile.removeFile();
246 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
248 if (!filename.isReadableFile()) {
249 lyxerr << "Cannot read layout file `" << filename << "'."
254 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
255 to_utf8(makeDisplayPath(filename.absFilename())));
257 // Define the plain layout used in table cells, ert, etc. Note that
258 // we do this before loading any layout file, so that classes can
259 // override features of this layout if they should choose to do so.
260 if (rt == BASECLASS && !hasLayout(plain_layout_))
261 layoutlist_.push_back(createBasicLayout(plain_layout_));
263 Lexer lexrc(textClassTags);
264 lexrc.setFile(filename);
265 ReturnValues retval = read(lexrc, rt);
267 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
268 to_utf8(makeDisplayPath(filename.absFilename())));
274 bool TextClass::read(FileName const & filename, ReadType rt)
276 ReturnValues const retval = readWithoutConv(filename, rt);
277 if (retval != FORMAT_MISMATCH)
280 bool const worx = convertLayoutFormat(filename, rt);
282 LYXERR0 ("Unable to convert " << filename <<
283 " to format " << FORMAT);
290 bool TextClass::validate(std::string const & str)
293 return tc.read(str, VALIDATION);
297 bool TextClass::read(std::string const & str, ReadType rt)
299 Lexer lexrc(textClassTags);
300 istringstream is(str);
302 ReturnValues retval = read(lexrc, rt);
304 if (retval != FORMAT_MISMATCH)
307 // write the layout string to a temporary file
308 FileName const tempfile = FileName::tempName("TextClass_read");
309 ofstream os(tempfile.toFilesystemEncoding().c_str());
311 LYXERR0("Unable to create temporary file");
317 // now try to convert it
318 bool const worx = convertLayoutFormat(tempfile, rt);
320 LYXERR0("Unable to convert internal layout information to format "
323 tempfile.removeFile();
328 // Reads a textclass structure from file.
329 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
331 bool error = !lexrc.isOK();
333 // Format of files before the 'Format' tag was introduced
337 while (lexrc.isOK() && !error) {
338 int le = lexrc.lex();
341 case Lexer::LEX_FEOF:
344 case Lexer::LEX_UNDEF:
345 lexrc.printError("Unknown TextClass tag `$$Token'");
353 switch (static_cast<TextClassTags>(le)) {
357 format = lexrc.getInteger();
360 case TC_OUTPUTFORMAT:
362 outputFormat_ = lexrc.getString();
366 readOutputType(lexrc);
367 switch(outputType_) {
369 outputFormat_ = "latex";
372 outputFormat_ = "docbook";
375 outputFormat_ = "literate";
380 case TC_INPUT: // Include file
382 string const inc = lexrc.getString();
383 FileName tmp = libFileSearch("layouts", inc,
387 lexrc.printError("Could not find input file: " + inc);
389 } else if (!read(tmp, MERGE)) {
390 lexrc.printError("Error reading input"
391 "file: " + tmp.absFilename());
397 case TC_DEFAULTSTYLE:
399 docstring const name = from_utf8(subst(lexrc.getString(),
401 defaultlayout_ = name;
407 lexrc.printError("No name given for style: `$$Token'.");
411 docstring const name = from_utf8(subst(lexrc.getString(),
414 string s = "Could not read name for style: `$$Token' "
415 + lexrc.getString() + " is probably not valid UTF-8!";
416 lexrc.printError(s.c_str());
418 // Since we couldn't read the name, we just scan the rest
419 // of the style and discard it.
420 error = !readStyle(lexrc, lay);
421 } else if (hasLayout(name)) {
422 Layout & lay = operator[](name);
423 error = !readStyle(lexrc, lay);
426 layout.setName(name);
427 error = !readStyle(lexrc, layout);
429 layoutlist_.push_back(layout);
431 if (defaultlayout_.empty()) {
432 // We do not have a default layout yet, so we choose
433 // the first layout we encounter.
434 defaultlayout_ = name;
442 docstring const style = from_utf8(subst(lexrc.getString(),
444 if (!deleteLayout(style))
445 lyxerr << "Cannot delete style `"
446 << to_utf8(style) << '\'' << endl;
452 columns_ = lexrc.getInteger();
457 switch (lexrc.getInteger()) {
458 case 1: sides_ = OneSide; break;
459 case 2: sides_ = TwoSides; break;
461 lyxerr << "Impossible number of page"
462 " sides, setting to one."
472 pagestyle_ = rtrim(lexrc.getString());
476 defaultfont_ = lyxRead(lexrc);
477 if (!defaultfont_.resolved()) {
478 lexrc.printError("Warning: defaultfont should "
479 "be fully instantiated!");
480 defaultfont_.realize(sane_font);
486 secnumdepth_ = lexrc.getInteger();
491 tocdepth_ = lexrc.getInteger();
494 // First step to support options
495 case TC_CLASSOPTIONS:
496 readClassOptions(lexrc);
500 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
503 case TC_HTMLPREAMBLE:
504 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
507 case TC_ADDTOPREAMBLE:
508 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
513 string const feature = lexrc.getString();
515 if (lexrc.getInteger())
516 provides_.insert(feature);
518 provides_.erase(feature);
524 vector<string> const req
525 = getVectorFromString(lexrc.getString());
526 requires_.insert(req.begin(), req.end());
530 case TC_DEFAULTMODULE: {
532 string const module = lexrc.getString();
533 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
534 default_modules_.push_back(module);
538 case TC_PROVIDESMODULE: {
540 string const module = lexrc.getString();
541 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
542 provided_modules_.push_back(module);
546 case TC_EXCLUDESMODULE: {
548 string const module = lexrc.getString();
549 // modules already have their own way to exclude other modules
551 LYXERR0("ExcludesModule tag cannot be used in a module!");
554 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
555 excluded_modules_.push_back(module);
559 case TC_LEFTMARGIN: // left margin type
561 leftmargin_ = lexrc.getDocString();
564 case TC_RIGHTMARGIN: // right margin type
566 rightmargin_ = lexrc.getDocString();
569 case TC_INSETLAYOUT: {
571 lexrc.printError("No name given for InsetLayout: `$$Token'.");
575 docstring const name = subst(lexrc.getDocString(), '_', ' ');
577 string s = "Could not read name for InsetLayout: `$$Token' "
578 + lexrc.getString() + " is probably not valid UTF-8!";
579 lexrc.printError(s.c_str());
581 // Since we couldn't read the name, we just scan the rest
582 // of the style and discard it.
583 il.read(lexrc, *this);
585 } else if (hasInsetLayout(name)) {
586 InsetLayout & il = insetlayoutlist_[name];
587 error = !il.read(lexrc, *this);
591 error = !il.read(lexrc, *this);
593 insetlayoutlist_[name] = il;
604 docstring const name = lexrc.getDocString();
606 string s = "Could not read name for counter: `$$Token' "
607 + lexrc.getString() + " is probably not valid UTF-8!";
608 lexrc.printError(s.c_str());
610 // Since we couldn't read the name, we just scan the rest
614 error = !counters_.read(lexrc, name);
617 lexrc.printError("No name given for style: `$$Token'.");
622 case TC_TITLELATEXTYPE:
623 readTitleType(lexrc);
626 case TC_TITLELATEXNAME:
628 titlename_ = lexrc.getString();
633 string const nofloat = lexrc.getString();
634 floatlist_.erase(nofloat);
639 //Note that this is triggered the first time through the loop unless
640 //we hit a format tag.
641 if (format != FORMAT)
645 if (format != FORMAT)
646 return FORMAT_MISMATCH;
649 return (error ? ERROR : OK);
651 if (defaultlayout_.empty()) {
652 LYXERR0("Error: Textclass '" << name_
653 << "' is missing a defaultstyle.");
657 // Try to erase "stdinsets" from the provides_ set.
659 // Provides stdinsets 1
660 // declaration simply tells us that the standard insets have been
661 // defined. (It's found in stdinsets.inc but could also be used in
662 // user-defined files.) There isn't really any such package. So we
663 // might as well go ahead and erase it.
664 // If we do not succeed, then it was not there, which means that
665 // the textclass did not provide the definitions of the standard
666 // insets. So we need to try to load them.
667 int erased = provides_.erase("stdinsets");
669 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
672 throw ExceptionMessage(WarningException, _("Missing File"),
673 _("Could not find stdinsets.inc! This may lead to data loss!"));
675 } else if (!read(tmp, MERGE)) {
676 throw ExceptionMessage(WarningException, _("Corrupt File"),
677 _("Could not read stdinsets.inc! This may lead to data loss!"));
682 min_toclevel_ = Layout::NOT_IN_TOC;
683 max_toclevel_ = Layout::NOT_IN_TOC;
684 const_iterator lit = begin();
685 const_iterator len = end();
686 for (; lit != len; ++lit) {
687 int const toclevel = lit->toclevel;
688 if (toclevel != Layout::NOT_IN_TOC) {
689 if (min_toclevel_ == Layout::NOT_IN_TOC)
690 min_toclevel_ = toclevel;
692 min_toclevel_ = min(min_toclevel_, toclevel);
693 max_toclevel_ = max(max_toclevel_, toclevel);
696 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
697 << ", maximum is " << max_toclevel_);
699 return (error ? ERROR : OK);
703 void TextClass::readTitleType(Lexer & lexrc)
705 LexerKeyword titleTypeTags[] = {
706 { "commandafter", TITLE_COMMAND_AFTER },
707 { "environment", TITLE_ENVIRONMENT }
710 PushPopHelper pph(lexrc, titleTypeTags);
712 int le = lexrc.lex();
714 case Lexer::LEX_UNDEF:
715 lexrc.printError("Unknown output type `$$Token'");
717 case TITLE_COMMAND_AFTER:
718 case TITLE_ENVIRONMENT:
719 titletype_ = static_cast<TitleLatexType>(le);
722 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
728 void TextClass::readOutputType(Lexer & lexrc)
730 LexerKeyword outputTypeTags[] = {
731 { "docbook", DOCBOOK },
733 { "literate", LITERATE }
736 PushPopHelper pph(lexrc, outputTypeTags);
738 int le = lexrc.lex();
740 case Lexer::LEX_UNDEF:
741 lexrc.printError("Unknown output type `$$Token'");
746 outputType_ = static_cast<OutputType>(le);
749 LYXERR0("Unhandled value " << le);
755 void TextClass::readClassOptions(Lexer & lexrc)
765 LexerKeyword classOptionsTags[] = {
767 {"fontsize", CO_FONTSIZE },
768 {"header", CO_HEADER },
769 {"other", CO_OTHER },
770 {"pagestyle", CO_PAGESTYLE }
773 lexrc.pushTable(classOptionsTags);
775 while (!getout && lexrc.isOK()) {
776 int le = lexrc.lex();
778 case Lexer::LEX_UNDEF:
779 lexrc.printError("Unknown ClassOption tag `$$Token'");
786 opt_fontsize_ = rtrim(lexrc.getString());
790 opt_pagestyle_ = rtrim(lexrc.getString());
794 options_ = lexrc.getString();
798 class_header_ = subst(lexrc.getString(), """, "\"");
809 void TextClass::readFloat(Lexer & lexrc)
823 LexerKeyword floatTags[] = {
825 { "extension", FT_EXT },
826 { "guiname", FT_NAME },
827 { "latexbuiltin", FT_BUILTIN },
828 { "listname", FT_LISTNAME },
829 { "numberwithin", FT_WITHIN },
830 { "placement", FT_PLACEMENT },
831 { "style", FT_STYLE },
835 lexrc.pushTable(floatTags);
844 bool builtin = false;
847 while (!getout && lexrc.isOK()) {
848 int le = lexrc.lex();
850 case Lexer::LEX_UNDEF:
851 lexrc.printError("Unknown float tag `$$Token'");
858 type = lexrc.getString();
859 if (floatlist_.typeExist(type)) {
860 Floating const & fl = floatlist_.getType(type);
861 placement = fl.placement();
863 within = fl.within();
866 listName = fl.listName();
867 builtin = fl.builtin();
872 name = lexrc.getString();
876 placement = lexrc.getString();
880 ext = lexrc.getString();
884 within = lexrc.getString();
885 if (within == "none")
890 style = lexrc.getString();
894 listName = lexrc.getString();
898 builtin = lexrc.getBool();
906 // Here if have a full float if getout == true
908 Floating fl(type, placement, ext, within,
909 style, name, listName, builtin);
910 floatlist_.newFloat(fl);
911 // each float has its own counter
912 counters_.newCounter(from_ascii(type), from_ascii(within),
913 docstring(), docstring());
914 // also define sub-float counters
915 docstring const subtype = "sub-" + from_ascii(type);
916 counters_.newCounter(subtype, from_ascii(type),
917 "\\alph{" + subtype + "}", docstring());
924 bool TextClass::hasLayout(docstring const & n) const
926 docstring const name = n.empty() ? defaultLayoutName() : n;
928 return find_if(layoutlist_.begin(), layoutlist_.end(),
929 LayoutNamesEqual(name))
930 != layoutlist_.end();
934 bool TextClass::hasInsetLayout(docstring const & n) const
938 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
939 InsetLayouts::const_iterator en = insetlayoutlist_.end();
940 for (; it != en; ++it)
947 Layout const & TextClass::operator[](docstring const & name) const
949 LASSERT(!name.empty(), /**/);
952 find_if(begin(), end(), LayoutNamesEqual(name));
955 lyxerr << "We failed to find the layout '" << to_utf8(name)
956 << "' in the layout list. You MUST investigate!"
958 for (const_iterator cit = begin(); cit != end(); ++cit)
959 lyxerr << " " << to_utf8(cit->name()) << endl;
961 // we require the name to exist
962 LASSERT(false, /**/);
969 Layout & TextClass::operator[](docstring const & name)
971 LASSERT(!name.empty(), /**/);
973 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
976 LYXERR0("We failed to find the layout '" << to_utf8(name)
977 << "' in the layout list. You MUST investigate!");
978 for (const_iterator cit = begin(); cit != end(); ++cit)
979 LYXERR0(" " << to_utf8(cit->name()));
981 // we require the name to exist
982 LASSERT(false, /**/);
989 bool TextClass::deleteLayout(docstring const & name)
991 if (name == defaultLayoutName() || name == plainLayoutName())
994 LayoutList::iterator it =
995 remove_if(layoutlist_.begin(), layoutlist_.end(),
996 LayoutNamesEqual(name));
998 LayoutList::iterator end = layoutlist_.end();
999 bool const ret = (it != end);
1000 layoutlist_.erase(it, end);
1005 // Load textclass info if not loaded yet
1006 bool TextClass::load(string const & path) const
1011 // Read style-file, provided path is searched before the system ones
1012 // If path is a file, it is loaded directly.
1013 FileName layout_file(path);
1014 if (!path.empty() && !layout_file.isReadableFile())
1015 layout_file = FileName(addName(path, name_ + ".layout"));
1016 if (layout_file.empty() || !layout_file.exists())
1017 layout_file = libFileSearch("layouts", name_, "layout");
1018 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1021 lyxerr << "Error reading `"
1022 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1023 << "'\n(Check `" << name_
1024 << "')\nCheck your installation and "
1025 "try Options/Reconfigure..." << endl;
1032 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1035 layoutlist_.push_back(createBasicLayout(n, true));
1039 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1041 // FIXME The fix for the InsetLayout part of 4812 would be here:
1042 // Add the InsetLayout to the document class if it is not found.
1044 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1045 while (!n.empty()) {
1046 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1047 if (cit != cen && cit->first == n)
1049 size_t i = n.find(':');
1050 if (i == string::npos)
1054 return plain_insetlayout_;
1058 docstring const & TextClass::defaultLayoutName() const
1060 // This really should come from the actual layout... (Lgb)
1061 return defaultlayout_;
1065 Layout const & TextClass::defaultLayout() const
1067 return operator[](defaultLayoutName());
1071 bool TextClass::isDefaultLayout(Layout const & layout) const
1073 return layout.name() == defaultLayoutName();
1077 bool TextClass::isPlainLayout(Layout const & layout) const
1079 return layout.name() == plainLayoutName();
1083 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1085 static Layout * defaultLayout = NULL;
1087 if (defaultLayout) {
1088 defaultLayout->setUnknown(unknown);
1089 defaultLayout->setName(name);
1090 return *defaultLayout;
1093 static char const * s = "Margin Static\n"
1094 "LatexType Paragraph\n"
1097 "AlignPossible Left, Right, Center\n"
1098 "LabelType No_Label\n"
1100 istringstream ss(s);
1101 Lexer lex(textClassTags);
1103 defaultLayout = new Layout;
1104 defaultLayout->setUnknown(unknown);
1105 defaultLayout->setName(name);
1106 if (!readStyle(lex, *defaultLayout)) {
1107 // The only way this happens is because the hardcoded layout above
1109 LASSERT(false, /**/);
1111 return *defaultLayout;
1114 /////////////////////////////////////////////////////////////////////////
1116 // DocumentClassBundle
1118 /////////////////////////////////////////////////////////////////////////
1120 DocumentClassBundle::~DocumentClassBundle()
1122 for (size_t i = 0; i != documentClasses_.size(); ++i)
1123 delete documentClasses_[i];
1124 documentClasses_.clear();
1127 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1129 DocumentClass * dc = new DocumentClass(baseClass);
1130 documentClasses_.push_back(dc);
1131 return *documentClasses_.back();
1135 DocumentClassBundle & DocumentClassBundle::get()
1137 static DocumentClassBundle singleton;
1142 DocumentClass & DocumentClassBundle::makeDocumentClass(
1143 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1145 DocumentClass & doc_class = newClass(baseClass);
1146 LayoutModuleList::const_iterator it = modlist.begin();
1147 LayoutModuleList::const_iterator en = modlist.end();
1148 for (; it != en; it++) {
1149 string const modName = *it;
1150 LyXModule * lm = moduleList[modName];
1152 docstring const msg =
1153 bformat(_("The module %1$s has been requested by\n"
1154 "this document but has not been found in the list of\n"
1155 "available modules. If you recently installed it, you\n"
1156 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1157 ExceptionMessage(WarningException,_("Module not available"),
1158 msg + _("Some layouts may not be available."));
1161 if (!lm->isAvailable()) {
1162 docstring const msg =
1163 bformat(_("The module %1$s requires a package that is\n"
1164 "not available in your LaTeX installation. LaTeX output\n"
1165 "may not be possible.\n"), from_utf8(modName));
1166 ExceptionMessage(WarningException, _("Package not available"), msg);
1168 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1169 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1170 docstring const msg =
1171 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1172 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1179 /////////////////////////////////////////////////////////////////////////
1183 /////////////////////////////////////////////////////////////////////////
1185 DocumentClass::DocumentClass(LayoutFile const & tc)
1190 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1192 LayoutList::const_iterator it = layoutlist_.begin();
1193 LayoutList::const_iterator end = layoutlist_.end();
1194 for (; it != end; ++it)
1195 if (it->latexname() == lay)
1201 bool DocumentClass::provides(string const & p) const
1203 return provides_.find(p) != provides_.end();
1207 bool DocumentClass::hasTocLevels() const
1209 return min_toclevel_ != Layout::NOT_IN_TOC;
1213 /////////////////////////////////////////////////////////////////////////
1217 /////////////////////////////////////////////////////////////////////////
1219 ostream & operator<<(ostream & os, PageSides p)