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 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());
1044 string const & TextClass::prerequisites() const
1046 if (contains(prerequisites_, ',')) {
1047 vector<string> const pres = getVectorFromString(prerequisites_);
1048 prerequisites_ = getStringFromVector(pres, "\n\t");
1050 return prerequisites_;
1053 bool TextClass::hasLayout(docstring const & n) const
1055 docstring const name = n.empty() ? defaultLayoutName() : n;
1057 return find_if(layoutlist_.begin(), layoutlist_.end(),
1058 LayoutNamesEqual(name))
1059 != layoutlist_.end();
1063 bool TextClass::hasInsetLayout(docstring const & n) const
1067 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1068 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1069 for (; it != en; ++it)
1076 Layout const & TextClass::operator[](docstring const & name) const
1078 LASSERT(!name.empty(), /**/);
1081 find_if(begin(), end(), LayoutNamesEqual(name));
1084 lyxerr << "We failed to find the layout '" << to_utf8(name)
1085 << "' in the layout list. You MUST investigate!"
1087 for (const_iterator cit = begin(); cit != end(); ++cit)
1088 lyxerr << " " << to_utf8(cit->name()) << endl;
1090 // we require the name to exist
1091 LASSERT(false, /**/);
1098 Layout & TextClass::operator[](docstring const & name)
1100 LASSERT(!name.empty(), /**/);
1102 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1105 LYXERR0("We failed to find the layout '" << to_utf8(name)
1106 << "' in the layout list. You MUST investigate!");
1107 for (const_iterator cit = begin(); cit != end(); ++cit)
1108 LYXERR0(" " << to_utf8(cit->name()));
1110 // we require the name to exist
1111 LASSERT(false, /**/);
1118 bool TextClass::deleteLayout(docstring const & name)
1120 if (name == defaultLayoutName() || name == plainLayoutName())
1123 LayoutList::iterator it =
1124 remove_if(layoutlist_.begin(), layoutlist_.end(),
1125 LayoutNamesEqual(name));
1127 LayoutList::iterator end = layoutlist_.end();
1128 bool const ret = (it != end);
1129 layoutlist_.erase(it, end);
1134 // Load textclass info if not loaded yet
1135 bool TextClass::load(string const & path) const
1140 // Read style-file, provided path is searched before the system ones
1141 // If path is a file, it is loaded directly.
1142 FileName layout_file(path);
1143 if (!path.empty() && !layout_file.isReadableFile())
1144 layout_file = FileName(addName(path, name_ + ".layout"));
1145 if (layout_file.empty() || !layout_file.exists())
1146 layout_file = libFileSearch("layouts", name_, "layout");
1147 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1150 lyxerr << "Error reading `"
1151 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1152 << "'\n(Check `" << name_
1153 << "')\nCheck your installation and "
1154 "try Options/Reconfigure..."
1162 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1165 layoutlist_.push_back(createBasicLayout(n, true));
1169 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1171 // FIXME The fix for the InsetLayout part of 4812 would be here:
1172 // Add the InsetLayout to the document class if it is not found.
1174 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1175 while (!n.empty()) {
1176 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1177 if (cit != cen && cit->first == n)
1179 size_t i = n.find(':');
1180 if (i == string::npos)
1184 return plain_insetlayout_;
1188 docstring const & TextClass::defaultLayoutName() const
1190 // This really should come from the actual layout... (Lgb)
1191 return defaultlayout_;
1195 Layout const & TextClass::defaultLayout() const
1197 return operator[](defaultLayoutName());
1201 bool TextClass::isDefaultLayout(Layout const & layout) const
1203 return layout.name() == defaultLayoutName();
1207 bool TextClass::isPlainLayout(Layout const & layout) const
1209 return layout.name() == plainLayoutName();
1213 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1215 static Layout * defaultLayout = NULL;
1217 if (defaultLayout) {
1218 defaultLayout->setUnknown(unknown);
1219 defaultLayout->setName(name);
1220 return *defaultLayout;
1223 static char const * s = "Margin Static\n"
1224 "LatexType Paragraph\n"
1227 "AlignPossible Left, Right, Center\n"
1228 "LabelType No_Label\n"
1230 istringstream ss(s);
1231 Lexer lex(textClassTags);
1233 defaultLayout = new Layout;
1234 defaultLayout->setUnknown(unknown);
1235 defaultLayout->setName(name);
1236 if (!readStyle(lex, *defaultLayout)) {
1237 // The only way this happens is because the hardcoded layout above
1239 LASSERT(false, /**/);
1241 return *defaultLayout;
1245 /////////////////////////////////////////////////////////////////////////
1247 // DocumentClassBundle
1249 /////////////////////////////////////////////////////////////////////////
1251 DocumentClassBundle::~DocumentClassBundle()
1253 for (size_t i = 0; i != documentClasses_.size(); ++i)
1254 delete documentClasses_[i];
1255 documentClasses_.clear();
1258 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1260 DocumentClass * dc = new DocumentClass(baseClass);
1261 documentClasses_.push_back(dc);
1262 return *documentClasses_.back();
1266 DocumentClassBundle & DocumentClassBundle::get()
1268 static DocumentClassBundle singleton;
1273 DocumentClass & DocumentClassBundle::makeDocumentClass(
1274 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1276 DocumentClass & doc_class = newClass(baseClass);
1277 LayoutModuleList::const_iterator it = modlist.begin();
1278 LayoutModuleList::const_iterator en = modlist.end();
1279 for (; it != en; it++) {
1280 string const modName = *it;
1281 LyXModule * lm = theModuleList[modName];
1283 docstring const msg =
1284 bformat(_("The module %1$s has been requested by\n"
1285 "this document but has not been found in the list of\n"
1286 "available modules. If you recently installed it, you\n"
1287 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1288 frontend::Alert::warning(_("Module not available"), msg);
1291 if (!lm->isAvailable()) {
1292 docstring const msg =
1293 bformat(_("The module %1$s requires a package that is\n"
1294 "not available in your LaTeX installation. LaTeX output\n"
1295 "may not be possible.\n"), from_utf8(modName));
1296 frontend::Alert::warning(_("Package not available"), msg);
1298 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1299 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1300 docstring const msg =
1301 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1302 frontend::Alert::warning(_("Read Error"), msg);
1309 /////////////////////////////////////////////////////////////////////////
1313 /////////////////////////////////////////////////////////////////////////
1315 DocumentClass::DocumentClass(LayoutFile const & tc)
1320 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1322 LayoutList::const_iterator it = layoutlist_.begin();
1323 LayoutList::const_iterator end = layoutlist_.end();
1324 for (; it != end; ++it)
1325 if (it->latexname() == lay)
1331 bool DocumentClass::provides(string const & p) const
1333 return provides_.find(p) != provides_.end();
1337 bool DocumentClass::hasTocLevels() const
1339 return min_toclevel_ != Layout::NOT_IN_TOC;
1343 Layout const & DocumentClass::htmlTOCLayout() const
1345 if (html_toc_section_.empty()) {
1346 // we're going to look for the layout with the minimum toclevel
1347 TextClass::LayoutList::const_iterator lit = begin();
1348 TextClass::LayoutList::const_iterator const len = end();
1349 int minlevel = 1000;
1350 Layout const * lay = NULL;
1351 for (; lit != len; ++lit) {
1352 int const level = lit->toclevel;
1353 // we don't want Part
1354 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1360 html_toc_section_ = lay->name();
1362 // hmm. that is very odd, so we'll do our best
1363 html_toc_section_ = defaultLayoutName();
1365 return operator[](html_toc_section_);
1369 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1371 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1373 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1374 if (it != cite_formats_.end())
1376 return default_format;
1380 string const & DocumentClass::getCiteMacro(string const & macro) const
1382 static string empty;
1383 map<string, string>::const_iterator it = cite_macros_.find(macro);
1384 if (it != cite_macros_.end())
1390 /////////////////////////////////////////////////////////////////////////
1394 /////////////////////////////////////////////////////////////////////////
1396 ostream & operator<<(ostream & os, PageSides p)