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/ExceptionMessage.h"
34 #include "support/FileName.h"
35 #include "support/filetools.h"
36 #include "support/gettext.h"
37 #include "support/lstrings.h"
38 #include "support/os.h"
49 using namespace lyx::support;
55 class LayoutNamesEqual : public unary_function<Layout, bool> {
57 LayoutNamesEqual(docstring const & name)
60 bool operator()(Layout const & c) const
62 return c.name() == name_;
68 // Keep the changes documented in the Customization manual.
69 int const FORMAT = 26;
72 bool layout2layout(FileName const & filename, FileName const & tempfile)
74 FileName const script = libFileSearch("scripts", "layout2layout.py");
76 LYXERR0("Could not find layout conversion "
77 "script layout2layout.py.");
81 ostringstream command;
82 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
83 << ' ' << quoteName(filename.toFilesystemEncoding())
84 << ' ' << quoteName(tempfile.toFilesystemEncoding());
85 string const command_str = command.str();
87 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
89 cmd_ret const ret = runCommand(command_str);
91 LYXERR0("Could not run layout conversion script layout2layout.py.");
98 string translateRT(TextClass::ReadType rt)
101 case TextClass::BASECLASS:
103 case TextClass::MERGE:
105 case TextClass::MODULE:
106 return "module file";
107 case TextClass::VALIDATION:
117 // This string should not be translated here,
118 // because it is a layout identifier.
119 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
122 InsetLayout DocumentClass::plain_insetlayout_;
125 /////////////////////////////////////////////////////////////////////////
129 /////////////////////////////////////////////////////////////////////////
131 TextClass::TextClass()
134 outputFormat_ = "latex";
139 pagestyle_ = "default";
140 defaultfont_ = sane_font;
141 opt_fontsize_ = "10|11|12";
142 opt_pagestyle_ = "empty|plain|headings|fancy";
143 titletype_ = TITLE_COMMAND_AFTER;
144 titlename_ = "maketitle";
146 _("Plain Layout"); // a hack to make this translatable
150 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
152 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
153 if (!lay.read(lexrc, *this)) {
154 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
158 lay.resfont = lay.font;
159 lay.resfont.realize(defaultfont_);
160 lay.reslabelfont = lay.labelfont;
161 lay.reslabelfont.realize(defaultfont_);
162 return true; // no errors
196 TC_ADDTOHTMLPREAMBLE,
207 LexerKeyword textClassTags[] = {
208 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
209 { "addtopreamble", TC_ADDTOPREAMBLE },
210 { "citeformat", TC_CITEFORMAT },
211 { "classoptions", TC_CLASSOPTIONS },
212 { "columns", TC_COLUMNS },
213 { "counter", TC_COUNTER },
214 { "defaultfont", TC_DEFAULTFONT },
215 { "defaultmodule", TC_DEFAULTMODULE },
216 { "defaultstyle", TC_DEFAULTSTYLE },
217 { "excludesmodule", TC_EXCLUDESMODULE },
218 { "float", TC_FLOAT },
219 { "format", TC_FORMAT },
220 { "htmlpreamble", TC_HTMLPREAMBLE },
221 { "htmltocsection", TC_HTMLTOCSECTION },
222 { "ifcounter", TC_IFCOUNTER },
223 { "ifstyle", TC_IFSTYLE },
224 { "input", TC_INPUT },
225 { "insetlayout", TC_INSETLAYOUT },
226 { "leftmargin", TC_LEFTMARGIN },
227 { "nofloat", TC_NOFLOAT },
228 { "nostyle", TC_NOSTYLE },
229 { "outputformat", TC_OUTPUTFORMAT },
230 { "outputtype", TC_OUTPUTTYPE },
231 { "pagestyle", TC_PAGESTYLE },
232 { "preamble", TC_PREAMBLE },
233 { "provides", TC_PROVIDES },
234 { "providesmodule", TC_PROVIDESMODULE },
235 { "requires", TC_REQUIRES },
236 { "rightmargin", TC_RIGHTMARGIN },
237 { "secnumdepth", TC_SECNUMDEPTH },
238 { "sides", TC_SIDES },
239 { "style", TC_STYLE },
240 { "titlelatexname", TC_TITLELATEXNAME },
241 { "titlelatextype", TC_TITLELATEXTYPE },
242 { "tocdepth", TC_TOCDEPTH }
248 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
250 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
251 FileName const tempfile = FileName::tempName("convert_layout");
252 bool success = layout2layout(filename, tempfile);
254 success = readWithoutConv(tempfile, rt) == OK;
255 tempfile.removeFile();
260 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
262 if (!filename.isReadableFile()) {
263 lyxerr << "Cannot read layout file `" << filename << "'."
268 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
269 to_utf8(makeDisplayPath(filename.absFilename())));
271 // Define the plain layout used in table cells, ert, etc. Note that
272 // we do this before loading any layout file, so that classes can
273 // override features of this layout if they should choose to do so.
274 if (rt == BASECLASS && !hasLayout(plain_layout_))
275 layoutlist_.push_back(createBasicLayout(plain_layout_));
277 Lexer lexrc(textClassTags);
278 lexrc.setFile(filename);
279 ReturnValues retval = read(lexrc, rt);
281 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
282 to_utf8(makeDisplayPath(filename.absFilename())));
288 bool TextClass::read(FileName const & filename, ReadType rt)
290 ReturnValues const retval = readWithoutConv(filename, rt);
291 if (retval != FORMAT_MISMATCH)
294 bool const worx = convertLayoutFormat(filename, rt);
296 LYXERR0 ("Unable to convert " << filename <<
297 " to format " << FORMAT);
304 bool TextClass::validate(std::string const & str)
307 return tc.read(str, VALIDATION);
311 bool TextClass::read(std::string const & str, ReadType rt)
313 Lexer lexrc(textClassTags);
314 istringstream is(str);
316 ReturnValues retval = read(lexrc, rt);
318 if (retval != FORMAT_MISMATCH)
321 // write the layout string to a temporary file
322 FileName const tempfile = FileName::tempName("TextClass_read");
323 ofstream os(tempfile.toFilesystemEncoding().c_str());
325 LYXERR0("Unable to create temporary file");
331 // now try to convert it
332 bool const worx = convertLayoutFormat(tempfile, rt);
334 LYXERR0("Unable to convert internal layout information to format "
337 tempfile.removeFile();
342 // Reads a textclass structure from file.
343 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
345 bool error = !lexrc.isOK();
347 // Format of files before the 'Format' tag was introduced
351 while (lexrc.isOK() && !error) {
352 int le = lexrc.lex();
355 case Lexer::LEX_FEOF:
358 case Lexer::LEX_UNDEF:
359 lexrc.printError("Unknown TextClass tag `$$Token'");
367 // used below to track whether we are in an IfStyle or IfCounter tag.
368 bool ifstyle = false;
369 bool ifcounter = false;
371 switch (static_cast<TextClassTags>(le)) {
375 format = lexrc.getInteger();
378 case TC_OUTPUTFORMAT:
380 outputFormat_ = lexrc.getString();
384 readOutputType(lexrc);
385 switch(outputType_) {
387 outputFormat_ = "latex";
390 outputFormat_ = "docbook";
393 outputFormat_ = "literate";
398 case TC_INPUT: // Include file
400 string const inc = lexrc.getString();
401 FileName tmp = libFileSearch("layouts", inc,
405 lexrc.printError("Could not find input file: " + inc);
407 } else if (!read(tmp, MERGE)) {
408 lexrc.printError("Error reading input"
409 "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!";
437 lexrc.printError(s.c_str());
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 // scan the rest and discard it
461 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!";
617 lexrc.printError(s.c_str());
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);
623 } else if (hasInsetLayout(name)) {
624 InsetLayout & il = insetlayoutlist_[name];
625 error = !il.read(lexrc, *this);
629 error = !il.read(lexrc, *this);
631 insetlayoutlist_[name] = il;
641 readCiteFormat(lexrc);
648 docstring const name = lexrc.getDocString();
650 string s = "Could not read name for counter: `$$Token' "
651 + lexrc.getString() + " is probably not valid UTF-8!";
652 lexrc.printError(s.c_str());
654 // Since we couldn't read the name, we just scan the rest
658 error = !counters_.read(lexrc, name, !ifcounter);
661 lexrc.printError("No name given for style: `$$Token'.");
668 case TC_TITLELATEXTYPE:
669 readTitleType(lexrc);
672 case TC_TITLELATEXNAME:
674 titlename_ = lexrc.getString();
679 string const nofloat = lexrc.getString();
680 floatlist_.erase(nofloat);
685 //Note that this is triggered the first time through the loop unless
686 //we hit a format tag.
687 if (format != FORMAT)
691 if (format != FORMAT)
692 return FORMAT_MISMATCH;
695 return (error ? ERROR : OK);
697 if (defaultlayout_.empty()) {
698 LYXERR0("Error: Textclass '" << name_
699 << "' is missing a defaultstyle.");
703 // Try to erase "stdinsets" from the provides_ set.
705 // Provides stdinsets 1
706 // declaration simply tells us that the standard insets have been
707 // defined. (It's found in stdinsets.inc but could also be used in
708 // user-defined files.) There isn't really any such package. So we
709 // might as well go ahead and erase it.
710 // If we do not succeed, then it was not there, which means that
711 // the textclass did not provide the definitions of the standard
712 // insets. So we need to try to load them.
713 int erased = provides_.erase("stdinsets");
715 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
718 throw ExceptionMessage(WarningException, _("Missing File"),
719 _("Could not find stdinsets.inc! This may lead to data loss!"));
721 } else if (!read(tmp, MERGE)) {
722 throw ExceptionMessage(WarningException, _("Corrupt File"),
723 _("Could not read stdinsets.inc! This may lead to data loss!"));
728 min_toclevel_ = Layout::NOT_IN_TOC;
729 max_toclevel_ = Layout::NOT_IN_TOC;
730 const_iterator lit = begin();
731 const_iterator len = end();
732 for (; lit != len; ++lit) {
733 int const toclevel = lit->toclevel;
734 if (toclevel != Layout::NOT_IN_TOC) {
735 if (min_toclevel_ == Layout::NOT_IN_TOC)
736 min_toclevel_ = toclevel;
738 min_toclevel_ = min(min_toclevel_, toclevel);
739 max_toclevel_ = max(max_toclevel_, toclevel);
742 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
743 << ", maximum is " << max_toclevel_);
745 return (error ? ERROR : OK);
749 void TextClass::readTitleType(Lexer & lexrc)
751 LexerKeyword titleTypeTags[] = {
752 { "commandafter", TITLE_COMMAND_AFTER },
753 { "environment", TITLE_ENVIRONMENT }
756 PushPopHelper pph(lexrc, titleTypeTags);
758 int le = lexrc.lex();
760 case Lexer::LEX_UNDEF:
761 lexrc.printError("Unknown output type `$$Token'");
763 case TITLE_COMMAND_AFTER:
764 case TITLE_ENVIRONMENT:
765 titletype_ = static_cast<TitleLatexType>(le);
768 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
774 void TextClass::readOutputType(Lexer & lexrc)
776 LexerKeyword outputTypeTags[] = {
777 { "docbook", DOCBOOK },
779 { "literate", LITERATE }
782 PushPopHelper pph(lexrc, outputTypeTags);
784 int le = lexrc.lex();
786 case Lexer::LEX_UNDEF:
787 lexrc.printError("Unknown output type `$$Token'");
792 outputType_ = static_cast<OutputType>(le);
795 LYXERR0("Unhandled value " << le);
801 void TextClass::readClassOptions(Lexer & lexrc)
811 LexerKeyword classOptionsTags[] = {
813 {"fontsize", CO_FONTSIZE },
814 {"header", CO_HEADER },
815 {"other", CO_OTHER },
816 {"pagestyle", CO_PAGESTYLE }
819 lexrc.pushTable(classOptionsTags);
821 while (!getout && lexrc.isOK()) {
822 int le = lexrc.lex();
824 case Lexer::LEX_UNDEF:
825 lexrc.printError("Unknown ClassOption tag `$$Token'");
832 opt_fontsize_ = rtrim(lexrc.getString());
836 opt_pagestyle_ = rtrim(lexrc.getString());
840 if (options_.empty())
841 options_ = lexrc.getString();
843 options_ += ',' + lexrc.getString();
847 class_header_ = subst(lexrc.getString(), """, "\"");
858 void TextClass::readCiteFormat(Lexer & lexrc)
862 while (lexrc.isOK()) {
864 etype = lexrc.getString();
865 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
868 definition = lexrc.getString();
869 char initchar = etype[0];
872 if (initchar == '!' || initchar == '_')
873 cite_macros_[etype] = definition;
875 cite_formats_[etype] = definition;
880 void TextClass::readFloat(Lexer & lexrc)
899 LexerKeyword floatTags[] = {
901 { "extension", FT_EXT },
902 { "guiname", FT_NAME },
903 { "htmlattr", FT_HTMLATTR },
904 { "htmlstyle", FT_HTMLSTYLE },
905 { "htmltag", FT_HTMLTAG },
906 { "listcommand", FT_LISTCOMMAND },
907 { "listname", FT_LISTNAME },
908 { "needsfloatpkg", FT_NEEDSFLOAT },
909 { "numberwithin", FT_WITHIN },
910 { "placement", FT_PLACEMENT },
911 { "refprefix", FT_REFPREFIX },
912 { "style", FT_STYLE },
916 lexrc.pushTable(floatTags);
930 bool needsfloat = true;
933 while (!getout && lexrc.isOK()) {
934 int le = lexrc.lex();
936 case Lexer::LEX_UNDEF:
937 lexrc.printError("Unknown float tag `$$Token'");
944 type = lexrc.getString();
945 if (floatlist_.typeExist(type)) {
946 Floating const & fl = floatlist_.getType(type);
947 placement = fl.placement();
949 within = fl.within();
952 listname = fl.listName();
953 needsfloat = fl.needsFloatPkg();
954 listcommand = fl.listCommand();
955 refprefix = fl.refPrefix();
960 name = lexrc.getString();
964 placement = lexrc.getString();
968 ext = lexrc.getString();
972 within = lexrc.getString();
973 if (within == "none")
978 style = lexrc.getString();
982 listcommand = lexrc.getString();
986 refprefix = lexrc.getString();
990 listname = lexrc.getString();
994 needsfloat = lexrc.getBool();
998 htmlattr = lexrc.getString();
1002 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1006 htmltag = lexrc.getString();
1014 // Here we have a full float if getout == true
1016 if (!needsfloat && listcommand.empty())
1017 LYXERR0("The layout does not provide a list command " <<
1018 "for the builtin float `" << type << "'. LyX will " <<
1019 "not be able to produce a float list.");
1020 Floating fl(type, placement, ext, within, style, name,
1021 listname, listcommand, refprefix,
1022 htmltag, htmlattr, htmlstyle, needsfloat);
1023 floatlist_.newFloat(fl);
1024 // each float has its own counter
1025 counters_.newCounter(from_ascii(type), from_ascii(within),
1026 docstring(), docstring());
1027 // also define sub-float counters
1028 docstring const subtype = "sub-" + from_ascii(type);
1029 counters_.newCounter(subtype, from_ascii(type),
1030 "\\alph{" + subtype + "}", docstring());
1037 string const & TextClass::prerequisites() const
1039 if (contains(prerequisites_, ',')) {
1040 vector<string> const pres = getVectorFromString(prerequisites_);
1041 prerequisites_ = getStringFromVector(pres, "\n\t");
1043 return prerequisites_;
1046 bool TextClass::hasLayout(docstring const & n) const
1048 docstring const name = n.empty() ? defaultLayoutName() : n;
1050 return find_if(layoutlist_.begin(), layoutlist_.end(),
1051 LayoutNamesEqual(name))
1052 != layoutlist_.end();
1056 bool TextClass::hasInsetLayout(docstring const & n) const
1060 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1061 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1062 for (; it != en; ++it)
1069 Layout const & TextClass::operator[](docstring const & name) const
1071 LASSERT(!name.empty(), /**/);
1074 find_if(begin(), end(), LayoutNamesEqual(name));
1077 lyxerr << "We failed to find the layout '" << to_utf8(name)
1078 << "' in the layout list. You MUST investigate!"
1080 for (const_iterator cit = begin(); cit != end(); ++cit)
1081 lyxerr << " " << to_utf8(cit->name()) << endl;
1083 // we require the name to exist
1084 LASSERT(false, /**/);
1091 Layout & TextClass::operator[](docstring const & name)
1093 LASSERT(!name.empty(), /**/);
1095 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1098 LYXERR0("We failed to find the layout '" << to_utf8(name)
1099 << "' in the layout list. You MUST investigate!");
1100 for (const_iterator cit = begin(); cit != end(); ++cit)
1101 LYXERR0(" " << to_utf8(cit->name()));
1103 // we require the name to exist
1104 LASSERT(false, /**/);
1111 bool TextClass::deleteLayout(docstring const & name)
1113 if (name == defaultLayoutName() || name == plainLayoutName())
1116 LayoutList::iterator it =
1117 remove_if(layoutlist_.begin(), layoutlist_.end(),
1118 LayoutNamesEqual(name));
1120 LayoutList::iterator end = layoutlist_.end();
1121 bool const ret = (it != end);
1122 layoutlist_.erase(it, end);
1127 // Load textclass info if not loaded yet
1128 bool TextClass::load(string const & path) const
1133 // Read style-file, provided path is searched before the system ones
1134 // If path is a file, it is loaded directly.
1135 FileName layout_file(path);
1136 if (!path.empty() && !layout_file.isReadableFile())
1137 layout_file = FileName(addName(path, name_ + ".layout"));
1138 if (layout_file.empty() || !layout_file.exists())
1139 layout_file = libFileSearch("layouts", name_, "layout");
1140 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1143 lyxerr << "Error reading `"
1144 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1145 << "'\n(Check `" << name_
1146 << "')\nCheck your installation and "
1147 "try Options/Reconfigure..." << endl;
1154 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1157 layoutlist_.push_back(createBasicLayout(n, true));
1161 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1163 // FIXME The fix for the InsetLayout part of 4812 would be here:
1164 // Add the InsetLayout to the document class if it is not found.
1166 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1167 while (!n.empty()) {
1168 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1169 if (cit != cen && cit->first == n)
1171 size_t i = n.find(':');
1172 if (i == string::npos)
1176 return plain_insetlayout_;
1180 docstring const & TextClass::defaultLayoutName() const
1182 // This really should come from the actual layout... (Lgb)
1183 return defaultlayout_;
1187 Layout const & TextClass::defaultLayout() const
1189 return operator[](defaultLayoutName());
1193 bool TextClass::isDefaultLayout(Layout const & layout) const
1195 return layout.name() == defaultLayoutName();
1199 bool TextClass::isPlainLayout(Layout const & layout) const
1201 return layout.name() == plainLayoutName();
1205 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1207 static Layout * defaultLayout = NULL;
1209 if (defaultLayout) {
1210 defaultLayout->setUnknown(unknown);
1211 defaultLayout->setName(name);
1212 return *defaultLayout;
1215 static char const * s = "Margin Static\n"
1216 "LatexType Paragraph\n"
1219 "AlignPossible Left, Right, Center\n"
1220 "LabelType No_Label\n"
1222 istringstream ss(s);
1223 Lexer lex(textClassTags);
1225 defaultLayout = new Layout;
1226 defaultLayout->setUnknown(unknown);
1227 defaultLayout->setName(name);
1228 if (!readStyle(lex, *defaultLayout)) {
1229 // The only way this happens is because the hardcoded layout above
1231 LASSERT(false, /**/);
1233 return *defaultLayout;
1237 /////////////////////////////////////////////////////////////////////////
1239 // DocumentClassBundle
1241 /////////////////////////////////////////////////////////////////////////
1243 DocumentClassBundle::~DocumentClassBundle()
1245 for (size_t i = 0; i != documentClasses_.size(); ++i)
1246 delete documentClasses_[i];
1247 documentClasses_.clear();
1250 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1252 DocumentClass * dc = new DocumentClass(baseClass);
1253 documentClasses_.push_back(dc);
1254 return *documentClasses_.back();
1258 DocumentClassBundle & DocumentClassBundle::get()
1260 static DocumentClassBundle singleton;
1265 DocumentClass & DocumentClassBundle::makeDocumentClass(
1266 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1268 DocumentClass & doc_class = newClass(baseClass);
1269 LayoutModuleList::const_iterator it = modlist.begin();
1270 LayoutModuleList::const_iterator en = modlist.end();
1271 for (; it != en; it++) {
1272 string const modName = *it;
1273 LyXModule * lm = theModuleList[modName];
1275 docstring const msg =
1276 bformat(_("The module %1$s has been requested by\n"
1277 "this document but has not been found in the list of\n"
1278 "available modules. If you recently installed it, you\n"
1279 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1280 ExceptionMessage(WarningException,_("Module not available"),
1281 msg + _("Some layouts may not be available."));
1284 if (!lm->isAvailable()) {
1285 docstring const msg =
1286 bformat(_("The module %1$s requires a package that is\n"
1287 "not available in your LaTeX installation. LaTeX output\n"
1288 "may not be possible.\n"), from_utf8(modName));
1289 ExceptionMessage(WarningException, _("Package not available"), msg);
1291 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1292 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1293 docstring const msg =
1294 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1295 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1302 /////////////////////////////////////////////////////////////////////////
1306 /////////////////////////////////////////////////////////////////////////
1308 DocumentClass::DocumentClass(LayoutFile const & tc)
1313 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1315 LayoutList::const_iterator it = layoutlist_.begin();
1316 LayoutList::const_iterator end = layoutlist_.end();
1317 for (; it != end; ++it)
1318 if (it->latexname() == lay)
1324 bool DocumentClass::provides(string const & p) const
1326 return provides_.find(p) != provides_.end();
1330 bool DocumentClass::hasTocLevels() const
1332 return min_toclevel_ != Layout::NOT_IN_TOC;
1336 Layout const & DocumentClass::htmlTOCLayout() const
1338 if (html_toc_section_.empty()) {
1339 // we're going to look for the layout with the minimum toclevel
1340 TextClass::LayoutList::const_iterator lit = begin();
1341 TextClass::LayoutList::const_iterator const len = end();
1342 int minlevel = 1000;
1343 Layout const * lay = NULL;
1344 for (; lit != len; ++lit) {
1345 int const level = lit->toclevel;
1346 // we don't want Part
1347 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1353 html_toc_section_ = lay->name();
1355 // hmm. that is very odd, so we'll do our best
1356 html_toc_section_ = defaultLayoutName();
1358 return operator[](html_toc_section_);
1362 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1364 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1366 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1367 if (it != cite_formats_.end())
1369 return default_format;
1373 string const & DocumentClass::getCiteMacro(string const & macro) const
1375 static string empty;
1376 map<string, string>::const_iterator it = cite_macros_.find(macro);
1377 if (it != cite_macros_.end())
1383 /////////////////////////////////////////////////////////////////////////
1387 /////////////////////////////////////////////////////////////////////////
1389 ostream & operator<<(ostream & os, PageSides p)