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;
52 // Keep the changes documented in the Customization manual.
54 // If you change this format, then you MUST also make sure that
55 // your changes do not invalidate the hardcoded layout file in
56 // LayoutFile.cpp. Additions will never do so, but syntax changes
57 // could. See LayoutFileList::addEmptyClass() and, especially, the
58 // definition of the layoutpost string.
59 // You should also run (or ask someone who has bash to run) the
60 // development/updatelayouts.sh script, to update the format of
61 // all of our layout files.
63 int const LAYOUT_FORMAT = 29;
67 class LayoutNamesEqual : public unary_function<Layout, bool> {
69 LayoutNamesEqual(docstring const & name)
72 bool operator()(Layout const & c) const
74 return c.name() == name_;
81 bool layout2layout(FileName const & filename, FileName const & tempfile)
83 FileName const script = libFileSearch("scripts", "layout2layout.py");
85 LYXERR0("Could not find layout conversion "
86 "script layout2layout.py.");
90 ostringstream command;
91 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
92 << ' ' << quoteName(filename.toFilesystemEncoding())
93 << ' ' << quoteName(tempfile.toFilesystemEncoding());
94 string const command_str = command.str();
96 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
98 cmd_ret const ret = runCommand(command_str);
100 LYXERR0("Could not run layout conversion script layout2layout.py.");
107 string translateReadType(TextClass::ReadType rt)
110 case TextClass::BASECLASS:
112 case TextClass::MERGE:
114 case TextClass::MODULE:
115 return "module file";
116 case TextClass::VALIDATION:
126 // This string should not be translated here,
127 // because it is a layout identifier.
128 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
131 InsetLayout DocumentClass::plain_insetlayout_;
134 /////////////////////////////////////////////////////////////////////////
138 /////////////////////////////////////////////////////////////////////////
140 TextClass::TextClass()
143 outputFormat_ = "latex";
148 pagestyle_ = "default";
149 defaultfont_ = sane_font;
150 opt_fontsize_ = "10|11|12";
151 opt_pagestyle_ = "empty|plain|headings|fancy";
152 titletype_ = TITLE_COMMAND_AFTER;
153 titlename_ = "maketitle";
155 _("Plain Layout"); // a hack to make this translatable
159 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
161 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
162 if (!lay.read(lexrc, *this)) {
163 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
167 lay.resfont = lay.font;
168 lay.resfont.realize(defaultfont_);
169 lay.reslabelfont = lay.labelfont;
170 lay.reslabelfont.realize(defaultfont_);
171 return true; // no errors
205 TC_ADDTOHTMLPREAMBLE,
216 LexerKeyword textClassTags[] = {
217 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
218 { "addtopreamble", TC_ADDTOPREAMBLE },
219 { "citeformat", TC_CITEFORMAT },
220 { "classoptions", TC_CLASSOPTIONS },
221 { "columns", TC_COLUMNS },
222 { "counter", TC_COUNTER },
223 { "defaultfont", TC_DEFAULTFONT },
224 { "defaultmodule", TC_DEFAULTMODULE },
225 { "defaultstyle", TC_DEFAULTSTYLE },
226 { "excludesmodule", TC_EXCLUDESMODULE },
227 { "float", TC_FLOAT },
228 { "format", TC_FORMAT },
229 { "htmlpreamble", TC_HTMLPREAMBLE },
230 { "htmltocsection", TC_HTMLTOCSECTION },
231 { "ifcounter", TC_IFCOUNTER },
232 { "ifstyle", TC_IFSTYLE },
233 { "input", TC_INPUT },
234 { "insetlayout", TC_INSETLAYOUT },
235 { "leftmargin", TC_LEFTMARGIN },
236 { "nofloat", TC_NOFLOAT },
237 { "nostyle", TC_NOSTYLE },
238 { "outputformat", TC_OUTPUTFORMAT },
239 { "outputtype", TC_OUTPUTTYPE },
240 { "pagestyle", TC_PAGESTYLE },
241 { "preamble", TC_PREAMBLE },
242 { "provides", TC_PROVIDES },
243 { "providesmodule", TC_PROVIDESMODULE },
244 { "requires", TC_REQUIRES },
245 { "rightmargin", TC_RIGHTMARGIN },
246 { "secnumdepth", TC_SECNUMDEPTH },
247 { "sides", TC_SIDES },
248 { "style", TC_STYLE },
249 { "titlelatexname", TC_TITLELATEXNAME },
250 { "titlelatextype", TC_TITLELATEXTYPE },
251 { "tocdepth", TC_TOCDEPTH }
257 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
259 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
260 FileName const tempfile = FileName::tempName("convert_layout");
261 bool success = layout2layout(filename, tempfile);
263 success = readWithoutConv(tempfile, rt) == OK;
264 tempfile.removeFile();
269 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
271 if (!filename.isReadableFile()) {
272 lyxerr << "Cannot read layout file `" << filename << "'."
277 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
278 to_utf8(makeDisplayPath(filename.absFileName())));
280 // Define the plain layout used in table cells, ert, etc. Note that
281 // we do this before loading any layout file, so that classes can
282 // override features of this layout if they should choose to do so.
283 if (rt == BASECLASS && !hasLayout(plain_layout_))
284 layoutlist_.push_back(createBasicLayout(plain_layout_));
286 Lexer lexrc(textClassTags);
287 lexrc.setFile(filename);
288 ReturnValues retval = read(lexrc, rt);
290 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
291 to_utf8(makeDisplayPath(filename.absFileName())));
297 bool TextClass::read(FileName const & filename, ReadType rt)
299 ReturnValues const retval = readWithoutConv(filename, rt);
300 if (retval != FORMAT_MISMATCH)
303 bool const worx = convertLayoutFormat(filename, rt);
305 LYXERR0 ("Unable to convert " << filename <<
306 " to format " << LAYOUT_FORMAT);
313 bool TextClass::validate(std::string const & str)
316 return tc.read(str, VALIDATION);
320 bool TextClass::read(std::string const & str, ReadType rt)
322 Lexer lexrc(textClassTags);
323 istringstream is(str);
325 ReturnValues retval = read(lexrc, rt);
327 if (retval != FORMAT_MISMATCH)
330 // write the layout string to a temporary file
331 FileName const tempfile = FileName::tempName("TextClass_read");
332 ofstream os(tempfile.toFilesystemEncoding().c_str());
334 LYXERR0("Unable to create temporary file");
340 // now try to convert it
341 bool const worx = convertLayoutFormat(tempfile, rt);
343 LYXERR0("Unable to convert internal layout information to format "
346 tempfile.removeFile();
351 // Reads a textclass structure from file.
352 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
357 // Format of files before the 'Format' tag was introduced
362 while (lexrc.isOK() && !error) {
363 int le = lexrc.lex();
366 case Lexer::LEX_FEOF:
369 case Lexer::LEX_UNDEF:
370 lexrc.printError("Unknown TextClass tag `$$Token'");
378 // used below to track whether we are in an IfStyle or IfCounter tag.
379 bool ifstyle = false;
380 bool ifcounter = false;
382 switch (static_cast<TextClassTags>(le)) {
386 format = lexrc.getInteger();
389 case TC_OUTPUTFORMAT:
391 outputFormat_ = lexrc.getString();
395 readOutputType(lexrc);
396 switch(outputType_) {
398 outputFormat_ = "latex";
401 outputFormat_ = "docbook";
404 outputFormat_ = "literate";
409 case TC_INPUT: // Include file
411 string const inc = lexrc.getString();
412 FileName tmp = libFileSearch("layouts", inc,
416 lexrc.printError("Could not find input file: " + inc);
418 } else if (!read(tmp, MERGE)) {
419 lexrc.printError("Error reading input file: " + tmp.absFileName());
425 case TC_DEFAULTSTYLE:
427 docstring const name = from_utf8(subst(lexrc.getString(),
429 defaultlayout_ = name;
438 lexrc.printError("No name given for style: `$$Token'.");
442 docstring const name = from_utf8(subst(lexrc.getString(),
445 string s = "Could not read name for style: `$$Token' "
446 + lexrc.getString() + " is probably not valid UTF-8!";
449 // Since we couldn't read the name, we just scan the rest
450 // of the style and discard it.
451 error = !readStyle(lexrc, lay);
452 } else if (hasLayout(name)) {
453 Layout & lay = operator[](name);
454 error = !readStyle(lexrc, lay);
455 } else if (!ifstyle) {
457 layout.setName(name);
458 error = !readStyle(lexrc, layout);
460 layoutlist_.push_back(layout);
462 if (defaultlayout_.empty()) {
463 // We do not have a default layout yet, so we choose
464 // the first layout we encounter.
465 defaultlayout_ = name;
469 // this was an ifstyle where we didn't have the style
470 // scan the rest and discard it
472 readStyle(lexrc, lay);
482 docstring const style = from_utf8(subst(lexrc.getString(),
484 if (!deleteLayout(style))
485 lyxerr << "Cannot delete style `"
486 << to_utf8(style) << '\'' << endl;
492 columns_ = lexrc.getInteger();
497 switch (lexrc.getInteger()) {
498 case 1: sides_ = OneSide; break;
499 case 2: sides_ = TwoSides; break;
501 lyxerr << "Impossible number of page"
502 " sides, setting to one."
512 pagestyle_ = rtrim(lexrc.getString());
516 defaultfont_ = lyxRead(lexrc);
517 if (!defaultfont_.resolved()) {
518 lexrc.printError("Warning: defaultfont should "
519 "be fully instantiated!");
520 defaultfont_.realize(sane_font);
526 secnumdepth_ = lexrc.getInteger();
531 tocdepth_ = lexrc.getInteger();
534 // First step to support options
535 case TC_CLASSOPTIONS:
536 readClassOptions(lexrc);
540 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
543 case TC_HTMLPREAMBLE:
544 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
547 case TC_HTMLTOCSECTION:
548 html_toc_section_ = from_utf8(trim(lexrc.getString()));
551 case TC_ADDTOPREAMBLE:
552 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
555 case TC_ADDTOHTMLPREAMBLE:
556 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
561 string const feature = lexrc.getString();
563 if (lexrc.getInteger())
564 provides_.insert(feature);
566 provides_.erase(feature);
572 vector<string> const req
573 = getVectorFromString(lexrc.getString());
574 requires_.insert(req.begin(), req.end());
578 case TC_DEFAULTMODULE: {
580 string const module = lexrc.getString();
581 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
582 default_modules_.push_back(module);
586 case TC_PROVIDESMODULE: {
588 string const module = lexrc.getString();
589 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
590 provided_modules_.push_back(module);
594 case TC_EXCLUDESMODULE: {
596 string const module = lexrc.getString();
597 // modules already have their own way to exclude other modules
599 LYXERR0("ExcludesModule tag cannot be used in a module!");
602 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
603 excluded_modules_.push_back(module);
607 case TC_LEFTMARGIN: // left margin type
609 leftmargin_ = lexrc.getDocString();
612 case TC_RIGHTMARGIN: // right margin type
614 rightmargin_ = lexrc.getDocString();
617 case TC_INSETLAYOUT: {
619 lexrc.printError("No name given for InsetLayout: `$$Token'.");
623 docstring const name = subst(lexrc.getDocString(), '_', ' ');
625 string s = "Could not read name for InsetLayout: `$$Token' "
626 + lexrc.getString() + " is probably not valid UTF-8!";
629 // Since we couldn't read the name, we just scan the rest
630 // of the style and discard it.
631 il.read(lexrc, *this);
632 // Let's try to continue rather than abort.
634 } else if (hasInsetLayout(name)) {
635 InsetLayout & il = insetlayoutlist_[name];
636 error = !il.read(lexrc, *this);
640 error = !il.read(lexrc, *this);
642 insetlayoutlist_[name] = il;
648 error = !readFloat(lexrc);
652 readCiteFormat(lexrc);
659 docstring const name = lexrc.getDocString();
661 string s = "Could not read name for counter: `$$Token' "
662 + lexrc.getString() + " is probably not valid UTF-8!";
663 lexrc.printError(s.c_str());
665 // Since we couldn't read the name, we just scan the rest
669 error = !counters_.read(lexrc, name, !ifcounter);
672 lexrc.printError("No name given for style: `$$Token'.");
679 case TC_TITLELATEXTYPE:
680 readTitleType(lexrc);
683 case TC_TITLELATEXNAME:
685 titlename_ = lexrc.getString();
690 string const nofloat = lexrc.getString();
691 floatlist_.erase(nofloat);
696 // Note that this is triggered the first time through the loop unless
697 // we hit a format tag.
698 if (format != LAYOUT_FORMAT)
699 return FORMAT_MISMATCH;
702 // at present, we abort if we encounter an error,
703 // so there is no point continuing.
708 return (error ? ERROR : OK);
710 if (defaultlayout_.empty()) {
711 LYXERR0("Error: Textclass '" << name_
712 << "' is missing a defaultstyle.");
716 // Try to erase "stdinsets" from the provides_ set.
718 // Provides stdinsets 1
719 // declaration simply tells us that the standard insets have been
720 // defined. (It's found in stdinsets.inc but could also be used in
721 // user-defined files.) There isn't really any such package. So we
722 // might as well go ahead and erase it.
723 // If we do not succeed, then it was not there, which means that
724 // the textclass did not provide the definitions of the standard
725 // insets. So we need to try to load them.
726 int erased = provides_.erase("stdinsets");
728 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
731 frontend::Alert::warning(_("Missing File"),
732 _("Could not find stdinsets.inc! This may lead to data loss!"));
734 } else if (!read(tmp, MERGE)) {
735 frontend::Alert::warning(_("Corrupt File"),
736 _("Could not read stdinsets.inc! This may lead to data loss!"));
741 min_toclevel_ = Layout::NOT_IN_TOC;
742 max_toclevel_ = Layout::NOT_IN_TOC;
743 const_iterator lit = begin();
744 const_iterator len = end();
745 for (; lit != len; ++lit) {
746 int const toclevel = lit->toclevel;
747 if (toclevel != Layout::NOT_IN_TOC) {
748 if (min_toclevel_ == Layout::NOT_IN_TOC)
749 min_toclevel_ = toclevel;
751 min_toclevel_ = min(min_toclevel_, toclevel);
752 max_toclevel_ = max(max_toclevel_, toclevel);
755 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
756 << ", maximum is " << max_toclevel_);
758 return (error ? ERROR : OK);
762 void TextClass::readTitleType(Lexer & lexrc)
764 LexerKeyword titleTypeTags[] = {
765 { "commandafter", TITLE_COMMAND_AFTER },
766 { "environment", TITLE_ENVIRONMENT }
769 PushPopHelper pph(lexrc, titleTypeTags);
771 int le = lexrc.lex();
773 case Lexer::LEX_UNDEF:
774 lexrc.printError("Unknown output type `$$Token'");
776 case TITLE_COMMAND_AFTER:
777 case TITLE_ENVIRONMENT:
778 titletype_ = static_cast<TitleLatexType>(le);
781 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
787 void TextClass::readOutputType(Lexer & lexrc)
789 LexerKeyword outputTypeTags[] = {
790 { "docbook", DOCBOOK },
792 { "literate", LITERATE }
795 PushPopHelper pph(lexrc, outputTypeTags);
797 int le = lexrc.lex();
799 case Lexer::LEX_UNDEF:
800 lexrc.printError("Unknown output type `$$Token'");
805 outputType_ = static_cast<OutputType>(le);
808 LYXERR0("Unhandled value " << le);
814 void TextClass::readClassOptions(Lexer & lexrc)
824 LexerKeyword classOptionsTags[] = {
826 {"fontsize", CO_FONTSIZE },
827 {"header", CO_HEADER },
828 {"other", CO_OTHER },
829 {"pagestyle", CO_PAGESTYLE }
832 lexrc.pushTable(classOptionsTags);
834 while (!getout && lexrc.isOK()) {
835 int le = lexrc.lex();
837 case Lexer::LEX_UNDEF:
838 lexrc.printError("Unknown ClassOption tag `$$Token'");
846 opt_fontsize_ = rtrim(lexrc.getString());
850 opt_pagestyle_ = rtrim(lexrc.getString());
854 if (options_.empty())
855 options_ = lexrc.getString();
857 options_ += ',' + lexrc.getString();
861 class_header_ = subst(lexrc.getString(), """, "\"");
872 void TextClass::readCiteFormat(Lexer & lexrc)
876 while (lexrc.isOK()) {
878 etype = lexrc.getString();
879 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
882 definition = lexrc.getString();
883 char initchar = etype[0];
886 if (initchar == '!' || initchar == '_')
887 cite_macros_[etype] = definition;
889 cite_formats_[etype] = definition;
894 bool TextClass::readFloat(Lexer & lexrc)
913 LexerKeyword floatTags[] = {
915 { "extension", FT_EXT },
916 { "guiname", FT_NAME },
917 { "htmlattr", FT_HTMLATTR },
918 { "htmlstyle", FT_HTMLSTYLE },
919 { "htmltag", FT_HTMLTAG },
920 { "listcommand", FT_LISTCOMMAND },
921 { "listname", FT_LISTNAME },
922 { "needsfloatpkg", FT_NEEDSFLOAT },
923 { "numberwithin", FT_WITHIN },
924 { "placement", FT_PLACEMENT },
925 { "refprefix", FT_REFPREFIX },
926 { "style", FT_STYLE },
930 lexrc.pushTable(floatTags);
944 bool needsfloat = true;
947 while (!getout && lexrc.isOK()) {
948 int le = lexrc.lex();
950 case Lexer::LEX_UNDEF:
951 lexrc.printError("Unknown float tag `$$Token'");
959 type = lexrc.getString();
960 if (floatlist_.typeExist(type)) {
961 Floating const & fl = floatlist_.getType(type);
962 placement = fl.placement();
964 within = fl.within();
967 listname = fl.listName();
968 needsfloat = fl.needsFloatPkg();
969 listcommand = fl.listCommand();
970 refprefix = fl.refPrefix();
975 name = lexrc.getString();
979 placement = lexrc.getString();
983 ext = lexrc.getString();
987 within = lexrc.getString();
988 if (within == "none")
993 style = lexrc.getString();
997 listcommand = lexrc.getString();
1001 refprefix = lexrc.getString();
1005 listname = lexrc.getString();
1009 needsfloat = lexrc.getBool();
1013 htmlattr = lexrc.getString();
1017 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1021 htmltag = lexrc.getString();
1031 // Here we have a full float if getout == true
1033 if (!needsfloat && listcommand.empty())
1034 LYXERR0("The layout does not provide a list command " <<
1035 "for the builtin float `" << type << "'. LyX will " <<
1036 "not be able to produce a float list.");
1037 Floating fl(type, placement, ext, within, style, name,
1038 listname, listcommand, refprefix,
1039 htmltag, htmlattr, htmlstyle, needsfloat);
1040 floatlist_.newFloat(fl);
1041 // each float has its own counter
1042 counters_.newCounter(from_ascii(type), from_ascii(within),
1043 docstring(), docstring());
1044 // also define sub-float counters
1045 docstring const subtype = "sub-" + from_ascii(type);
1046 counters_.newCounter(subtype, from_ascii(type),
1047 "\\alph{" + subtype + "}", docstring());
1053 string const & TextClass::prerequisites() const
1055 if (contains(prerequisites_, ',')) {
1056 vector<string> const pres = getVectorFromString(prerequisites_);
1057 prerequisites_ = getStringFromVector(pres, "\n\t");
1059 return prerequisites_;
1062 bool TextClass::hasLayout(docstring const & n) const
1064 docstring const name = n.empty() ? defaultLayoutName() : n;
1066 return find_if(layoutlist_.begin(), layoutlist_.end(),
1067 LayoutNamesEqual(name))
1068 != layoutlist_.end();
1072 bool TextClass::hasInsetLayout(docstring const & n) const
1076 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1077 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1078 for (; it != en; ++it)
1085 Layout const & TextClass::operator[](docstring const & name) const
1087 LASSERT(!name.empty(), /**/);
1090 find_if(begin(), end(), LayoutNamesEqual(name));
1093 lyxerr << "We failed to find the layout '" << to_utf8(name)
1094 << "' in the layout list. You MUST investigate!"
1096 for (const_iterator cit = begin(); cit != end(); ++cit)
1097 lyxerr << " " << to_utf8(cit->name()) << endl;
1099 // we require the name to exist
1100 LASSERT(false, /**/);
1107 Layout & TextClass::operator[](docstring const & name)
1109 LASSERT(!name.empty(), /**/);
1111 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1114 LYXERR0("We failed to find the layout '" << to_utf8(name)
1115 << "' in the layout list. You MUST investigate!");
1116 for (const_iterator cit = begin(); cit != end(); ++cit)
1117 LYXERR0(" " << to_utf8(cit->name()));
1119 // we require the name to exist
1120 LASSERT(false, /**/);
1127 bool TextClass::deleteLayout(docstring const & name)
1129 if (name == defaultLayoutName() || name == plainLayoutName())
1132 LayoutList::iterator it =
1133 remove_if(layoutlist_.begin(), layoutlist_.end(),
1134 LayoutNamesEqual(name));
1136 LayoutList::iterator end = layoutlist_.end();
1137 bool const ret = (it != end);
1138 layoutlist_.erase(it, end);
1143 // Load textclass info if not loaded yet
1144 bool TextClass::load(string const & path) const
1149 // Read style-file, provided path is searched before the system ones
1150 // If path is a file, it is loaded directly.
1151 FileName layout_file(path);
1152 if (!path.empty() && !layout_file.isReadableFile())
1153 layout_file = FileName(addName(path, name_ + ".layout"));
1154 if (layout_file.empty() || !layout_file.exists())
1155 layout_file = libFileSearch("layouts", name_, "layout");
1156 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1159 lyxerr << "Error reading `"
1160 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1161 << "'\n(Check `" << name_
1162 << "')\nCheck your installation and "
1163 "try Options/Reconfigure..."
1171 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1174 layoutlist_.push_back(createBasicLayout(n, true));
1178 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1180 // FIXME The fix for the InsetLayout part of 4812 would be here:
1181 // Add the InsetLayout to the document class if it is not found.
1183 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1184 while (!n.empty()) {
1185 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1186 if (cit != cen && cit->first == n)
1188 size_t i = n.find(':');
1189 if (i == string::npos)
1193 return plain_insetlayout_;
1197 docstring const & TextClass::defaultLayoutName() const
1199 // This really should come from the actual layout... (Lgb)
1200 return defaultlayout_;
1204 Layout const & TextClass::defaultLayout() const
1206 return operator[](defaultLayoutName());
1210 bool TextClass::isDefaultLayout(Layout const & layout) const
1212 return layout.name() == defaultLayoutName();
1216 bool TextClass::isPlainLayout(Layout const & layout) const
1218 return layout.name() == plainLayoutName();
1222 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1224 static Layout * defaultLayout = NULL;
1226 if (defaultLayout) {
1227 defaultLayout->setUnknown(unknown);
1228 defaultLayout->setName(name);
1229 return *defaultLayout;
1232 static char const * s = "Margin Static\n"
1233 "LatexType Paragraph\n"
1236 "AlignPossible Left, Right, Center\n"
1237 "LabelType No_Label\n"
1239 istringstream ss(s);
1240 Lexer lex(textClassTags);
1242 defaultLayout = new Layout;
1243 defaultLayout->setUnknown(unknown);
1244 defaultLayout->setName(name);
1245 if (!readStyle(lex, *defaultLayout)) {
1246 // The only way this happens is because the hardcoded layout above
1248 LASSERT(false, /**/);
1250 return *defaultLayout;
1254 /////////////////////////////////////////////////////////////////////////
1256 // DocumentClassBundle
1258 /////////////////////////////////////////////////////////////////////////
1260 DocumentClassBundle::~DocumentClassBundle()
1262 for (size_t i = 0; i != documentClasses_.size(); ++i)
1263 delete documentClasses_[i];
1264 documentClasses_.clear();
1267 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1269 DocumentClass * dc = new DocumentClass(baseClass);
1270 documentClasses_.push_back(dc);
1271 return *documentClasses_.back();
1275 DocumentClassBundle & DocumentClassBundle::get()
1277 static DocumentClassBundle singleton;
1282 DocumentClass & DocumentClassBundle::makeDocumentClass(
1283 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1285 DocumentClass & doc_class = newClass(baseClass);
1286 LayoutModuleList::const_iterator it = modlist.begin();
1287 LayoutModuleList::const_iterator en = modlist.end();
1288 for (; it != en; it++) {
1289 string const modName = *it;
1290 LyXModule * lm = theModuleList[modName];
1292 docstring const msg =
1293 bformat(_("The module %1$s has been requested by\n"
1294 "this document but has not been found in the list of\n"
1295 "available modules. If you recently installed it, you\n"
1296 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1297 frontend::Alert::warning(_("Module not available"), msg);
1300 if (!lm->isAvailable()) {
1301 docstring const msg =
1302 bformat(_("The module %1$s requires a package that is\n"
1303 "not available in your LaTeX installation. LaTeX output\n"
1304 "may not be possible.\n"), from_utf8(modName));
1305 frontend::Alert::warning(_("Package not available"), msg);
1307 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1308 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1309 docstring const msg =
1310 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1311 frontend::Alert::warning(_("Read Error"), msg);
1318 /////////////////////////////////////////////////////////////////////////
1322 /////////////////////////////////////////////////////////////////////////
1324 DocumentClass::DocumentClass(LayoutFile const & tc)
1329 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1331 LayoutList::const_iterator it = layoutlist_.begin();
1332 LayoutList::const_iterator end = layoutlist_.end();
1333 for (; it != end; ++it)
1334 if (it->latexname() == lay)
1340 bool DocumentClass::provides(string const & p) const
1342 return provides_.find(p) != provides_.end();
1346 bool DocumentClass::hasTocLevels() const
1348 return min_toclevel_ != Layout::NOT_IN_TOC;
1352 Layout const & DocumentClass::htmlTOCLayout() const
1354 if (html_toc_section_.empty()) {
1355 // we're going to look for the layout with the minimum toclevel
1356 TextClass::LayoutList::const_iterator lit = begin();
1357 TextClass::LayoutList::const_iterator const len = end();
1358 int minlevel = 1000;
1359 Layout const * lay = NULL;
1360 for (; lit != len; ++lit) {
1361 int const level = lit->toclevel;
1362 // we don't want Part
1363 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1369 html_toc_section_ = lay->name();
1371 // hmm. that is very odd, so we'll do our best
1372 html_toc_section_ = defaultLayoutName();
1374 return operator[](html_toc_section_);
1378 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1380 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1382 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1383 if (it != cite_formats_.end())
1385 return default_format;
1389 string const & DocumentClass::getCiteMacro(string const & macro) const
1391 static string empty;
1392 map<string, string>::const_iterator it = cite_macros_.find(macro);
1393 if (it != cite_macros_.end())
1399 /////////////////////////////////////////////////////////////////////////
1403 /////////////////////////////////////////////////////////////////////////
1405 ostream & operator<<(ostream & os, PageSides p)