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 = 33;
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
206 TC_ADDTOHTMLPREAMBLE,
217 LexerKeyword textClassTags[] = {
218 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
219 { "addtopreamble", TC_ADDTOPREAMBLE },
220 { "citeformat", TC_CITEFORMAT },
221 { "classoptions", TC_CLASSOPTIONS },
222 { "columns", TC_COLUMNS },
223 { "counter", TC_COUNTER },
224 { "defaultfont", TC_DEFAULTFONT },
225 { "defaultmodule", TC_DEFAULTMODULE },
226 { "defaultstyle", TC_DEFAULTSTYLE },
227 { "excludesmodule", TC_EXCLUDESMODULE },
228 { "float", TC_FLOAT },
229 { "format", TC_FORMAT },
230 { "htmlpreamble", TC_HTMLPREAMBLE },
231 { "htmltocsection", TC_HTMLTOCSECTION },
232 { "ifcounter", TC_IFCOUNTER },
233 { "ifstyle", TC_IFSTYLE },
234 { "input", TC_INPUT },
235 { "insetlayout", TC_INSETLAYOUT },
236 { "leftmargin", TC_LEFTMARGIN },
237 { "nocounter", TC_NOCOUNTER },
238 { "nofloat", TC_NOFLOAT },
239 { "nostyle", TC_NOSTYLE },
240 { "outputformat", TC_OUTPUTFORMAT },
241 { "outputtype", TC_OUTPUTTYPE },
242 { "pagestyle", TC_PAGESTYLE },
243 { "preamble", TC_PREAMBLE },
244 { "provides", TC_PROVIDES },
245 { "providesmodule", TC_PROVIDESMODULE },
246 { "requires", TC_REQUIRES },
247 { "rightmargin", TC_RIGHTMARGIN },
248 { "secnumdepth", TC_SECNUMDEPTH },
249 { "sides", TC_SIDES },
250 { "style", TC_STYLE },
251 { "titlelatexname", TC_TITLELATEXNAME },
252 { "titlelatextype", TC_TITLELATEXTYPE },
253 { "tocdepth", TC_TOCDEPTH }
259 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
261 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
262 FileName const tempfile = FileName::tempName("convert_layout");
263 bool success = layout2layout(filename, tempfile);
265 success = readWithoutConv(tempfile, rt) == OK;
266 tempfile.removeFile();
271 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
273 if (!filename.isReadableFile()) {
274 lyxerr << "Cannot read layout file `" << filename << "'."
279 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
280 to_utf8(makeDisplayPath(filename.absFileName())));
282 // Define the plain layout used in table cells, ert, etc. Note that
283 // we do this before loading any layout file, so that classes can
284 // override features of this layout if they should choose to do so.
285 if (rt == BASECLASS && !hasLayout(plain_layout_))
286 layoutlist_.push_back(createBasicLayout(plain_layout_));
288 Lexer lexrc(textClassTags);
289 lexrc.setFile(filename);
290 ReturnValues retval = read(lexrc, rt);
292 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
293 to_utf8(makeDisplayPath(filename.absFileName())));
299 bool TextClass::read(FileName const & filename, ReadType rt)
301 ReturnValues const retval = readWithoutConv(filename, rt);
302 if (retval != FORMAT_MISMATCH)
305 bool const worx = convertLayoutFormat(filename, rt);
307 LYXERR0 ("Unable to convert " << filename <<
308 " to format " << LAYOUT_FORMAT);
315 bool TextClass::validate(std::string const & str)
318 return tc.read(str, VALIDATION);
322 bool TextClass::read(std::string const & str, ReadType rt)
324 Lexer lexrc(textClassTags);
325 istringstream is(str);
327 ReturnValues retval = read(lexrc, rt);
329 if (retval != FORMAT_MISMATCH)
332 // write the layout string to a temporary file
333 FileName const tempfile = FileName::tempName("TextClass_read");
334 ofstream os(tempfile.toFilesystemEncoding().c_str());
336 LYXERR0("Unable to create temporary file");
342 // now try to convert it
343 bool const worx = convertLayoutFormat(tempfile, rt);
345 LYXERR0("Unable to convert internal layout information to format "
348 tempfile.removeFile();
353 // Reads a textclass structure from file.
354 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
359 // Format of files before the 'Format' tag was introduced
364 while (lexrc.isOK() && !error) {
365 int le = lexrc.lex();
368 case Lexer::LEX_FEOF:
371 case Lexer::LEX_UNDEF:
372 lexrc.printError("Unknown TextClass tag `$$Token'");
380 // used below to track whether we are in an IfStyle or IfCounter tag.
381 bool ifstyle = false;
382 bool ifcounter = false;
384 switch (static_cast<TextClassTags>(le)) {
388 format = lexrc.getInteger();
391 case TC_OUTPUTFORMAT:
393 outputFormat_ = lexrc.getString();
397 readOutputType(lexrc);
398 switch(outputType_) {
400 outputFormat_ = "latex";
403 outputFormat_ = "docbook";
406 outputFormat_ = "literate";
411 case TC_INPUT: // Include file
413 string const inc = lexrc.getString();
414 FileName tmp = libFileSearch("layouts", inc,
418 lexrc.printError("Could not find input file: " + inc);
420 } else if (!read(tmp, MERGE)) {
421 lexrc.printError("Error reading input file: " + tmp.absFileName());
427 case TC_DEFAULTSTYLE:
429 docstring const name = from_utf8(subst(lexrc.getString(),
431 defaultlayout_ = name;
440 lexrc.printError("No name given for style: `$$Token'.");
444 docstring const name = from_utf8(subst(lexrc.getString(),
447 string s = "Could not read name for style: `$$Token' "
448 + lexrc.getString() + " is probably not valid UTF-8!";
451 // Since we couldn't read the name, we just scan the rest
452 // of the style and discard it.
453 error = !readStyle(lexrc, lay);
454 } else if (hasLayout(name)) {
455 Layout & lay = operator[](name);
456 error = !readStyle(lexrc, lay);
457 } else if (!ifstyle) {
459 layout.setName(name);
460 error = !readStyle(lexrc, layout);
462 layoutlist_.push_back(layout);
464 if (defaultlayout_.empty()) {
465 // We do not have a default layout yet, so we choose
466 // the first layout we encounter.
467 defaultlayout_ = name;
471 // this was an ifstyle where we didn't have the style
472 // scan the rest and discard it
474 readStyle(lexrc, lay);
484 docstring const style = from_utf8(subst(lexrc.getString(),
486 if (!deleteLayout(style))
487 lyxerr << "Cannot delete style `"
488 << to_utf8(style) << '\'' << endl;
494 columns_ = lexrc.getInteger();
499 switch (lexrc.getInteger()) {
500 case 1: sides_ = OneSide; break;
501 case 2: sides_ = TwoSides; break;
503 lyxerr << "Impossible number of page"
504 " sides, setting to one."
514 pagestyle_ = rtrim(lexrc.getString());
518 defaultfont_ = lyxRead(lexrc);
519 if (!defaultfont_.resolved()) {
520 lexrc.printError("Warning: defaultfont should "
521 "be fully instantiated!");
522 defaultfont_.realize(sane_font);
528 secnumdepth_ = lexrc.getInteger();
533 tocdepth_ = lexrc.getInteger();
536 // First step to support options
537 case TC_CLASSOPTIONS:
538 readClassOptions(lexrc);
542 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
545 case TC_HTMLPREAMBLE:
546 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
549 case TC_HTMLTOCSECTION:
550 html_toc_section_ = from_utf8(trim(lexrc.getString()));
553 case TC_ADDTOPREAMBLE:
554 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
557 case TC_ADDTOHTMLPREAMBLE:
558 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
563 string const feature = lexrc.getString();
565 if (lexrc.getInteger())
566 provides_.insert(feature);
568 provides_.erase(feature);
574 vector<string> const req
575 = getVectorFromString(lexrc.getString());
576 requires_.insert(req.begin(), req.end());
580 case TC_DEFAULTMODULE: {
582 string const module = lexrc.getString();
583 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
584 default_modules_.push_back(module);
588 case TC_PROVIDESMODULE: {
590 string const module = lexrc.getString();
591 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
592 provided_modules_.push_back(module);
596 case TC_EXCLUDESMODULE: {
598 string const module = lexrc.getString();
599 // modules already have their own way to exclude other modules
601 LYXERR0("ExcludesModule tag cannot be used in a module!");
604 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
605 excluded_modules_.push_back(module);
609 case TC_LEFTMARGIN: // left margin type
611 leftmargin_ = lexrc.getDocString();
614 case TC_RIGHTMARGIN: // right margin type
616 rightmargin_ = lexrc.getDocString();
619 case TC_INSETLAYOUT: {
621 lexrc.printError("No name given for InsetLayout: `$$Token'.");
625 docstring const name = subst(lexrc.getDocString(), '_', ' ');
627 string s = "Could not read name for InsetLayout: `$$Token' "
628 + lexrc.getString() + " is probably not valid UTF-8!";
631 // Since we couldn't read the name, we just scan the rest
632 // of the style and discard it.
633 il.read(lexrc, *this);
634 // Let's try to continue rather than abort.
636 } else if (hasInsetLayout(name)) {
637 InsetLayout & il = insetlayoutlist_[name];
638 error = !il.read(lexrc, *this);
642 error = !il.read(lexrc, *this);
644 insetlayoutlist_[name] = il;
650 error = !readFloat(lexrc);
654 readCiteFormat(lexrc);
659 docstring const cnt = lexrc.getDocString();
660 if (!counters_.remove(cnt))
661 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
669 docstring const name = lexrc.getDocString();
671 string s = "Could not read name for counter: `$$Token' "
672 + lexrc.getString() + " is probably not valid UTF-8!";
673 lexrc.printError(s.c_str());
675 // Since we couldn't read the name, we just scan the rest
679 error = !counters_.read(lexrc, name, !ifcounter);
682 lexrc.printError("No name given for style: `$$Token'.");
689 case TC_TITLELATEXTYPE:
690 readTitleType(lexrc);
693 case TC_TITLELATEXNAME:
695 titlename_ = lexrc.getString();
700 string const nofloat = lexrc.getString();
701 floatlist_.erase(nofloat);
706 // Note that this is triggered the first time through the loop unless
707 // we hit a format tag.
708 if (format != LAYOUT_FORMAT)
709 return FORMAT_MISMATCH;
712 // at present, we abort if we encounter an error,
713 // so there is no point continuing.
718 return (error ? ERROR : OK);
720 if (defaultlayout_.empty()) {
721 LYXERR0("Error: Textclass '" << name_
722 << "' is missing a defaultstyle.");
726 // Try to erase "stdinsets" from the provides_ set.
728 // Provides stdinsets 1
729 // declaration simply tells us that the standard insets have been
730 // defined. (It's found in stdinsets.inc but could also be used in
731 // user-defined files.) There isn't really any such package. So we
732 // might as well go ahead and erase it.
733 // If we do not succeed, then it was not there, which means that
734 // the textclass did not provide the definitions of the standard
735 // insets. So we need to try to load them.
736 int erased = provides_.erase("stdinsets");
738 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
741 frontend::Alert::warning(_("Missing File"),
742 _("Could not find stdinsets.inc! This may lead to data loss!"));
744 } else if (!read(tmp, MERGE)) {
745 frontend::Alert::warning(_("Corrupt File"),
746 _("Could not read stdinsets.inc! This may lead to data loss!"));
751 min_toclevel_ = Layout::NOT_IN_TOC;
752 max_toclevel_ = Layout::NOT_IN_TOC;
753 const_iterator lit = begin();
754 const_iterator len = end();
755 for (; lit != len; ++lit) {
756 int const toclevel = lit->toclevel;
757 if (toclevel != Layout::NOT_IN_TOC) {
758 if (min_toclevel_ == Layout::NOT_IN_TOC)
759 min_toclevel_ = toclevel;
761 min_toclevel_ = min(min_toclevel_, toclevel);
762 max_toclevel_ = max(max_toclevel_, toclevel);
765 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
766 << ", maximum is " << max_toclevel_);
768 return (error ? ERROR : OK);
772 void TextClass::readTitleType(Lexer & lexrc)
774 LexerKeyword titleTypeTags[] = {
775 { "commandafter", TITLE_COMMAND_AFTER },
776 { "environment", TITLE_ENVIRONMENT }
779 PushPopHelper pph(lexrc, titleTypeTags);
781 int le = lexrc.lex();
783 case Lexer::LEX_UNDEF:
784 lexrc.printError("Unknown output type `$$Token'");
786 case TITLE_COMMAND_AFTER:
787 case TITLE_ENVIRONMENT:
788 titletype_ = static_cast<TitleLatexType>(le);
791 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
797 void TextClass::readOutputType(Lexer & lexrc)
799 LexerKeyword outputTypeTags[] = {
800 { "docbook", DOCBOOK },
802 { "literate", LITERATE }
805 PushPopHelper pph(lexrc, outputTypeTags);
807 int le = lexrc.lex();
809 case Lexer::LEX_UNDEF:
810 lexrc.printError("Unknown output type `$$Token'");
815 outputType_ = static_cast<OutputType>(le);
818 LYXERR0("Unhandled value " << le);
824 void TextClass::readClassOptions(Lexer & lexrc)
834 LexerKeyword classOptionsTags[] = {
836 {"fontsize", CO_FONTSIZE },
837 {"header", CO_HEADER },
838 {"other", CO_OTHER },
839 {"pagestyle", CO_PAGESTYLE }
842 lexrc.pushTable(classOptionsTags);
844 while (!getout && lexrc.isOK()) {
845 int le = lexrc.lex();
847 case Lexer::LEX_UNDEF:
848 lexrc.printError("Unknown ClassOption tag `$$Token'");
856 opt_fontsize_ = rtrim(lexrc.getString());
860 opt_pagestyle_ = rtrim(lexrc.getString());
864 if (options_.empty())
865 options_ = lexrc.getString();
867 options_ += ',' + lexrc.getString();
871 class_header_ = subst(lexrc.getString(), """, "\"");
882 void TextClass::readCiteFormat(Lexer & lexrc)
886 while (lexrc.isOK()) {
888 etype = lexrc.getString();
889 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
892 definition = lexrc.getString();
893 char initchar = etype[0];
896 if (initchar == '!' || initchar == '_')
897 cite_macros_[etype] = definition;
899 cite_formats_[etype] = definition;
904 bool TextClass::readFloat(Lexer & lexrc)
924 LexerKeyword floatTags[] = {
926 { "extension", FT_EXT },
927 { "guiname", FT_NAME },
928 { "htmlattr", FT_HTMLATTR },
929 { "htmlstyle", FT_HTMLSTYLE },
930 { "htmltag", FT_HTMLTAG },
931 { "ispredefined", FT_PREDEFINED },
932 { "listcommand", FT_LISTCOMMAND },
933 { "listname", FT_LISTNAME },
934 { "numberwithin", FT_WITHIN },
935 { "placement", FT_PLACEMENT },
936 { "refprefix", FT_REFPREFIX },
937 { "style", FT_STYLE },
939 { "usesfloatpkg", FT_USESFLOAT }
942 lexrc.pushTable(floatTags);
956 bool usesfloat = true;
957 bool ispredefined = false;
960 while (!getout && lexrc.isOK()) {
961 int le = lexrc.lex();
963 case Lexer::LEX_UNDEF:
964 lexrc.printError("Unknown float tag `$$Token'");
972 type = lexrc.getString();
973 if (floatlist_.typeExist(type)) {
974 Floating const & fl = floatlist_.getType(type);
975 placement = fl.placement();
977 within = fl.within();
980 listname = fl.listName();
981 usesfloat = fl.usesFloatPkg();
982 ispredefined = fl.isPredefined();
983 listcommand = fl.listCommand();
984 refprefix = fl.refPrefix();
989 name = lexrc.getString();
993 placement = lexrc.getString();
997 ext = lexrc.getString();
1001 within = lexrc.getString();
1002 if (within == "none")
1007 style = lexrc.getString();
1009 case FT_LISTCOMMAND:
1011 listcommand = lexrc.getString();
1015 refprefix = lexrc.getString();
1019 listname = lexrc.getString();
1023 usesfloat = lexrc.getBool();
1027 ispredefined = lexrc.getBool();
1031 htmlattr = lexrc.getString();
1035 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1039 htmltag = lexrc.getString();
1049 // Here we have a full float if getout == true
1051 if (!usesfloat && listcommand.empty()) {
1052 // if this float uses the same auxfile as an existing one,
1053 // there is no need for it to provide a list command.
1054 FloatList::const_iterator it = floatlist_.begin();
1055 FloatList::const_iterator en = floatlist_.end();
1056 bool found_ext = false;
1057 for (; it != en; ++it) {
1058 if (it->second.ext() == ext) {
1064 LYXERR0("The layout does not provide a list command " <<
1065 "for the float `" << type << "'. LyX will " <<
1066 "not be able to produce a float list.");
1068 Floating fl(type, placement, ext, within, style, name,
1069 listname, listcommand, refprefix,
1070 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1071 floatlist_.newFloat(fl);
1072 // each float has its own counter
1073 counters_.newCounter(from_ascii(type), from_ascii(within),
1074 docstring(), docstring());
1075 // also define sub-float counters
1076 docstring const subtype = "sub-" + from_ascii(type);
1077 counters_.newCounter(subtype, from_ascii(type),
1078 "\\alph{" + subtype + "}", docstring());
1084 string const & TextClass::prerequisites() const
1086 if (contains(prerequisites_, ',')) {
1087 vector<string> const pres = getVectorFromString(prerequisites_);
1088 prerequisites_ = getStringFromVector(pres, "\n\t");
1090 return prerequisites_;
1093 bool TextClass::hasLayout(docstring const & n) const
1095 docstring const name = n.empty() ? defaultLayoutName() : n;
1097 return find_if(layoutlist_.begin(), layoutlist_.end(),
1098 LayoutNamesEqual(name))
1099 != layoutlist_.end();
1103 bool TextClass::hasInsetLayout(docstring const & n) const
1107 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1108 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1109 for (; it != en; ++it)
1116 Layout const & TextClass::operator[](docstring const & name) const
1118 LASSERT(!name.empty(), /**/);
1121 find_if(begin(), end(), LayoutNamesEqual(name));
1124 lyxerr << "We failed to find the layout '" << to_utf8(name)
1125 << "' in the layout list. You MUST investigate!"
1127 for (const_iterator cit = begin(); cit != end(); ++cit)
1128 lyxerr << " " << to_utf8(cit->name()) << endl;
1130 // we require the name to exist
1131 LASSERT(false, /**/);
1138 Layout & TextClass::operator[](docstring const & name)
1140 LASSERT(!name.empty(), /**/);
1142 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1145 LYXERR0("We failed to find the layout '" << to_utf8(name)
1146 << "' in the layout list. You MUST investigate!");
1147 for (const_iterator cit = begin(); cit != end(); ++cit)
1148 LYXERR0(" " << to_utf8(cit->name()));
1150 // we require the name to exist
1151 LASSERT(false, /**/);
1158 bool TextClass::deleteLayout(docstring const & name)
1160 if (name == defaultLayoutName() || name == plainLayoutName())
1163 LayoutList::iterator it =
1164 remove_if(layoutlist_.begin(), layoutlist_.end(),
1165 LayoutNamesEqual(name));
1167 LayoutList::iterator end = layoutlist_.end();
1168 bool const ret = (it != end);
1169 layoutlist_.erase(it, end);
1174 // Load textclass info if not loaded yet
1175 bool TextClass::load(string const & path) const
1180 // Read style-file, provided path is searched before the system ones
1181 // If path is a file, it is loaded directly.
1182 FileName layout_file(path);
1183 if (!path.empty() && !layout_file.isReadableFile())
1184 layout_file = FileName(addName(path, name_ + ".layout"));
1185 if (layout_file.empty() || !layout_file.exists())
1186 layout_file = libFileSearch("layouts", name_, "layout");
1187 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1190 lyxerr << "Error reading `"
1191 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1192 << "'\n(Check `" << name_
1193 << "')\nCheck your installation and "
1194 "try Options/Reconfigure..."
1202 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1205 layoutlist_.push_back(createBasicLayout(n, true));
1209 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1211 // FIXME The fix for the InsetLayout part of 4812 would be here:
1212 // Add the InsetLayout to the document class if it is not found.
1214 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1215 while (!n.empty()) {
1216 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1217 if (cit != cen && cit->first == n)
1219 size_t i = n.find(':');
1220 if (i == string::npos)
1224 return plain_insetlayout_;
1228 docstring const & TextClass::defaultLayoutName() const
1230 return defaultlayout_;
1234 Layout const & TextClass::defaultLayout() const
1236 return operator[](defaultLayoutName());
1240 bool TextClass::isDefaultLayout(Layout const & layout) const
1242 return layout.name() == defaultLayoutName();
1246 bool TextClass::isPlainLayout(Layout const & layout) const
1248 return layout.name() == plainLayoutName();
1252 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1254 static Layout * defaultLayout = NULL;
1256 if (defaultLayout) {
1257 defaultLayout->setUnknown(unknown);
1258 defaultLayout->setName(name);
1259 return *defaultLayout;
1262 static char const * s = "Margin Static\n"
1263 "LatexType Paragraph\n"
1266 "AlignPossible Left, Right, Center\n"
1267 "LabelType No_Label\n"
1269 istringstream ss(s);
1270 Lexer lex(textClassTags);
1272 defaultLayout = new Layout;
1273 defaultLayout->setUnknown(unknown);
1274 defaultLayout->setName(name);
1275 if (!readStyle(lex, *defaultLayout)) {
1276 // The only way this happens is because the hardcoded layout above
1278 LASSERT(false, /**/);
1280 return *defaultLayout;
1284 /////////////////////////////////////////////////////////////////////////
1286 // DocumentClassBundle
1288 /////////////////////////////////////////////////////////////////////////
1290 DocumentClassBundle::~DocumentClassBundle()
1292 for (size_t i = 0; i != documentClasses_.size(); ++i)
1293 delete documentClasses_[i];
1294 documentClasses_.clear();
1297 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1299 DocumentClass * dc = new DocumentClass(baseClass);
1300 documentClasses_.push_back(dc);
1301 return *documentClasses_.back();
1305 DocumentClassBundle & DocumentClassBundle::get()
1307 static DocumentClassBundle singleton;
1312 DocumentClass & DocumentClassBundle::makeDocumentClass(
1313 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1315 DocumentClass & doc_class = newClass(baseClass);
1316 LayoutModuleList::const_iterator it = modlist.begin();
1317 LayoutModuleList::const_iterator en = modlist.end();
1318 for (; it != en; it++) {
1319 string const modName = *it;
1320 LyXModule * lm = theModuleList[modName];
1322 docstring const msg =
1323 bformat(_("The module %1$s has been requested by\n"
1324 "this document but has not been found in the list of\n"
1325 "available modules. If you recently installed it, you\n"
1326 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1327 frontend::Alert::warning(_("Module not available"), msg);
1330 if (!lm->isAvailable()) {
1331 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1332 docstring const msg =
1333 bformat(_("The module %1$s requires a package that is not\n"
1334 "available in your LaTeX installation, or a converter that\n"
1335 "you have not installed. LaTeX output may not be possible.\n"
1336 "Missing prerequisites:\n"
1338 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1339 from_utf8(modName), prereqs);
1340 frontend::Alert::warning(_("Package not available"), msg, true);
1342 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1343 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1344 docstring const msg =
1345 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1346 frontend::Alert::warning(_("Read Error"), msg);
1353 /////////////////////////////////////////////////////////////////////////
1357 /////////////////////////////////////////////////////////////////////////
1359 DocumentClass::DocumentClass(LayoutFile const & tc)
1364 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1366 LayoutList::const_iterator it = layoutlist_.begin();
1367 LayoutList::const_iterator end = layoutlist_.end();
1368 for (; it != end; ++it)
1369 if (it->latexname() == lay)
1375 bool DocumentClass::provides(string const & p) const
1377 return provides_.find(p) != provides_.end();
1381 bool DocumentClass::hasTocLevels() const
1383 return min_toclevel_ != Layout::NOT_IN_TOC;
1387 Layout const & DocumentClass::htmlTOCLayout() const
1389 if (html_toc_section_.empty()) {
1390 // we're going to look for the layout with the minimum toclevel
1391 TextClass::LayoutList::const_iterator lit = begin();
1392 TextClass::LayoutList::const_iterator const len = end();
1393 int minlevel = 1000;
1394 Layout const * lay = NULL;
1395 for (; lit != len; ++lit) {
1396 int const level = lit->toclevel;
1397 // we don't want Part
1398 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1404 html_toc_section_ = lay->name();
1406 // hmm. that is very odd, so we'll do our best
1407 html_toc_section_ = defaultLayoutName();
1409 return operator[](html_toc_section_);
1413 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1415 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1417 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1418 if (it != cite_formats_.end())
1420 return default_format;
1424 string const & DocumentClass::getCiteMacro(string const & macro) const
1426 static string empty;
1427 map<string, string>::const_iterator it = cite_macros_.find(macro);
1428 if (it != cite_macros_.end())
1434 /////////////////////////////////////////////////////////////////////////
1438 /////////////////////////////////////////////////////////////////////////
1440 ostream & operator<<(ostream & os, PageSides p)