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/FileName.h"
34 #include "support/filetools.h"
35 #include "support/gettext.h"
36 #include "support/lstrings.h"
37 #include "support/os.h"
48 using namespace lyx::support;
54 class LayoutNamesEqual : public unary_function<Layout, bool> {
56 LayoutNamesEqual(docstring const & name)
59 bool operator()(Layout const & c) const
61 return c.name() == name_;
67 // Keep the changes documented in the Customization manual.
68 int const FORMAT = 26;
71 bool layout2layout(FileName const & filename, FileName const & tempfile)
73 FileName const script = libFileSearch("scripts", "layout2layout.py");
75 LYXERR0("Could not find layout conversion "
76 "script layout2layout.py.");
80 ostringstream command;
81 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
82 << ' ' << quoteName(filename.toFilesystemEncoding())
83 << ' ' << quoteName(tempfile.toFilesystemEncoding());
84 string const command_str = command.str();
86 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
88 cmd_ret const ret = runCommand(command_str);
90 LYXERR0("Could not run layout conversion script layout2layout.py.");
97 string translateRT(TextClass::ReadType rt)
100 case TextClass::BASECLASS:
102 case TextClass::MERGE:
104 case TextClass::MODULE:
105 return "module file";
106 case TextClass::VALIDATION:
116 // This string should not be translated here,
117 // because it is a layout identifier.
118 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
121 InsetLayout DocumentClass::plain_insetlayout_;
124 /////////////////////////////////////////////////////////////////////////
128 /////////////////////////////////////////////////////////////////////////
130 TextClass::TextClass()
133 outputFormat_ = "latex";
138 pagestyle_ = "default";
139 defaultfont_ = sane_font;
140 opt_fontsize_ = "10|11|12";
141 opt_pagestyle_ = "empty|plain|headings|fancy";
142 titletype_ = TITLE_COMMAND_AFTER;
143 titlename_ = "maketitle";
145 _("Plain Layout"); // a hack to make this translatable
149 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
151 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
152 if (!lay.read(lexrc, *this)) {
153 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
157 lay.resfont = lay.font;
158 lay.resfont.realize(defaultfont_);
159 lay.reslabelfont = lay.labelfont;
160 lay.reslabelfont.realize(defaultfont_);
161 return true; // no errors
195 TC_ADDTOHTMLPREAMBLE,
206 LexerKeyword textClassTags[] = {
207 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
208 { "addtopreamble", TC_ADDTOPREAMBLE },
209 { "citeformat", TC_CITEFORMAT },
210 { "classoptions", TC_CLASSOPTIONS },
211 { "columns", TC_COLUMNS },
212 { "counter", TC_COUNTER },
213 { "defaultfont", TC_DEFAULTFONT },
214 { "defaultmodule", TC_DEFAULTMODULE },
215 { "defaultstyle", TC_DEFAULTSTYLE },
216 { "excludesmodule", TC_EXCLUDESMODULE },
217 { "float", TC_FLOAT },
218 { "format", TC_FORMAT },
219 { "htmlpreamble", TC_HTMLPREAMBLE },
220 { "htmltocsection", TC_HTMLTOCSECTION },
221 { "ifcounter", TC_IFCOUNTER },
222 { "ifstyle", TC_IFSTYLE },
223 { "input", TC_INPUT },
224 { "insetlayout", TC_INSETLAYOUT },
225 { "leftmargin", TC_LEFTMARGIN },
226 { "nofloat", TC_NOFLOAT },
227 { "nostyle", TC_NOSTYLE },
228 { "outputformat", TC_OUTPUTFORMAT },
229 { "outputtype", TC_OUTPUTTYPE },
230 { "pagestyle", TC_PAGESTYLE },
231 { "preamble", TC_PREAMBLE },
232 { "provides", TC_PROVIDES },
233 { "providesmodule", TC_PROVIDESMODULE },
234 { "requires", TC_REQUIRES },
235 { "rightmargin", TC_RIGHTMARGIN },
236 { "secnumdepth", TC_SECNUMDEPTH },
237 { "sides", TC_SIDES },
238 { "style", TC_STYLE },
239 { "titlelatexname", TC_TITLELATEXNAME },
240 { "titlelatextype", TC_TITLELATEXTYPE },
241 { "tocdepth", TC_TOCDEPTH }
247 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
249 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
250 FileName const tempfile = FileName::tempName("convert_layout");
251 bool success = layout2layout(filename, tempfile);
253 success = readWithoutConv(tempfile, rt) == OK;
254 tempfile.removeFile();
259 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
261 if (!filename.isReadableFile()) {
262 lyxerr << "Cannot read layout file `" << filename << "'."
267 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
268 to_utf8(makeDisplayPath(filename.absFilename())));
270 // Define the plain layout used in table cells, ert, etc. Note that
271 // we do this before loading any layout file, so that classes can
272 // override features of this layout if they should choose to do so.
273 if (rt == BASECLASS && !hasLayout(plain_layout_))
274 layoutlist_.push_back(createBasicLayout(plain_layout_));
276 Lexer lexrc(textClassTags);
277 lexrc.setFile(filename);
278 ReturnValues retval = read(lexrc, rt);
280 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
281 to_utf8(makeDisplayPath(filename.absFilename())));
287 bool TextClass::read(FileName const & filename, ReadType rt)
289 ReturnValues const retval = readWithoutConv(filename, rt);
290 if (retval != FORMAT_MISMATCH)
293 bool const worx = convertLayoutFormat(filename, rt);
295 LYXERR0 ("Unable to convert " << filename <<
296 " to format " << FORMAT);
303 bool TextClass::validate(std::string const & str)
306 return tc.read(str, VALIDATION);
310 bool TextClass::read(std::string const & str, ReadType rt)
312 Lexer lexrc(textClassTags);
313 istringstream is(str);
315 ReturnValues retval = read(lexrc, rt);
317 if (retval != FORMAT_MISMATCH)
320 // write the layout string to a temporary file
321 FileName const tempfile = FileName::tempName("TextClass_read");
322 ofstream os(tempfile.toFilesystemEncoding().c_str());
324 LYXERR0("Unable to create temporary file");
330 // now try to convert it
331 bool const worx = convertLayoutFormat(tempfile, rt);
333 LYXERR0("Unable to convert internal layout information to format "
336 tempfile.removeFile();
341 // Reads a textclass structure from file.
342 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
344 bool error = !lexrc.isOK();
346 // Format of files before the 'Format' tag was introduced
350 while (lexrc.isOK() && !error) {
351 int le = lexrc.lex();
354 case Lexer::LEX_FEOF:
357 case Lexer::LEX_UNDEF:
358 lexrc.printError("Unknown TextClass tag `$$Token'");
366 // used below to track whether we are in an IfStyle or IfCounter tag.
367 bool ifstyle = false;
368 bool ifcounter = false;
370 switch (static_cast<TextClassTags>(le)) {
374 format = lexrc.getInteger();
377 case TC_OUTPUTFORMAT:
379 outputFormat_ = lexrc.getString();
383 readOutputType(lexrc);
384 switch(outputType_) {
386 outputFormat_ = "latex";
389 outputFormat_ = "docbook";
392 outputFormat_ = "literate";
397 case TC_INPUT: // Include file
399 string const inc = lexrc.getString();
400 FileName tmp = libFileSearch("layouts", inc,
404 lexrc.printError("Could not find input file: " + inc);
406 } else if (!read(tmp, MERGE)) {
407 lexrc.printError("Error reading input"
408 "file: " + tmp.absFilename());
414 case TC_DEFAULTSTYLE:
416 docstring const name = from_utf8(subst(lexrc.getString(),
418 defaultlayout_ = name;
427 lexrc.printError("No name given for style: `$$Token'.");
431 docstring const name = from_utf8(subst(lexrc.getString(),
434 string s = "Could not read name for style: `$$Token' "
435 + lexrc.getString() + " is probably not valid UTF-8!";
436 lexrc.printError(s.c_str());
438 // Since we couldn't read the name, we just scan the rest
439 // of the style and discard it.
440 error = !readStyle(lexrc, lay);
441 } else if (hasLayout(name)) {
442 Layout & lay = operator[](name);
443 error = !readStyle(lexrc, lay);
444 } else if (!ifstyle) {
446 layout.setName(name);
447 error = !readStyle(lexrc, layout);
449 layoutlist_.push_back(layout);
451 if (defaultlayout_.empty()) {
452 // We do not have a default layout yet, so we choose
453 // the first layout we encounter.
454 defaultlayout_ = name;
458 // scan the rest and discard it
460 readStyle(lexrc, lay);
471 docstring const style = from_utf8(subst(lexrc.getString(),
473 if (!deleteLayout(style))
474 lyxerr << "Cannot delete style `"
475 << to_utf8(style) << '\'' << endl;
481 columns_ = lexrc.getInteger();
486 switch (lexrc.getInteger()) {
487 case 1: sides_ = OneSide; break;
488 case 2: sides_ = TwoSides; break;
490 lyxerr << "Impossible number of page"
491 " sides, setting to one."
501 pagestyle_ = rtrim(lexrc.getString());
505 defaultfont_ = lyxRead(lexrc);
506 if (!defaultfont_.resolved()) {
507 lexrc.printError("Warning: defaultfont should "
508 "be fully instantiated!");
509 defaultfont_.realize(sane_font);
515 secnumdepth_ = lexrc.getInteger();
520 tocdepth_ = lexrc.getInteger();
523 // First step to support options
524 case TC_CLASSOPTIONS:
525 readClassOptions(lexrc);
529 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
532 case TC_HTMLPREAMBLE:
533 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
536 case TC_HTMLTOCSECTION:
537 html_toc_section_ = from_utf8(trim(lexrc.getString()));
540 case TC_ADDTOPREAMBLE:
541 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
544 case TC_ADDTOHTMLPREAMBLE:
545 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
550 string const feature = lexrc.getString();
552 if (lexrc.getInteger())
553 provides_.insert(feature);
555 provides_.erase(feature);
561 vector<string> const req
562 = getVectorFromString(lexrc.getString());
563 requires_.insert(req.begin(), req.end());
567 case TC_DEFAULTMODULE: {
569 string const module = lexrc.getString();
570 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
571 default_modules_.push_back(module);
575 case TC_PROVIDESMODULE: {
577 string const module = lexrc.getString();
578 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
579 provided_modules_.push_back(module);
583 case TC_EXCLUDESMODULE: {
585 string const module = lexrc.getString();
586 // modules already have their own way to exclude other modules
588 LYXERR0("ExcludesModule tag cannot be used in a module!");
591 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
592 excluded_modules_.push_back(module);
596 case TC_LEFTMARGIN: // left margin type
598 leftmargin_ = lexrc.getDocString();
601 case TC_RIGHTMARGIN: // right margin type
603 rightmargin_ = lexrc.getDocString();
606 case TC_INSETLAYOUT: {
608 lexrc.printError("No name given for InsetLayout: `$$Token'.");
612 docstring const name = subst(lexrc.getDocString(), '_', ' ');
614 string s = "Could not read name for InsetLayout: `$$Token' "
615 + lexrc.getString() + " is probably not valid UTF-8!";
616 lexrc.printError(s.c_str());
618 // Since we couldn't read the name, we just scan the rest
619 // of the style and discard it.
620 il.read(lexrc, *this);
622 } else if (hasInsetLayout(name)) {
623 InsetLayout & il = insetlayoutlist_[name];
624 error = !il.read(lexrc, *this);
628 error = !il.read(lexrc, *this);
630 insetlayoutlist_[name] = il;
640 readCiteFormat(lexrc);
647 docstring const name = lexrc.getDocString();
649 string s = "Could not read name for counter: `$$Token' "
650 + lexrc.getString() + " is probably not valid UTF-8!";
651 lexrc.printError(s.c_str());
653 // Since we couldn't read the name, we just scan the rest
657 error = !counters_.read(lexrc, name, !ifcounter);
660 lexrc.printError("No name given for style: `$$Token'.");
667 case TC_TITLELATEXTYPE:
668 readTitleType(lexrc);
671 case TC_TITLELATEXNAME:
673 titlename_ = lexrc.getString();
678 string const nofloat = lexrc.getString();
679 floatlist_.erase(nofloat);
684 //Note that this is triggered the first time through the loop unless
685 //we hit a format tag.
686 if (format != FORMAT)
690 if (format != FORMAT)
691 return FORMAT_MISMATCH;
694 return (error ? ERROR : OK);
696 if (defaultlayout_.empty()) {
697 LYXERR0("Error: Textclass '" << name_
698 << "' is missing a defaultstyle.");
702 // Try to erase "stdinsets" from the provides_ set.
704 // Provides stdinsets 1
705 // declaration simply tells us that the standard insets have been
706 // defined. (It's found in stdinsets.inc but could also be used in
707 // user-defined files.) There isn't really any such package. So we
708 // might as well go ahead and erase it.
709 // If we do not succeed, then it was not there, which means that
710 // the textclass did not provide the definitions of the standard
711 // insets. So we need to try to load them.
712 int erased = provides_.erase("stdinsets");
714 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
717 frontend::Alert::warning(_("Missing File"),
718 _("Could not find stdinsets.inc! This may lead to data loss!"));
720 } else if (!read(tmp, MERGE)) {
721 frontend::Alert::warning(_("Corrupt File"),
722 _("Could not read stdinsets.inc! This may lead to data loss!"));
727 min_toclevel_ = Layout::NOT_IN_TOC;
728 max_toclevel_ = Layout::NOT_IN_TOC;
729 const_iterator lit = begin();
730 const_iterator len = end();
731 for (; lit != len; ++lit) {
732 int const toclevel = lit->toclevel;
733 if (toclevel != Layout::NOT_IN_TOC) {
734 if (min_toclevel_ == Layout::NOT_IN_TOC)
735 min_toclevel_ = toclevel;
737 min_toclevel_ = min(min_toclevel_, toclevel);
738 max_toclevel_ = max(max_toclevel_, toclevel);
741 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
742 << ", maximum is " << max_toclevel_);
744 return (error ? ERROR : OK);
748 void TextClass::readTitleType(Lexer & lexrc)
750 LexerKeyword titleTypeTags[] = {
751 { "commandafter", TITLE_COMMAND_AFTER },
752 { "environment", TITLE_ENVIRONMENT }
755 PushPopHelper pph(lexrc, titleTypeTags);
757 int le = lexrc.lex();
759 case Lexer::LEX_UNDEF:
760 lexrc.printError("Unknown output type `$$Token'");
762 case TITLE_COMMAND_AFTER:
763 case TITLE_ENVIRONMENT:
764 titletype_ = static_cast<TitleLatexType>(le);
767 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
773 void TextClass::readOutputType(Lexer & lexrc)
775 LexerKeyword outputTypeTags[] = {
776 { "docbook", DOCBOOK },
778 { "literate", LITERATE }
781 PushPopHelper pph(lexrc, outputTypeTags);
783 int le = lexrc.lex();
785 case Lexer::LEX_UNDEF:
786 lexrc.printError("Unknown output type `$$Token'");
791 outputType_ = static_cast<OutputType>(le);
794 LYXERR0("Unhandled value " << le);
800 void TextClass::readClassOptions(Lexer & lexrc)
810 LexerKeyword classOptionsTags[] = {
812 {"fontsize", CO_FONTSIZE },
813 {"header", CO_HEADER },
814 {"other", CO_OTHER },
815 {"pagestyle", CO_PAGESTYLE }
818 lexrc.pushTable(classOptionsTags);
820 while (!getout && lexrc.isOK()) {
821 int le = lexrc.lex();
823 case Lexer::LEX_UNDEF:
824 lexrc.printError("Unknown ClassOption tag `$$Token'");
831 opt_fontsize_ = rtrim(lexrc.getString());
835 opt_pagestyle_ = rtrim(lexrc.getString());
839 if (options_.empty())
840 options_ = lexrc.getString();
842 options_ += ',' + lexrc.getString();
846 class_header_ = subst(lexrc.getString(), """, "\"");
857 void TextClass::readCiteFormat(Lexer & lexrc)
861 while (lexrc.isOK()) {
863 etype = lexrc.getString();
864 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
867 definition = lexrc.getString();
868 char initchar = etype[0];
871 if (initchar == '!' || initchar == '_')
872 cite_macros_[etype] = definition;
874 cite_formats_[etype] = definition;
879 void TextClass::readFloat(Lexer & lexrc)
898 LexerKeyword floatTags[] = {
900 { "extension", FT_EXT },
901 { "guiname", FT_NAME },
902 { "htmlattr", FT_HTMLATTR },
903 { "htmlstyle", FT_HTMLSTYLE },
904 { "htmltag", FT_HTMLTAG },
905 { "listcommand", FT_LISTCOMMAND },
906 { "listname", FT_LISTNAME },
907 { "needsfloatpkg", FT_NEEDSFLOAT },
908 { "numberwithin", FT_WITHIN },
909 { "placement", FT_PLACEMENT },
910 { "refprefix", FT_REFPREFIX },
911 { "style", FT_STYLE },
915 lexrc.pushTable(floatTags);
929 bool needsfloat = true;
932 while (!getout && lexrc.isOK()) {
933 int le = lexrc.lex();
935 case Lexer::LEX_UNDEF:
936 lexrc.printError("Unknown float tag `$$Token'");
943 type = lexrc.getString();
944 if (floatlist_.typeExist(type)) {
945 Floating const & fl = floatlist_.getType(type);
946 placement = fl.placement();
948 within = fl.within();
951 listname = fl.listName();
952 needsfloat = fl.needsFloatPkg();
953 listcommand = fl.listCommand();
954 refprefix = fl.refPrefix();
959 name = lexrc.getString();
963 placement = lexrc.getString();
967 ext = lexrc.getString();
971 within = lexrc.getString();
972 if (within == "none")
977 style = lexrc.getString();
981 listcommand = lexrc.getString();
985 refprefix = lexrc.getString();
989 listname = lexrc.getString();
993 needsfloat = lexrc.getBool();
997 htmlattr = lexrc.getString();
1001 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1005 htmltag = lexrc.getString();
1013 // Here we have a full float if getout == true
1015 if (!needsfloat && listcommand.empty())
1016 LYXERR0("The layout does not provide a list command " <<
1017 "for the builtin float `" << type << "'. LyX will " <<
1018 "not be able to produce a float list.");
1019 Floating fl(type, placement, ext, within, style, name,
1020 listname, listcommand, refprefix,
1021 htmltag, htmlattr, htmlstyle, needsfloat);
1022 floatlist_.newFloat(fl);
1023 // each float has its own counter
1024 counters_.newCounter(from_ascii(type), from_ascii(within),
1025 docstring(), docstring());
1026 // also define sub-float counters
1027 docstring const subtype = "sub-" + from_ascii(type);
1028 counters_.newCounter(subtype, from_ascii(type),
1029 "\\alph{" + subtype + "}", docstring());
1036 string const & TextClass::prerequisites() const
1038 if (contains(prerequisites_, ',')) {
1039 vector<string> const pres = getVectorFromString(prerequisites_);
1040 prerequisites_ = getStringFromVector(pres, "\n\t");
1042 return prerequisites_;
1045 bool TextClass::hasLayout(docstring const & n) const
1047 docstring const name = n.empty() ? defaultLayoutName() : n;
1049 return find_if(layoutlist_.begin(), layoutlist_.end(),
1050 LayoutNamesEqual(name))
1051 != layoutlist_.end();
1055 bool TextClass::hasInsetLayout(docstring const & n) const
1059 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1060 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1061 for (; it != en; ++it)
1068 Layout const & TextClass::operator[](docstring const & name) const
1070 LASSERT(!name.empty(), /**/);
1073 find_if(begin(), end(), LayoutNamesEqual(name));
1076 lyxerr << "We failed to find the layout '" << to_utf8(name)
1077 << "' in the layout list. You MUST investigate!"
1079 for (const_iterator cit = begin(); cit != end(); ++cit)
1080 lyxerr << " " << to_utf8(cit->name()) << endl;
1082 // we require the name to exist
1083 LASSERT(false, /**/);
1090 Layout & TextClass::operator[](docstring const & name)
1092 LASSERT(!name.empty(), /**/);
1094 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1097 LYXERR0("We failed to find the layout '" << to_utf8(name)
1098 << "' in the layout list. You MUST investigate!");
1099 for (const_iterator cit = begin(); cit != end(); ++cit)
1100 LYXERR0(" " << to_utf8(cit->name()));
1102 // we require the name to exist
1103 LASSERT(false, /**/);
1110 bool TextClass::deleteLayout(docstring const & name)
1112 if (name == defaultLayoutName() || name == plainLayoutName())
1115 LayoutList::iterator it =
1116 remove_if(layoutlist_.begin(), layoutlist_.end(),
1117 LayoutNamesEqual(name));
1119 LayoutList::iterator end = layoutlist_.end();
1120 bool const ret = (it != end);
1121 layoutlist_.erase(it, end);
1126 // Load textclass info if not loaded yet
1127 bool TextClass::load(string const & path) const
1132 // Read style-file, provided path is searched before the system ones
1133 // If path is a file, it is loaded directly.
1134 FileName layout_file(path);
1135 if (!path.empty() && !layout_file.isReadableFile())
1136 layout_file = FileName(addName(path, name_ + ".layout"));
1137 if (layout_file.empty() || !layout_file.exists())
1138 layout_file = libFileSearch("layouts", name_, "layout");
1139 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1142 lyxerr << "Error reading `"
1143 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1144 << "'\n(Check `" << name_
1145 << "')\nCheck your installation and "
1146 "try Options/Reconfigure..."
1154 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1157 layoutlist_.push_back(createBasicLayout(n, true));
1161 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1163 // FIXME The fix for the InsetLayout part of 4812 would be here:
1164 // Add the InsetLayout to the document class if it is not found.
1166 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1167 while (!n.empty()) {
1168 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1169 if (cit != cen && cit->first == n)
1171 size_t i = n.find(':');
1172 if (i == string::npos)
1176 return plain_insetlayout_;
1180 docstring const & TextClass::defaultLayoutName() const
1182 // This really should come from the actual layout... (Lgb)
1183 return defaultlayout_;
1187 Layout const & TextClass::defaultLayout() const
1189 return operator[](defaultLayoutName());
1193 bool TextClass::isDefaultLayout(Layout const & layout) const
1195 return layout.name() == defaultLayoutName();
1199 bool TextClass::isPlainLayout(Layout const & layout) const
1201 return layout.name() == plainLayoutName();
1205 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1207 static Layout * defaultLayout = NULL;
1209 if (defaultLayout) {
1210 defaultLayout->setUnknown(unknown);
1211 defaultLayout->setName(name);
1212 return *defaultLayout;
1215 static char const * s = "Margin Static\n"
1216 "LatexType Paragraph\n"
1219 "AlignPossible Left, Right, Center\n"
1220 "LabelType No_Label\n"
1222 istringstream ss(s);
1223 Lexer lex(textClassTags);
1225 defaultLayout = new Layout;
1226 defaultLayout->setUnknown(unknown);
1227 defaultLayout->setName(name);
1228 if (!readStyle(lex, *defaultLayout)) {
1229 // The only way this happens is because the hardcoded layout above
1231 LASSERT(false, /**/);
1233 return *defaultLayout;
1237 /////////////////////////////////////////////////////////////////////////
1239 // DocumentClassBundle
1241 /////////////////////////////////////////////////////////////////////////
1243 DocumentClassBundle::~DocumentClassBundle()
1245 for (size_t i = 0; i != documentClasses_.size(); ++i)
1246 delete documentClasses_[i];
1247 documentClasses_.clear();
1250 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1252 DocumentClass * dc = new DocumentClass(baseClass);
1253 documentClasses_.push_back(dc);
1254 return *documentClasses_.back();
1258 DocumentClassBundle & DocumentClassBundle::get()
1260 static DocumentClassBundle singleton;
1265 DocumentClass & DocumentClassBundle::makeDocumentClass(
1266 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1268 DocumentClass & doc_class = newClass(baseClass);
1269 LayoutModuleList::const_iterator it = modlist.begin();
1270 LayoutModuleList::const_iterator en = modlist.end();
1271 for (; it != en; it++) {
1272 string const modName = *it;
1273 LyXModule * lm = theModuleList[modName];
1275 docstring const msg =
1276 bformat(_("The module %1$s has been requested by\n"
1277 "this document but has not been found in the list of\n"
1278 "available modules. If you recently installed it, you\n"
1279 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1280 frontend::Alert::warning(_("Module not available"), msg);
1283 if (!lm->isAvailable()) {
1284 docstring const msg =
1285 bformat(_("The module %1$s requires a package that is\n"
1286 "not available in your LaTeX installation. LaTeX output\n"
1287 "may not be possible.\n"), from_utf8(modName));
1288 frontend::Alert::warning(_("Package not available"), msg);
1290 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1291 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1292 docstring const msg =
1293 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1294 frontend::Alert::warning(_("Read Error"), msg);
1301 /////////////////////////////////////////////////////////////////////////
1305 /////////////////////////////////////////////////////////////////////////
1307 DocumentClass::DocumentClass(LayoutFile const & tc)
1312 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1314 LayoutList::const_iterator it = layoutlist_.begin();
1315 LayoutList::const_iterator end = layoutlist_.end();
1316 for (; it != end; ++it)
1317 if (it->latexname() == lay)
1323 bool DocumentClass::provides(string const & p) const
1325 return provides_.find(p) != provides_.end();
1329 bool DocumentClass::hasTocLevels() const
1331 return min_toclevel_ != Layout::NOT_IN_TOC;
1335 Layout const & DocumentClass::htmlTOCLayout() const
1337 if (html_toc_section_.empty()) {
1338 // we're going to look for the layout with the minimum toclevel
1339 TextClass::LayoutList::const_iterator lit = begin();
1340 TextClass::LayoutList::const_iterator const len = end();
1341 int minlevel = 1000;
1342 Layout const * lay = NULL;
1343 for (; lit != len; ++lit) {
1344 int const level = lit->toclevel;
1345 // we don't want Part
1346 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1352 html_toc_section_ = lay->name();
1354 // hmm. that is very odd, so we'll do our best
1355 html_toc_section_ = defaultLayoutName();
1357 return operator[](html_toc_section_);
1361 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1363 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1365 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1366 if (it != cite_formats_.end())
1368 return default_format;
1372 string const & DocumentClass::getCiteMacro(string const & macro) const
1374 static string empty;
1375 map<string, string>::const_iterator it = cite_macros_.find(macro);
1376 if (it != cite_macros_.end())
1382 /////////////////////////////////////////////////////////////////////////
1386 /////////////////////////////////////////////////////////////////////////
1388 ostream & operator<<(ostream & os, PageSides p)