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 = 36;
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
207 TC_ADDTOHTMLPREAMBLE,
219 LexerKeyword textClassTags[] = {
220 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
221 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
222 { "addtopreamble", TC_ADDTOPREAMBLE },
223 { "citeformat", TC_CITEFORMAT },
224 { "classoptions", TC_CLASSOPTIONS },
225 { "columns", TC_COLUMNS },
226 { "counter", TC_COUNTER },
227 { "defaultfont", TC_DEFAULTFONT },
228 { "defaultmodule", TC_DEFAULTMODULE },
229 { "defaultstyle", TC_DEFAULTSTYLE },
230 { "excludesmodule", TC_EXCLUDESMODULE },
231 { "float", TC_FLOAT },
232 { "format", TC_FORMAT },
233 { "htmlpreamble", TC_HTMLPREAMBLE },
234 { "htmlstyles", TC_HTMLSTYLES },
235 { "htmltocsection", TC_HTMLTOCSECTION },
236 { "ifcounter", TC_IFCOUNTER },
237 { "ifstyle", TC_IFSTYLE },
238 { "input", TC_INPUT },
239 { "insetlayout", TC_INSETLAYOUT },
240 { "leftmargin", TC_LEFTMARGIN },
241 { "nocounter", TC_NOCOUNTER },
242 { "nofloat", TC_NOFLOAT },
243 { "nostyle", TC_NOSTYLE },
244 { "outputformat", TC_OUTPUTFORMAT },
245 { "outputtype", TC_OUTPUTTYPE },
246 { "pagestyle", TC_PAGESTYLE },
247 { "preamble", TC_PREAMBLE },
248 { "provides", TC_PROVIDES },
249 { "providesmodule", TC_PROVIDESMODULE },
250 { "requires", TC_REQUIRES },
251 { "rightmargin", TC_RIGHTMARGIN },
252 { "secnumdepth", TC_SECNUMDEPTH },
253 { "sides", TC_SIDES },
254 { "style", TC_STYLE },
255 { "titlelatexname", TC_TITLELATEXNAME },
256 { "titlelatextype", TC_TITLELATEXTYPE },
257 { "tocdepth", TC_TOCDEPTH }
263 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
265 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
266 FileName const tempfile = FileName::tempName("convert_layout");
267 bool success = layout2layout(filename, tempfile);
269 success = readWithoutConv(tempfile, rt) == OK;
270 tempfile.removeFile();
275 std::string TextClass::convert(std::string const & str)
277 FileName const fn = FileName::tempName("locallayout");
278 ofstream os(fn.toFilesystemEncoding().c_str());
281 FileName const tempfile = FileName::tempName("convert_locallayout");
282 bool success = layout2layout(fn, tempfile);
285 ifstream is(tempfile.toFilesystemEncoding().c_str());
293 tempfile.removeFile();
298 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
300 if (!filename.isReadableFile()) {
301 lyxerr << "Cannot read layout file `" << filename << "'."
306 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
307 to_utf8(makeDisplayPath(filename.absFileName())));
309 // Define the plain layout used in table cells, ert, etc. Note that
310 // we do this before loading any layout file, so that classes can
311 // override features of this layout if they should choose to do so.
312 if (rt == BASECLASS && !hasLayout(plain_layout_))
313 layoutlist_.push_back(createBasicLayout(plain_layout_));
315 Lexer lexrc(textClassTags);
316 lexrc.setFile(filename);
317 ReturnValues retval = read(lexrc, rt);
319 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
320 to_utf8(makeDisplayPath(filename.absFileName())));
326 bool TextClass::read(FileName const & filename, ReadType rt)
328 ReturnValues const retval = readWithoutConv(filename, rt);
329 if (retval != FORMAT_MISMATCH)
332 bool const worx = convertLayoutFormat(filename, rt);
334 LYXERR0 ("Unable to convert " << filename <<
335 " to format " << LAYOUT_FORMAT);
340 TextClass::ReturnValues TextClass::validate(std::string const & str)
343 return tc.read(str, VALIDATION);
347 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
349 Lexer lexrc(textClassTags);
350 istringstream is(str);
352 ReturnValues retval = read(lexrc, rt);
354 if (retval != FORMAT_MISMATCH)
357 // write the layout string to a temporary file
358 FileName const tempfile = FileName::tempName("TextClass_read");
359 ofstream os(tempfile.toFilesystemEncoding().c_str());
361 LYXERR0("Unable to create temporary file");
367 // now try to convert it
368 bool const worx = convertLayoutFormat(tempfile, rt);
370 LYXERR0("Unable to convert internal layout information to format "
374 tempfile.removeFile();
379 // Reads a textclass structure from file.
380 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
385 // Format of files before the 'Format' tag was introduced
390 while (lexrc.isOK() && !error) {
391 int le = lexrc.lex();
394 case Lexer::LEX_FEOF:
397 case Lexer::LEX_UNDEF:
398 lexrc.printError("Unknown TextClass tag `$$Token'");
406 // used below to track whether we are in an IfStyle or IfCounter tag.
407 bool ifstyle = false;
408 bool ifcounter = false;
410 switch (static_cast<TextClassTags>(le)) {
414 format = lexrc.getInteger();
417 case TC_OUTPUTFORMAT:
419 outputFormat_ = lexrc.getString();
423 readOutputType(lexrc);
424 switch(outputType_) {
426 outputFormat_ = "latex";
429 outputFormat_ = "docbook";
432 outputFormat_ = "literate";
437 case TC_INPUT: // Include file
439 string const inc = lexrc.getString();
440 FileName tmp = libFileSearch("layouts", inc,
444 lexrc.printError("Could not find input file: " + inc);
446 } else if (!read(tmp, MERGE)) {
447 lexrc.printError("Error reading input file: " + tmp.absFileName());
453 case TC_DEFAULTSTYLE:
455 docstring const name = from_utf8(subst(lexrc.getString(),
457 defaultlayout_ = name;
466 lexrc.printError("No name given for style: `$$Token'.");
470 docstring const name = from_utf8(subst(lexrc.getString(),
473 string s = "Could not read name for style: `$$Token' "
474 + lexrc.getString() + " is probably not valid UTF-8!";
477 // Since we couldn't read the name, we just scan the rest
478 // of the style and discard it.
479 error = !readStyle(lexrc, lay);
480 } else if (hasLayout(name)) {
481 Layout & lay = operator[](name);
482 error = !readStyle(lexrc, lay);
483 } else if (!ifstyle) {
485 layout.setName(name);
486 error = !readStyle(lexrc, layout);
488 layoutlist_.push_back(layout);
490 if (defaultlayout_.empty()) {
491 // We do not have a default layout yet, so we choose
492 // the first layout we encounter.
493 defaultlayout_ = name;
497 // this was an ifstyle where we didn't have the style
498 // scan the rest and discard it
500 readStyle(lexrc, lay);
510 docstring const style = from_utf8(subst(lexrc.getString(),
512 if (!deleteLayout(style))
513 lyxerr << "Cannot delete style `"
514 << to_utf8(style) << '\'' << endl;
520 columns_ = lexrc.getInteger();
525 switch (lexrc.getInteger()) {
526 case 1: sides_ = OneSide; break;
527 case 2: sides_ = TwoSides; break;
529 lyxerr << "Impossible number of page"
530 " sides, setting to one."
540 pagestyle_ = rtrim(lexrc.getString());
544 defaultfont_ = lyxRead(lexrc);
545 if (!defaultfont_.resolved()) {
546 lexrc.printError("Warning: defaultfont should "
547 "be fully instantiated!");
548 defaultfont_.realize(sane_font);
554 secnumdepth_ = lexrc.getInteger();
559 tocdepth_ = lexrc.getInteger();
562 // First step to support options
563 case TC_CLASSOPTIONS:
564 readClassOptions(lexrc);
568 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
571 case TC_HTMLPREAMBLE:
572 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
576 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
579 case TC_HTMLTOCSECTION:
580 html_toc_section_ = from_utf8(trim(lexrc.getString()));
583 case TC_ADDTOPREAMBLE:
584 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
587 case TC_ADDTOHTMLPREAMBLE:
588 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
591 case TC_ADDTOHTMLSTYLES:
592 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
597 string const feature = lexrc.getString();
599 if (lexrc.getInteger())
600 provides_.insert(feature);
602 provides_.erase(feature);
608 vector<string> const req
609 = getVectorFromString(lexrc.getString());
610 requires_.insert(req.begin(), req.end());
614 case TC_DEFAULTMODULE: {
616 string const module = lexrc.getString();
617 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
618 default_modules_.push_back(module);
622 case TC_PROVIDESMODULE: {
624 string const module = lexrc.getString();
625 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
626 provided_modules_.push_back(module);
630 case TC_EXCLUDESMODULE: {
632 string const module = lexrc.getString();
633 // modules already have their own way to exclude other modules
635 LYXERR0("ExcludesModule tag cannot be used in a module!");
638 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
639 excluded_modules_.push_back(module);
643 case TC_LEFTMARGIN: // left margin type
645 leftmargin_ = lexrc.getDocString();
648 case TC_RIGHTMARGIN: // right margin type
650 rightmargin_ = lexrc.getDocString();
653 case TC_INSETLAYOUT: {
655 lexrc.printError("No name given for InsetLayout: `$$Token'.");
659 docstring const name = subst(lexrc.getDocString(), '_', ' ');
661 string s = "Could not read name for InsetLayout: `$$Token' "
662 + lexrc.getString() + " is probably not valid UTF-8!";
665 // Since we couldn't read the name, we just scan the rest
666 // of the style and discard it.
667 il.read(lexrc, *this);
668 // Let's try to continue rather than abort.
670 } else if (hasInsetLayout(name)) {
671 InsetLayout & il = insetlayoutlist_[name];
672 error = !il.read(lexrc, *this);
676 error = !il.read(lexrc, *this);
678 insetlayoutlist_[name] = il;
684 error = !readFloat(lexrc);
688 readCiteFormat(lexrc);
693 docstring const cnt = lexrc.getDocString();
694 if (!counters_.remove(cnt))
695 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
703 docstring const name = lexrc.getDocString();
705 string s = "Could not read name for counter: `$$Token' "
706 + lexrc.getString() + " is probably not valid UTF-8!";
707 lexrc.printError(s.c_str());
709 // Since we couldn't read the name, we just scan the rest
713 error = !counters_.read(lexrc, name, !ifcounter);
716 lexrc.printError("No name given for style: `$$Token'.");
723 case TC_TITLELATEXTYPE:
724 readTitleType(lexrc);
727 case TC_TITLELATEXNAME:
729 titlename_ = lexrc.getString();
734 string const nofloat = lexrc.getString();
735 floatlist_.erase(nofloat);
740 // Note that this is triggered the first time through the loop unless
741 // we hit a format tag.
742 if (format != LAYOUT_FORMAT)
743 return FORMAT_MISMATCH;
746 // at present, we abort if we encounter an error,
747 // so there is no point continuing.
752 return (error ? ERROR : OK);
754 if (defaultlayout_.empty()) {
755 LYXERR0("Error: Textclass '" << name_
756 << "' is missing a defaultstyle.");
760 // Try to erase "stdinsets" from the provides_ set.
762 // Provides stdinsets 1
763 // declaration simply tells us that the standard insets have been
764 // defined. (It's found in stdinsets.inc but could also be used in
765 // user-defined files.) There isn't really any such package. So we
766 // might as well go ahead and erase it.
767 // If we do not succeed, then it was not there, which means that
768 // the textclass did not provide the definitions of the standard
769 // insets. So we need to try to load them.
770 int erased = provides_.erase("stdinsets");
772 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
775 frontend::Alert::warning(_("Missing File"),
776 _("Could not find stdinsets.inc! This may lead to data loss!"));
778 } else if (!read(tmp, MERGE)) {
779 frontend::Alert::warning(_("Corrupt File"),
780 _("Could not read stdinsets.inc! This may lead to data loss!"));
785 min_toclevel_ = Layout::NOT_IN_TOC;
786 max_toclevel_ = Layout::NOT_IN_TOC;
787 const_iterator lit = begin();
788 const_iterator len = end();
789 for (; lit != len; ++lit) {
790 int const toclevel = lit->toclevel;
791 if (toclevel != Layout::NOT_IN_TOC) {
792 if (min_toclevel_ == Layout::NOT_IN_TOC)
793 min_toclevel_ = toclevel;
795 min_toclevel_ = min(min_toclevel_, toclevel);
796 max_toclevel_ = max(max_toclevel_, toclevel);
799 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
800 << ", maximum is " << max_toclevel_);
802 return (error ? ERROR : OK);
806 void TextClass::readTitleType(Lexer & lexrc)
808 LexerKeyword titleTypeTags[] = {
809 { "commandafter", TITLE_COMMAND_AFTER },
810 { "environment", TITLE_ENVIRONMENT }
813 PushPopHelper pph(lexrc, titleTypeTags);
815 int le = lexrc.lex();
817 case Lexer::LEX_UNDEF:
818 lexrc.printError("Unknown output type `$$Token'");
820 case TITLE_COMMAND_AFTER:
821 case TITLE_ENVIRONMENT:
822 titletype_ = static_cast<TitleLatexType>(le);
825 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
831 void TextClass::readOutputType(Lexer & lexrc)
833 LexerKeyword outputTypeTags[] = {
834 { "docbook", DOCBOOK },
836 { "literate", LITERATE }
839 PushPopHelper pph(lexrc, outputTypeTags);
841 int le = lexrc.lex();
843 case Lexer::LEX_UNDEF:
844 lexrc.printError("Unknown output type `$$Token'");
849 outputType_ = static_cast<OutputType>(le);
852 LYXERR0("Unhandled value " << le);
858 void TextClass::readClassOptions(Lexer & lexrc)
868 LexerKeyword classOptionsTags[] = {
870 {"fontsize", CO_FONTSIZE },
871 {"header", CO_HEADER },
872 {"other", CO_OTHER },
873 {"pagestyle", CO_PAGESTYLE }
876 lexrc.pushTable(classOptionsTags);
878 while (!getout && lexrc.isOK()) {
879 int le = lexrc.lex();
881 case Lexer::LEX_UNDEF:
882 lexrc.printError("Unknown ClassOption tag `$$Token'");
890 opt_fontsize_ = rtrim(lexrc.getString());
894 opt_pagestyle_ = rtrim(lexrc.getString());
898 if (options_.empty())
899 options_ = lexrc.getString();
901 options_ += ',' + lexrc.getString();
905 class_header_ = subst(lexrc.getString(), """, "\"");
916 void TextClass::readCiteFormat(Lexer & lexrc)
920 while (lexrc.isOK()) {
922 etype = lexrc.getString();
923 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
926 definition = lexrc.getString();
927 char initchar = etype[0];
930 if (initchar == '!' || initchar == '_')
931 cite_macros_[etype] = definition;
933 cite_formats_[etype] = definition;
938 bool TextClass::readFloat(Lexer & lexrc)
958 LexerKeyword floatTags[] = {
960 { "extension", FT_EXT },
961 { "guiname", FT_NAME },
962 { "htmlattr", FT_HTMLATTR },
963 { "htmlstyle", FT_HTMLSTYLE },
964 { "htmltag", FT_HTMLTAG },
965 { "ispredefined", FT_PREDEFINED },
966 { "listcommand", FT_LISTCOMMAND },
967 { "listname", FT_LISTNAME },
968 { "numberwithin", FT_WITHIN },
969 { "placement", FT_PLACEMENT },
970 { "refprefix", FT_REFPREFIX },
971 { "style", FT_STYLE },
973 { "usesfloatpkg", FT_USESFLOAT }
976 lexrc.pushTable(floatTags);
990 bool usesfloat = true;
991 bool ispredefined = false;
994 while (!getout && lexrc.isOK()) {
995 int le = lexrc.lex();
997 case Lexer::LEX_UNDEF:
998 lexrc.printError("Unknown float tag `$$Token'");
1006 type = lexrc.getString();
1007 if (floatlist_.typeExist(type)) {
1008 Floating const & fl = floatlist_.getType(type);
1009 placement = fl.placement();
1011 within = fl.within();
1014 listname = fl.listName();
1015 usesfloat = fl.usesFloatPkg();
1016 ispredefined = fl.isPredefined();
1017 listcommand = fl.listCommand();
1018 refprefix = fl.refPrefix();
1023 name = lexrc.getString();
1027 placement = lexrc.getString();
1031 ext = lexrc.getString();
1035 within = lexrc.getString();
1036 if (within == "none")
1041 style = lexrc.getString();
1043 case FT_LISTCOMMAND:
1045 listcommand = lexrc.getString();
1049 refprefix = lexrc.getString();
1053 listname = lexrc.getString();
1057 usesfloat = lexrc.getBool();
1061 ispredefined = lexrc.getBool();
1065 htmlattr = lexrc.getString();
1069 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1073 htmltag = lexrc.getString();
1083 // Here we have a full float if getout == true
1085 if (!usesfloat && listcommand.empty()) {
1086 // if this float uses the same auxfile as an existing one,
1087 // there is no need for it to provide a list command.
1088 FloatList::const_iterator it = floatlist_.begin();
1089 FloatList::const_iterator en = floatlist_.end();
1090 bool found_ext = false;
1091 for (; it != en; ++it) {
1092 if (it->second.ext() == ext) {
1098 LYXERR0("The layout does not provide a list command " <<
1099 "for the float `" << type << "'. LyX will " <<
1100 "not be able to produce a float list.");
1102 Floating fl(type, placement, ext, within, style, name,
1103 listname, listcommand, refprefix,
1104 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1105 floatlist_.newFloat(fl);
1106 // each float has its own counter
1107 counters_.newCounter(from_ascii(type), from_ascii(within),
1108 docstring(), docstring());
1109 // also define sub-float counters
1110 docstring const subtype = "sub-" + from_ascii(type);
1111 counters_.newCounter(subtype, from_ascii(type),
1112 "\\alph{" + subtype + "}", docstring());
1118 string const & TextClass::prerequisites() const
1120 if (contains(prerequisites_, ',')) {
1121 vector<string> const pres = getVectorFromString(prerequisites_);
1122 prerequisites_ = getStringFromVector(pres, "\n\t");
1124 return prerequisites_;
1127 bool TextClass::hasLayout(docstring const & n) const
1129 docstring const name = n.empty() ? defaultLayoutName() : n;
1131 return find_if(layoutlist_.begin(), layoutlist_.end(),
1132 LayoutNamesEqual(name))
1133 != layoutlist_.end();
1137 bool TextClass::hasInsetLayout(docstring const & n) const
1141 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1142 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1143 for (; it != en; ++it)
1150 Layout const & TextClass::operator[](docstring const & name) const
1152 LASSERT(!name.empty(), /**/);
1155 find_if(begin(), end(), LayoutNamesEqual(name));
1158 lyxerr << "We failed to find the layout '" << to_utf8(name)
1159 << "' in the layout list. You MUST investigate!"
1161 for (const_iterator cit = begin(); cit != end(); ++cit)
1162 lyxerr << " " << to_utf8(cit->name()) << endl;
1164 // we require the name to exist
1165 LASSERT(false, /**/);
1172 Layout & TextClass::operator[](docstring const & name)
1174 LASSERT(!name.empty(), /**/);
1176 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1179 LYXERR0("We failed to find the layout '" << to_utf8(name)
1180 << "' in the layout list. You MUST investigate!");
1181 for (const_iterator cit = begin(); cit != end(); ++cit)
1182 LYXERR0(" " << to_utf8(cit->name()));
1184 // we require the name to exist
1185 LASSERT(false, /**/);
1192 bool TextClass::deleteLayout(docstring const & name)
1194 if (name == defaultLayoutName() || name == plainLayoutName())
1197 LayoutList::iterator it =
1198 remove_if(layoutlist_.begin(), layoutlist_.end(),
1199 LayoutNamesEqual(name));
1201 LayoutList::iterator end = layoutlist_.end();
1202 bool const ret = (it != end);
1203 layoutlist_.erase(it, end);
1208 // Load textclass info if not loaded yet
1209 bool TextClass::load(string const & path) const
1214 // Read style-file, provided path is searched before the system ones
1215 // If path is a file, it is loaded directly.
1216 FileName layout_file(path);
1217 if (!path.empty() && !layout_file.isReadableFile())
1218 layout_file = FileName(addName(path, name_ + ".layout"));
1219 if (layout_file.empty() || !layout_file.exists())
1220 layout_file = libFileSearch("layouts", name_, "layout");
1221 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1224 lyxerr << "Error reading `"
1225 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1226 << "'\n(Check `" << name_
1227 << "')\nCheck your installation and "
1228 "try Options/Reconfigure..."
1236 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1241 layoutlist_.push_back(createBasicLayout(n, true));
1246 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1248 // FIXME The fix for the InsetLayout part of 4812 would be here:
1249 // Add the InsetLayout to the document class if it is not found.
1251 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1252 while (!n.empty()) {
1253 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1254 if (cit != cen && cit->first == n)
1256 size_t i = n.find(':');
1257 if (i == string::npos)
1261 return plain_insetlayout_;
1265 docstring const & TextClass::defaultLayoutName() const
1267 return defaultlayout_;
1271 Layout const & TextClass::defaultLayout() const
1273 return operator[](defaultLayoutName());
1277 bool TextClass::isDefaultLayout(Layout const & layout) const
1279 return layout.name() == defaultLayoutName();
1283 bool TextClass::isPlainLayout(Layout const & layout) const
1285 return layout.name() == plainLayoutName();
1289 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1291 static Layout * defaultLayout = NULL;
1293 if (defaultLayout) {
1294 defaultLayout->setUnknown(unknown);
1295 defaultLayout->setName(name);
1296 return *defaultLayout;
1299 static char const * s = "Margin Static\n"
1300 "LatexType Paragraph\n"
1303 "AlignPossible Left, Right, Center\n"
1304 "LabelType No_Label\n"
1306 istringstream ss(s);
1307 Lexer lex(textClassTags);
1309 defaultLayout = new Layout;
1310 defaultLayout->setUnknown(unknown);
1311 defaultLayout->setName(name);
1312 if (!readStyle(lex, *defaultLayout)) {
1313 // The only way this happens is because the hardcoded layout above
1315 LASSERT(false, /**/);
1317 return *defaultLayout;
1321 /////////////////////////////////////////////////////////////////////////
1323 // DocumentClassBundle
1325 /////////////////////////////////////////////////////////////////////////
1327 DocumentClassBundle::~DocumentClassBundle()
1329 for (size_t i = 0; i != documentClasses_.size(); ++i)
1330 delete documentClasses_[i];
1331 documentClasses_.clear();
1334 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1336 DocumentClass * dc = new DocumentClass(baseClass);
1337 documentClasses_.push_back(dc);
1338 return *documentClasses_.back();
1342 DocumentClassBundle & DocumentClassBundle::get()
1344 static DocumentClassBundle singleton;
1349 DocumentClass & DocumentClassBundle::makeDocumentClass(
1350 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1352 DocumentClass & doc_class = newClass(baseClass);
1353 LayoutModuleList::const_iterator it = modlist.begin();
1354 LayoutModuleList::const_iterator en = modlist.end();
1355 for (; it != en; it++) {
1356 string const modName = *it;
1357 LyXModule * lm = theModuleList[modName];
1359 docstring const msg =
1360 bformat(_("The module %1$s has been requested by\n"
1361 "this document but has not been found in the list of\n"
1362 "available modules. If you recently installed it, you\n"
1363 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1364 frontend::Alert::warning(_("Module not available"), msg);
1367 if (!lm->isAvailable()) {
1368 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1369 docstring const msg =
1370 bformat(_("The module %1$s requires a package that is not\n"
1371 "available in your LaTeX installation, or a converter that\n"
1372 "you have not installed. LaTeX output may not be possible.\n"
1373 "Missing prerequisites:\n"
1375 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1376 from_utf8(modName), prereqs);
1377 frontend::Alert::warning(_("Package not available"), msg, true);
1379 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1380 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1381 docstring const msg =
1382 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1383 frontend::Alert::warning(_("Read Error"), msg);
1390 /////////////////////////////////////////////////////////////////////////
1394 /////////////////////////////////////////////////////////////////////////
1396 DocumentClass::DocumentClass(LayoutFile const & tc)
1401 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1403 LayoutList::const_iterator it = layoutlist_.begin();
1404 LayoutList::const_iterator end = layoutlist_.end();
1405 for (; it != end; ++it)
1406 if (it->latexname() == lay)
1412 bool DocumentClass::provides(string const & p) const
1414 return provides_.find(p) != provides_.end();
1418 bool DocumentClass::hasTocLevels() const
1420 return min_toclevel_ != Layout::NOT_IN_TOC;
1424 Layout const & DocumentClass::htmlTOCLayout() const
1426 if (html_toc_section_.empty()) {
1427 // we're going to look for the layout with the minimum toclevel
1428 TextClass::LayoutList::const_iterator lit = begin();
1429 TextClass::LayoutList::const_iterator const len = end();
1430 int minlevel = 1000;
1431 Layout const * lay = NULL;
1432 for (; lit != len; ++lit) {
1433 int const level = lit->toclevel;
1434 // we don't want Part
1435 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1441 html_toc_section_ = lay->name();
1443 // hmm. that is very odd, so we'll do our best
1444 html_toc_section_ = defaultLayoutName();
1446 return operator[](html_toc_section_);
1450 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1452 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1454 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1455 if (it != cite_formats_.end())
1457 return default_format;
1461 string const & DocumentClass::getCiteMacro(string const & macro) const
1463 static string empty;
1464 map<string, string>::const_iterator it = cite_macros_.find(macro);
1465 if (it != cite_macros_.end())
1471 /////////////////////////////////////////////////////////////////////////
1475 /////////////////////////////////////////////////////////////////////////
1477 ostream & operator<<(ostream & os, PageSides p)