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)
347 // Format of files before the 'Format' tag was introduced
352 while (lexrc.isOK() && !error) {
353 int le = lexrc.lex();
356 case Lexer::LEX_FEOF:
359 case Lexer::LEX_UNDEF:
360 lexrc.printError("Unknown TextClass tag `$$Token'");
368 // used below to track whether we are in an IfStyle or IfCounter tag.
369 bool ifstyle = false;
370 bool ifcounter = false;
372 switch (static_cast<TextClassTags>(le)) {
376 format = lexrc.getInteger();
379 case TC_OUTPUTFORMAT:
381 outputFormat_ = lexrc.getString();
385 readOutputType(lexrc);
386 switch(outputType_) {
388 outputFormat_ = "latex";
391 outputFormat_ = "docbook";
394 outputFormat_ = "literate";
399 case TC_INPUT: // Include file
401 string const inc = lexrc.getString();
402 FileName tmp = libFileSearch("layouts", inc,
406 lexrc.printError("Could not find input file: " + inc);
408 } else if (!read(tmp, MERGE)) {
409 lexrc.printError("Error reading input"
410 "file: " + tmp.absFilename());
416 case TC_DEFAULTSTYLE:
418 docstring const name = from_utf8(subst(lexrc.getString(),
420 defaultlayout_ = name;
429 lexrc.printError("No name given for style: `$$Token'.");
433 docstring const name = from_utf8(subst(lexrc.getString(),
436 string s = "Could not read name for style: `$$Token' "
437 + lexrc.getString() + " is probably not valid UTF-8!";
438 lexrc.printError(s.c_str());
440 // Since we couldn't read the name, we just scan the rest
441 // of the style and discard it.
442 error = !readStyle(lexrc, lay);
443 } else if (hasLayout(name)) {
444 Layout & lay = operator[](name);
445 error = !readStyle(lexrc, lay);
446 } else if (!ifstyle) {
448 layout.setName(name);
449 error = !readStyle(lexrc, layout);
451 layoutlist_.push_back(layout);
453 if (defaultlayout_.empty()) {
454 // We do not have a default layout yet, so we choose
455 // the first layout we encounter.
456 defaultlayout_ = name;
460 // scan the rest and discard it
462 readStyle(lexrc, lay);
473 docstring const style = from_utf8(subst(lexrc.getString(),
475 if (!deleteLayout(style))
476 lyxerr << "Cannot delete style `"
477 << to_utf8(style) << '\'' << endl;
483 columns_ = lexrc.getInteger();
488 switch (lexrc.getInteger()) {
489 case 1: sides_ = OneSide; break;
490 case 2: sides_ = TwoSides; break;
492 lyxerr << "Impossible number of page"
493 " sides, setting to one."
503 pagestyle_ = rtrim(lexrc.getString());
507 defaultfont_ = lyxRead(lexrc);
508 if (!defaultfont_.resolved()) {
509 lexrc.printError("Warning: defaultfont should "
510 "be fully instantiated!");
511 defaultfont_.realize(sane_font);
517 secnumdepth_ = lexrc.getInteger();
522 tocdepth_ = lexrc.getInteger();
525 // First step to support options
526 case TC_CLASSOPTIONS:
527 readClassOptions(lexrc);
531 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
534 case TC_HTMLPREAMBLE:
535 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
538 case TC_HTMLTOCSECTION:
539 html_toc_section_ = from_utf8(trim(lexrc.getString()));
542 case TC_ADDTOPREAMBLE:
543 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
546 case TC_ADDTOHTMLPREAMBLE:
547 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
552 string const feature = lexrc.getString();
554 if (lexrc.getInteger())
555 provides_.insert(feature);
557 provides_.erase(feature);
563 vector<string> const req
564 = getVectorFromString(lexrc.getString());
565 requires_.insert(req.begin(), req.end());
569 case TC_DEFAULTMODULE: {
571 string const module = lexrc.getString();
572 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
573 default_modules_.push_back(module);
577 case TC_PROVIDESMODULE: {
579 string const module = lexrc.getString();
580 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
581 provided_modules_.push_back(module);
585 case TC_EXCLUDESMODULE: {
587 string const module = lexrc.getString();
588 // modules already have their own way to exclude other modules
590 LYXERR0("ExcludesModule tag cannot be used in a module!");
593 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
594 excluded_modules_.push_back(module);
598 case TC_LEFTMARGIN: // left margin type
600 leftmargin_ = lexrc.getDocString();
603 case TC_RIGHTMARGIN: // right margin type
605 rightmargin_ = lexrc.getDocString();
608 case TC_INSETLAYOUT: {
610 lexrc.printError("No name given for InsetLayout: `$$Token'.");
614 docstring const name = subst(lexrc.getDocString(), '_', ' ');
616 string s = "Could not read name for InsetLayout: `$$Token' "
617 + lexrc.getString() + " is probably not valid UTF-8!";
618 lexrc.printError(s.c_str());
620 // Since we couldn't read the name, we just scan the rest
621 // of the style and discard it.
622 il.read(lexrc, *this);
624 } else if (hasInsetLayout(name)) {
625 InsetLayout & il = insetlayoutlist_[name];
626 error = !il.read(lexrc, *this);
630 error = !il.read(lexrc, *this);
632 insetlayoutlist_[name] = il;
642 readCiteFormat(lexrc);
649 docstring const name = lexrc.getDocString();
651 string s = "Could not read name for counter: `$$Token' "
652 + lexrc.getString() + " is probably not valid UTF-8!";
653 lexrc.printError(s.c_str());
655 // Since we couldn't read the name, we just scan the rest
659 error = !counters_.read(lexrc, name, !ifcounter);
662 lexrc.printError("No name given for style: `$$Token'.");
669 case TC_TITLELATEXTYPE:
670 readTitleType(lexrc);
673 case TC_TITLELATEXNAME:
675 titlename_ = lexrc.getString();
680 string const nofloat = lexrc.getString();
681 floatlist_.erase(nofloat);
686 //Note that this is triggered the first time through the loop unless
687 //we hit a format tag.
688 if (format != FORMAT)
692 if (format != FORMAT)
693 return FORMAT_MISMATCH;
696 return (error ? ERROR : OK);
698 if (defaultlayout_.empty()) {
699 LYXERR0("Error: Textclass '" << name_
700 << "' is missing a defaultstyle.");
704 // Try to erase "stdinsets" from the provides_ set.
706 // Provides stdinsets 1
707 // declaration simply tells us that the standard insets have been
708 // defined. (It's found in stdinsets.inc but could also be used in
709 // user-defined files.) There isn't really any such package. So we
710 // might as well go ahead and erase it.
711 // If we do not succeed, then it was not there, which means that
712 // the textclass did not provide the definitions of the standard
713 // insets. So we need to try to load them.
714 int erased = provides_.erase("stdinsets");
716 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
719 frontend::Alert::warning(_("Missing File"),
720 _("Could not find stdinsets.inc! This may lead to data loss!"));
722 } else if (!read(tmp, MERGE)) {
723 frontend::Alert::warning(_("Corrupt File"),
724 _("Could not read stdinsets.inc! This may lead to data loss!"));
729 min_toclevel_ = Layout::NOT_IN_TOC;
730 max_toclevel_ = Layout::NOT_IN_TOC;
731 const_iterator lit = begin();
732 const_iterator len = end();
733 for (; lit != len; ++lit) {
734 int const toclevel = lit->toclevel;
735 if (toclevel != Layout::NOT_IN_TOC) {
736 if (min_toclevel_ == Layout::NOT_IN_TOC)
737 min_toclevel_ = toclevel;
739 min_toclevel_ = min(min_toclevel_, toclevel);
740 max_toclevel_ = max(max_toclevel_, toclevel);
743 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
744 << ", maximum is " << max_toclevel_);
746 return (error ? ERROR : OK);
750 void TextClass::readTitleType(Lexer & lexrc)
752 LexerKeyword titleTypeTags[] = {
753 { "commandafter", TITLE_COMMAND_AFTER },
754 { "environment", TITLE_ENVIRONMENT }
757 PushPopHelper pph(lexrc, titleTypeTags);
759 int le = lexrc.lex();
761 case Lexer::LEX_UNDEF:
762 lexrc.printError("Unknown output type `$$Token'");
764 case TITLE_COMMAND_AFTER:
765 case TITLE_ENVIRONMENT:
766 titletype_ = static_cast<TitleLatexType>(le);
769 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
775 void TextClass::readOutputType(Lexer & lexrc)
777 LexerKeyword outputTypeTags[] = {
778 { "docbook", DOCBOOK },
780 { "literate", LITERATE }
783 PushPopHelper pph(lexrc, outputTypeTags);
785 int le = lexrc.lex();
787 case Lexer::LEX_UNDEF:
788 lexrc.printError("Unknown output type `$$Token'");
793 outputType_ = static_cast<OutputType>(le);
796 LYXERR0("Unhandled value " << le);
802 void TextClass::readClassOptions(Lexer & lexrc)
812 LexerKeyword classOptionsTags[] = {
814 {"fontsize", CO_FONTSIZE },
815 {"header", CO_HEADER },
816 {"other", CO_OTHER },
817 {"pagestyle", CO_PAGESTYLE }
820 lexrc.pushTable(classOptionsTags);
822 while (!getout && lexrc.isOK()) {
823 int le = lexrc.lex();
825 case Lexer::LEX_UNDEF:
826 lexrc.printError("Unknown ClassOption tag `$$Token'");
833 opt_fontsize_ = rtrim(lexrc.getString());
837 opt_pagestyle_ = rtrim(lexrc.getString());
841 if (options_.empty())
842 options_ = lexrc.getString();
844 options_ += ',' + lexrc.getString();
848 class_header_ = subst(lexrc.getString(), """, "\"");
859 void TextClass::readCiteFormat(Lexer & lexrc)
863 while (lexrc.isOK()) {
865 etype = lexrc.getString();
866 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
869 definition = lexrc.getString();
870 char initchar = etype[0];
873 if (initchar == '!' || initchar == '_')
874 cite_macros_[etype] = definition;
876 cite_formats_[etype] = definition;
881 void TextClass::readFloat(Lexer & lexrc)
900 LexerKeyword floatTags[] = {
902 { "extension", FT_EXT },
903 { "guiname", FT_NAME },
904 { "htmlattr", FT_HTMLATTR },
905 { "htmlstyle", FT_HTMLSTYLE },
906 { "htmltag", FT_HTMLTAG },
907 { "listcommand", FT_LISTCOMMAND },
908 { "listname", FT_LISTNAME },
909 { "needsfloatpkg", FT_NEEDSFLOAT },
910 { "numberwithin", FT_WITHIN },
911 { "placement", FT_PLACEMENT },
912 { "refprefix", FT_REFPREFIX },
913 { "style", FT_STYLE },
917 lexrc.pushTable(floatTags);
931 bool needsfloat = true;
934 while (!getout && lexrc.isOK()) {
935 int le = lexrc.lex();
937 case Lexer::LEX_UNDEF:
938 lexrc.printError("Unknown float tag `$$Token'");
945 type = lexrc.getString();
946 if (floatlist_.typeExist(type)) {
947 Floating const & fl = floatlist_.getType(type);
948 placement = fl.placement();
950 within = fl.within();
953 listname = fl.listName();
954 needsfloat = fl.needsFloatPkg();
955 listcommand = fl.listCommand();
956 refprefix = fl.refPrefix();
961 name = lexrc.getString();
965 placement = lexrc.getString();
969 ext = lexrc.getString();
973 within = lexrc.getString();
974 if (within == "none")
979 style = lexrc.getString();
983 listcommand = lexrc.getString();
987 refprefix = lexrc.getString();
991 listname = lexrc.getString();
995 needsfloat = lexrc.getBool();
999 htmlattr = lexrc.getString();
1003 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1007 htmltag = lexrc.getString();
1015 // Here we have a full float if getout == true
1017 if (!needsfloat && listcommand.empty())
1018 LYXERR0("The layout does not provide a list command " <<
1019 "for the builtin float `" << type << "'. LyX will " <<
1020 "not be able to produce a float list.");
1021 Floating fl(type, placement, ext, within, style, name,
1022 listname, listcommand, refprefix,
1023 htmltag, htmlattr, htmlstyle, needsfloat);
1024 floatlist_.newFloat(fl);
1025 // each float has its own counter
1026 counters_.newCounter(from_ascii(type), from_ascii(within),
1027 docstring(), docstring());
1028 // also define sub-float counters
1029 docstring const subtype = "sub-" + from_ascii(type);
1030 counters_.newCounter(subtype, from_ascii(type),
1031 "\\alph{" + subtype + "}", docstring());
1038 string const & TextClass::prerequisites() const
1040 if (contains(prerequisites_, ',')) {
1041 vector<string> const pres = getVectorFromString(prerequisites_);
1042 prerequisites_ = getStringFromVector(pres, "\n\t");
1044 return prerequisites_;
1047 bool TextClass::hasLayout(docstring const & n) const
1049 docstring const name = n.empty() ? defaultLayoutName() : n;
1051 return find_if(layoutlist_.begin(), layoutlist_.end(),
1052 LayoutNamesEqual(name))
1053 != layoutlist_.end();
1057 bool TextClass::hasInsetLayout(docstring const & n) const
1061 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1062 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1063 for (; it != en; ++it)
1070 Layout const & TextClass::operator[](docstring const & name) const
1072 LASSERT(!name.empty(), /**/);
1075 find_if(begin(), end(), LayoutNamesEqual(name));
1078 lyxerr << "We failed to find the layout '" << to_utf8(name)
1079 << "' in the layout list. You MUST investigate!"
1081 for (const_iterator cit = begin(); cit != end(); ++cit)
1082 lyxerr << " " << to_utf8(cit->name()) << endl;
1084 // we require the name to exist
1085 LASSERT(false, /**/);
1092 Layout & TextClass::operator[](docstring const & name)
1094 LASSERT(!name.empty(), /**/);
1096 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1099 LYXERR0("We failed to find the layout '" << to_utf8(name)
1100 << "' in the layout list. You MUST investigate!");
1101 for (const_iterator cit = begin(); cit != end(); ++cit)
1102 LYXERR0(" " << to_utf8(cit->name()));
1104 // we require the name to exist
1105 LASSERT(false, /**/);
1112 bool TextClass::deleteLayout(docstring const & name)
1114 if (name == defaultLayoutName() || name == plainLayoutName())
1117 LayoutList::iterator it =
1118 remove_if(layoutlist_.begin(), layoutlist_.end(),
1119 LayoutNamesEqual(name));
1121 LayoutList::iterator end = layoutlist_.end();
1122 bool const ret = (it != end);
1123 layoutlist_.erase(it, end);
1128 // Load textclass info if not loaded yet
1129 bool TextClass::load(string const & path) const
1134 // Read style-file, provided path is searched before the system ones
1135 // If path is a file, it is loaded directly.
1136 FileName layout_file(path);
1137 if (!path.empty() && !layout_file.isReadableFile())
1138 layout_file = FileName(addName(path, name_ + ".layout"));
1139 if (layout_file.empty() || !layout_file.exists())
1140 layout_file = libFileSearch("layouts", name_, "layout");
1141 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1144 lyxerr << "Error reading `"
1145 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1146 << "'\n(Check `" << name_
1147 << "')\nCheck your installation and "
1148 "try Options/Reconfigure..."
1156 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1159 layoutlist_.push_back(createBasicLayout(n, true));
1163 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1165 // FIXME The fix for the InsetLayout part of 4812 would be here:
1166 // Add the InsetLayout to the document class if it is not found.
1168 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1169 while (!n.empty()) {
1170 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1171 if (cit != cen && cit->first == n)
1173 size_t i = n.find(':');
1174 if (i == string::npos)
1178 return plain_insetlayout_;
1182 docstring const & TextClass::defaultLayoutName() const
1184 // This really should come from the actual layout... (Lgb)
1185 return defaultlayout_;
1189 Layout const & TextClass::defaultLayout() const
1191 return operator[](defaultLayoutName());
1195 bool TextClass::isDefaultLayout(Layout const & layout) const
1197 return layout.name() == defaultLayoutName();
1201 bool TextClass::isPlainLayout(Layout const & layout) const
1203 return layout.name() == plainLayoutName();
1207 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1209 static Layout * defaultLayout = NULL;
1211 if (defaultLayout) {
1212 defaultLayout->setUnknown(unknown);
1213 defaultLayout->setName(name);
1214 return *defaultLayout;
1217 static char const * s = "Margin Static\n"
1218 "LatexType Paragraph\n"
1221 "AlignPossible Left, Right, Center\n"
1222 "LabelType No_Label\n"
1224 istringstream ss(s);
1225 Lexer lex(textClassTags);
1227 defaultLayout = new Layout;
1228 defaultLayout->setUnknown(unknown);
1229 defaultLayout->setName(name);
1230 if (!readStyle(lex, *defaultLayout)) {
1231 // The only way this happens is because the hardcoded layout above
1233 LASSERT(false, /**/);
1235 return *defaultLayout;
1239 /////////////////////////////////////////////////////////////////////////
1241 // DocumentClassBundle
1243 /////////////////////////////////////////////////////////////////////////
1245 DocumentClassBundle::~DocumentClassBundle()
1247 for (size_t i = 0; i != documentClasses_.size(); ++i)
1248 delete documentClasses_[i];
1249 documentClasses_.clear();
1252 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1254 DocumentClass * dc = new DocumentClass(baseClass);
1255 documentClasses_.push_back(dc);
1256 return *documentClasses_.back();
1260 DocumentClassBundle & DocumentClassBundle::get()
1262 static DocumentClassBundle singleton;
1267 DocumentClass & DocumentClassBundle::makeDocumentClass(
1268 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1270 DocumentClass & doc_class = newClass(baseClass);
1271 LayoutModuleList::const_iterator it = modlist.begin();
1272 LayoutModuleList::const_iterator en = modlist.end();
1273 for (; it != en; it++) {
1274 string const modName = *it;
1275 LyXModule * lm = theModuleList[modName];
1277 docstring const msg =
1278 bformat(_("The module %1$s has been requested by\n"
1279 "this document but has not been found in the list of\n"
1280 "available modules. If you recently installed it, you\n"
1281 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1282 frontend::Alert::warning(_("Module not available"), msg);
1285 if (!lm->isAvailable()) {
1286 docstring const msg =
1287 bformat(_("The module %1$s requires a package that is\n"
1288 "not available in your LaTeX installation. LaTeX output\n"
1289 "may not be possible.\n"), from_utf8(modName));
1290 frontend::Alert::warning(_("Package not available"), msg);
1292 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1293 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1294 docstring const msg =
1295 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1296 frontend::Alert::warning(_("Read Error"), msg);
1303 /////////////////////////////////////////////////////////////////////////
1307 /////////////////////////////////////////////////////////////////////////
1309 DocumentClass::DocumentClass(LayoutFile const & tc)
1314 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1316 LayoutList::const_iterator it = layoutlist_.begin();
1317 LayoutList::const_iterator end = layoutlist_.end();
1318 for (; it != end; ++it)
1319 if (it->latexname() == lay)
1325 bool DocumentClass::provides(string const & p) const
1327 return provides_.find(p) != provides_.end();
1331 bool DocumentClass::hasTocLevels() const
1333 return min_toclevel_ != Layout::NOT_IN_TOC;
1337 Layout const & DocumentClass::htmlTOCLayout() const
1339 if (html_toc_section_.empty()) {
1340 // we're going to look for the layout with the minimum toclevel
1341 TextClass::LayoutList::const_iterator lit = begin();
1342 TextClass::LayoutList::const_iterator const len = end();
1343 int minlevel = 1000;
1344 Layout const * lay = NULL;
1345 for (; lit != len; ++lit) {
1346 int const level = lit->toclevel;
1347 // we don't want Part
1348 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1354 html_toc_section_ = lay->name();
1356 // hmm. that is very odd, so we'll do our best
1357 html_toc_section_ = defaultLayoutName();
1359 return operator[](html_toc_section_);
1363 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1365 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1367 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1368 if (it != cite_formats_.end())
1370 return default_format;
1374 string const & DocumentClass::getCiteMacro(string const & macro) const
1376 static string empty;
1377 map<string, string>::const_iterator it = cite_macros_.find(macro);
1378 if (it != cite_macros_.end())
1384 /////////////////////////////////////////////////////////////////////////
1388 /////////////////////////////////////////////////////////////////////////
1390 ostream & operator<<(ostream & os, PageSides p)