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 = 27;
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 file: " + tmp.absFileName());
415 case TC_DEFAULTSTYLE:
417 docstring const name = from_utf8(subst(lexrc.getString(),
419 defaultlayout_ = name;
428 lexrc.printError("No name given for style: `$$Token'.");
432 docstring const name = from_utf8(subst(lexrc.getString(),
435 string s = "Could not read name for style: `$$Token' "
436 + lexrc.getString() + " is probably not valid UTF-8!";
439 // Since we couldn't read the name, we just scan the rest
440 // of the style and discard it.
441 error = !readStyle(lexrc, lay);
442 } else if (hasLayout(name)) {
443 Layout & lay = operator[](name);
444 error = !readStyle(lexrc, lay);
445 } else if (!ifstyle) {
447 layout.setName(name);
448 error = !readStyle(lexrc, layout);
450 layoutlist_.push_back(layout);
452 if (defaultlayout_.empty()) {
453 // We do not have a default layout yet, so we choose
454 // the first layout we encounter.
455 defaultlayout_ = name;
459 // this was an ifstyle where we didn't have the style
460 // scan the rest and discard it
462 readStyle(lexrc, lay);
472 docstring const style = from_utf8(subst(lexrc.getString(),
474 if (!deleteLayout(style))
475 lyxerr << "Cannot delete style `"
476 << to_utf8(style) << '\'' << endl;
482 columns_ = lexrc.getInteger();
487 switch (lexrc.getInteger()) {
488 case 1: sides_ = OneSide; break;
489 case 2: sides_ = TwoSides; break;
491 lyxerr << "Impossible number of page"
492 " sides, setting to one."
502 pagestyle_ = rtrim(lexrc.getString());
506 defaultfont_ = lyxRead(lexrc);
507 if (!defaultfont_.resolved()) {
508 lexrc.printError("Warning: defaultfont should "
509 "be fully instantiated!");
510 defaultfont_.realize(sane_font);
516 secnumdepth_ = lexrc.getInteger();
521 tocdepth_ = lexrc.getInteger();
524 // First step to support options
525 case TC_CLASSOPTIONS:
526 readClassOptions(lexrc);
530 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
533 case TC_HTMLPREAMBLE:
534 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
537 case TC_HTMLTOCSECTION:
538 html_toc_section_ = from_utf8(trim(lexrc.getString()));
541 case TC_ADDTOPREAMBLE:
542 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
545 case TC_ADDTOHTMLPREAMBLE:
546 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
551 string const feature = lexrc.getString();
553 if (lexrc.getInteger())
554 provides_.insert(feature);
556 provides_.erase(feature);
562 vector<string> const req
563 = getVectorFromString(lexrc.getString());
564 requires_.insert(req.begin(), req.end());
568 case TC_DEFAULTMODULE: {
570 string const module = lexrc.getString();
571 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
572 default_modules_.push_back(module);
576 case TC_PROVIDESMODULE: {
578 string const module = lexrc.getString();
579 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
580 provided_modules_.push_back(module);
584 case TC_EXCLUDESMODULE: {
586 string const module = lexrc.getString();
587 // modules already have their own way to exclude other modules
589 LYXERR0("ExcludesModule tag cannot be used in a module!");
592 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
593 excluded_modules_.push_back(module);
597 case TC_LEFTMARGIN: // left margin type
599 leftmargin_ = lexrc.getDocString();
602 case TC_RIGHTMARGIN: // right margin type
604 rightmargin_ = lexrc.getDocString();
607 case TC_INSETLAYOUT: {
609 lexrc.printError("No name given for InsetLayout: `$$Token'.");
613 docstring const name = subst(lexrc.getDocString(), '_', ' ');
615 string s = "Could not read name for InsetLayout: `$$Token' "
616 + lexrc.getString() + " is probably not valid UTF-8!";
619 // Since we couldn't read the name, we just scan the rest
620 // of the style and discard it.
621 il.read(lexrc, *this);
622 // Let's try to continue rather than abort.
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;
638 error = !readFloat(lexrc);
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)
689 return FORMAT_MISMATCH;
692 // at present, we abort if we encounter an error,
693 // so there is no point continuing.
698 return (error ? ERROR : OK);
700 if (defaultlayout_.empty()) {
701 LYXERR0("Error: Textclass '" << name_
702 << "' is missing a defaultstyle.");
706 // Try to erase "stdinsets" from the provides_ set.
708 // Provides stdinsets 1
709 // declaration simply tells us that the standard insets have been
710 // defined. (It's found in stdinsets.inc but could also be used in
711 // user-defined files.) There isn't really any such package. So we
712 // might as well go ahead and erase it.
713 // If we do not succeed, then it was not there, which means that
714 // the textclass did not provide the definitions of the standard
715 // insets. So we need to try to load them.
716 int erased = provides_.erase("stdinsets");
718 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
721 frontend::Alert::warning(_("Missing File"),
722 _("Could not find stdinsets.inc! This may lead to data loss!"));
724 } else if (!read(tmp, MERGE)) {
725 frontend::Alert::warning(_("Corrupt File"),
726 _("Could not read stdinsets.inc! This may lead to data loss!"));
731 min_toclevel_ = Layout::NOT_IN_TOC;
732 max_toclevel_ = Layout::NOT_IN_TOC;
733 const_iterator lit = begin();
734 const_iterator len = end();
735 for (; lit != len; ++lit) {
736 int const toclevel = lit->toclevel;
737 if (toclevel != Layout::NOT_IN_TOC) {
738 if (min_toclevel_ == Layout::NOT_IN_TOC)
739 min_toclevel_ = toclevel;
741 min_toclevel_ = min(min_toclevel_, toclevel);
742 max_toclevel_ = max(max_toclevel_, toclevel);
745 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
746 << ", maximum is " << max_toclevel_);
748 return (error ? ERROR : OK);
752 void TextClass::readTitleType(Lexer & lexrc)
754 LexerKeyword titleTypeTags[] = {
755 { "commandafter", TITLE_COMMAND_AFTER },
756 { "environment", TITLE_ENVIRONMENT }
759 PushPopHelper pph(lexrc, titleTypeTags);
761 int le = lexrc.lex();
763 case Lexer::LEX_UNDEF:
764 lexrc.printError("Unknown output type `$$Token'");
766 case TITLE_COMMAND_AFTER:
767 case TITLE_ENVIRONMENT:
768 titletype_ = static_cast<TitleLatexType>(le);
771 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
777 void TextClass::readOutputType(Lexer & lexrc)
779 LexerKeyword outputTypeTags[] = {
780 { "docbook", DOCBOOK },
782 { "literate", LITERATE }
785 PushPopHelper pph(lexrc, outputTypeTags);
787 int le = lexrc.lex();
789 case Lexer::LEX_UNDEF:
790 lexrc.printError("Unknown output type `$$Token'");
795 outputType_ = static_cast<OutputType>(le);
798 LYXERR0("Unhandled value " << le);
804 void TextClass::readClassOptions(Lexer & lexrc)
814 LexerKeyword classOptionsTags[] = {
816 {"fontsize", CO_FONTSIZE },
817 {"header", CO_HEADER },
818 {"other", CO_OTHER },
819 {"pagestyle", CO_PAGESTYLE }
822 lexrc.pushTable(classOptionsTags);
824 while (!getout && lexrc.isOK()) {
825 int le = lexrc.lex();
827 case Lexer::LEX_UNDEF:
828 lexrc.printError("Unknown ClassOption tag `$$Token'");
836 opt_fontsize_ = rtrim(lexrc.getString());
840 opt_pagestyle_ = rtrim(lexrc.getString());
844 if (options_.empty())
845 options_ = lexrc.getString();
847 options_ += ',' + lexrc.getString();
851 class_header_ = subst(lexrc.getString(), """, "\"");
862 void TextClass::readCiteFormat(Lexer & lexrc)
866 while (lexrc.isOK()) {
868 etype = lexrc.getString();
869 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
872 definition = lexrc.getString();
873 char initchar = etype[0];
876 if (initchar == '!' || initchar == '_')
877 cite_macros_[etype] = definition;
879 cite_formats_[etype] = definition;
884 bool TextClass::readFloat(Lexer & lexrc)
903 LexerKeyword floatTags[] = {
905 { "extension", FT_EXT },
906 { "guiname", FT_NAME },
907 { "htmlattr", FT_HTMLATTR },
908 { "htmlstyle", FT_HTMLSTYLE },
909 { "htmltag", FT_HTMLTAG },
910 { "listcommand", FT_LISTCOMMAND },
911 { "listname", FT_LISTNAME },
912 { "needsfloatpkg", FT_NEEDSFLOAT },
913 { "numberwithin", FT_WITHIN },
914 { "placement", FT_PLACEMENT },
915 { "refprefix", FT_REFPREFIX },
916 { "style", FT_STYLE },
920 lexrc.pushTable(floatTags);
934 bool needsfloat = true;
937 while (!getout && lexrc.isOK()) {
938 int le = lexrc.lex();
940 case Lexer::LEX_UNDEF:
941 lexrc.printError("Unknown float tag `$$Token'");
949 type = lexrc.getString();
950 if (floatlist_.typeExist(type)) {
951 Floating const & fl = floatlist_.getType(type);
952 placement = fl.placement();
954 within = fl.within();
957 listname = fl.listName();
958 needsfloat = fl.needsFloatPkg();
959 listcommand = fl.listCommand();
960 refprefix = fl.refPrefix();
965 name = lexrc.getString();
969 placement = lexrc.getString();
973 ext = lexrc.getString();
977 within = lexrc.getString();
978 if (within == "none")
983 style = lexrc.getString();
987 listcommand = lexrc.getString();
991 refprefix = lexrc.getString();
995 listname = lexrc.getString();
999 needsfloat = lexrc.getBool();
1003 htmlattr = lexrc.getString();
1007 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1011 htmltag = lexrc.getString();
1021 // Here we have a full float if getout == true
1023 if (!needsfloat && listcommand.empty())
1024 LYXERR0("The layout does not provide a list command " <<
1025 "for the builtin float `" << type << "'. LyX will " <<
1026 "not be able to produce a float list.");
1027 Floating fl(type, placement, ext, within, style, name,
1028 listname, listcommand, refprefix,
1029 htmltag, htmlattr, htmlstyle, needsfloat);
1030 floatlist_.newFloat(fl);
1031 // each float has its own counter
1032 counters_.newCounter(from_ascii(type), from_ascii(within),
1033 docstring(), docstring());
1034 // also define sub-float counters
1035 docstring const subtype = "sub-" + from_ascii(type);
1036 counters_.newCounter(subtype, from_ascii(type),
1037 "\\alph{" + subtype + "}", docstring());
1043 string const & TextClass::prerequisites() const
1045 if (contains(prerequisites_, ',')) {
1046 vector<string> const pres = getVectorFromString(prerequisites_);
1047 prerequisites_ = getStringFromVector(pres, "\n\t");
1049 return prerequisites_;
1052 bool TextClass::hasLayout(docstring const & n) const
1054 docstring const name = n.empty() ? defaultLayoutName() : n;
1056 return find_if(layoutlist_.begin(), layoutlist_.end(),
1057 LayoutNamesEqual(name))
1058 != layoutlist_.end();
1062 bool TextClass::hasInsetLayout(docstring const & n) const
1066 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1067 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1068 for (; it != en; ++it)
1075 Layout const & TextClass::operator[](docstring const & name) const
1077 LASSERT(!name.empty(), /**/);
1080 find_if(begin(), end(), LayoutNamesEqual(name));
1083 lyxerr << "We failed to find the layout '" << to_utf8(name)
1084 << "' in the layout list. You MUST investigate!"
1086 for (const_iterator cit = begin(); cit != end(); ++cit)
1087 lyxerr << " " << to_utf8(cit->name()) << endl;
1089 // we require the name to exist
1090 LASSERT(false, /**/);
1097 Layout & TextClass::operator[](docstring const & name)
1099 LASSERT(!name.empty(), /**/);
1101 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1104 LYXERR0("We failed to find the layout '" << to_utf8(name)
1105 << "' in the layout list. You MUST investigate!");
1106 for (const_iterator cit = begin(); cit != end(); ++cit)
1107 LYXERR0(" " << to_utf8(cit->name()));
1109 // we require the name to exist
1110 LASSERT(false, /**/);
1117 bool TextClass::deleteLayout(docstring const & name)
1119 if (name == defaultLayoutName() || name == plainLayoutName())
1122 LayoutList::iterator it =
1123 remove_if(layoutlist_.begin(), layoutlist_.end(),
1124 LayoutNamesEqual(name));
1126 LayoutList::iterator end = layoutlist_.end();
1127 bool const ret = (it != end);
1128 layoutlist_.erase(it, end);
1133 // Load textclass info if not loaded yet
1134 bool TextClass::load(string const & path) const
1139 // Read style-file, provided path is searched before the system ones
1140 // If path is a file, it is loaded directly.
1141 FileName layout_file(path);
1142 if (!path.empty() && !layout_file.isReadableFile())
1143 layout_file = FileName(addName(path, name_ + ".layout"));
1144 if (layout_file.empty() || !layout_file.exists())
1145 layout_file = libFileSearch("layouts", name_, "layout");
1146 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1149 lyxerr << "Error reading `"
1150 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1151 << "'\n(Check `" << name_
1152 << "')\nCheck your installation and "
1153 "try Options/Reconfigure..."
1161 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1164 layoutlist_.push_back(createBasicLayout(n, true));
1168 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1170 // FIXME The fix for the InsetLayout part of 4812 would be here:
1171 // Add the InsetLayout to the document class if it is not found.
1173 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1174 while (!n.empty()) {
1175 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1176 if (cit != cen && cit->first == n)
1178 size_t i = n.find(':');
1179 if (i == string::npos)
1183 return plain_insetlayout_;
1187 docstring const & TextClass::defaultLayoutName() const
1189 // This really should come from the actual layout... (Lgb)
1190 return defaultlayout_;
1194 Layout const & TextClass::defaultLayout() const
1196 return operator[](defaultLayoutName());
1200 bool TextClass::isDefaultLayout(Layout const & layout) const
1202 return layout.name() == defaultLayoutName();
1206 bool TextClass::isPlainLayout(Layout const & layout) const
1208 return layout.name() == plainLayoutName();
1212 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1214 static Layout * defaultLayout = NULL;
1216 if (defaultLayout) {
1217 defaultLayout->setUnknown(unknown);
1218 defaultLayout->setName(name);
1219 return *defaultLayout;
1222 static char const * s = "Margin Static\n"
1223 "LatexType Paragraph\n"
1226 "AlignPossible Left, Right, Center\n"
1227 "LabelType No_Label\n"
1229 istringstream ss(s);
1230 Lexer lex(textClassTags);
1232 defaultLayout = new Layout;
1233 defaultLayout->setUnknown(unknown);
1234 defaultLayout->setName(name);
1235 if (!readStyle(lex, *defaultLayout)) {
1236 // The only way this happens is because the hardcoded layout above
1238 LASSERT(false, /**/);
1240 return *defaultLayout;
1244 /////////////////////////////////////////////////////////////////////////
1246 // DocumentClassBundle
1248 /////////////////////////////////////////////////////////////////////////
1250 DocumentClassBundle::~DocumentClassBundle()
1252 for (size_t i = 0; i != documentClasses_.size(); ++i)
1253 delete documentClasses_[i];
1254 documentClasses_.clear();
1257 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1259 DocumentClass * dc = new DocumentClass(baseClass);
1260 documentClasses_.push_back(dc);
1261 return *documentClasses_.back();
1265 DocumentClassBundle & DocumentClassBundle::get()
1267 static DocumentClassBundle singleton;
1272 DocumentClass & DocumentClassBundle::makeDocumentClass(
1273 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1275 DocumentClass & doc_class = newClass(baseClass);
1276 LayoutModuleList::const_iterator it = modlist.begin();
1277 LayoutModuleList::const_iterator en = modlist.end();
1278 for (; it != en; it++) {
1279 string const modName = *it;
1280 LyXModule * lm = theModuleList[modName];
1282 docstring const msg =
1283 bformat(_("The module %1$s has been requested by\n"
1284 "this document but has not been found in the list of\n"
1285 "available modules. If you recently installed it, you\n"
1286 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1287 frontend::Alert::warning(_("Module not available"), msg);
1290 if (!lm->isAvailable()) {
1291 docstring const msg =
1292 bformat(_("The module %1$s requires a package that is\n"
1293 "not available in your LaTeX installation. LaTeX output\n"
1294 "may not be possible.\n"), from_utf8(modName));
1295 frontend::Alert::warning(_("Package not available"), msg);
1297 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1298 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1299 docstring const msg =
1300 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1301 frontend::Alert::warning(_("Read Error"), msg);
1308 /////////////////////////////////////////////////////////////////////////
1312 /////////////////////////////////////////////////////////////////////////
1314 DocumentClass::DocumentClass(LayoutFile const & tc)
1319 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1321 LayoutList::const_iterator it = layoutlist_.begin();
1322 LayoutList::const_iterator end = layoutlist_.end();
1323 for (; it != end; ++it)
1324 if (it->latexname() == lay)
1330 bool DocumentClass::provides(string const & p) const
1332 return provides_.find(p) != provides_.end();
1336 bool DocumentClass::hasTocLevels() const
1338 return min_toclevel_ != Layout::NOT_IN_TOC;
1342 Layout const & DocumentClass::htmlTOCLayout() const
1344 if (html_toc_section_.empty()) {
1345 // we're going to look for the layout with the minimum toclevel
1346 TextClass::LayoutList::const_iterator lit = begin();
1347 TextClass::LayoutList::const_iterator const len = end();
1348 int minlevel = 1000;
1349 Layout const * lay = NULL;
1350 for (; lit != len; ++lit) {
1351 int const level = lit->toclevel;
1352 // we don't want Part
1353 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1359 html_toc_section_ = lay->name();
1361 // hmm. that is very odd, so we'll do our best
1362 html_toc_section_ = defaultLayoutName();
1364 return operator[](html_toc_section_);
1368 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1370 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1372 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1373 if (it != cite_formats_.end())
1375 return default_format;
1379 string const & DocumentClass::getCiteMacro(string const & macro) const
1381 static string empty;
1382 map<string, string>::const_iterator it = cite_macros_.find(macro);
1383 if (it != cite_macros_.end())
1389 /////////////////////////////////////////////////////////////////////////
1393 /////////////////////////////////////////////////////////////////////////
1395 ostream & operator<<(ostream & os, PageSides p)