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 = 35;
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 std::string TextClass::convert(std::string const & str)
273 FileName const fn = FileName::tempName("locallayout");
274 ofstream os(fn.toFilesystemEncoding().c_str());
277 FileName const tempfile = FileName::tempName("convert_locallayout");
278 bool success = layout2layout(fn, tempfile);
281 ifstream is(tempfile.toFilesystemEncoding().c_str());
289 tempfile.removeFile();
294 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
296 if (!filename.isReadableFile()) {
297 lyxerr << "Cannot read layout file `" << filename << "'."
302 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
303 to_utf8(makeDisplayPath(filename.absFileName())));
305 // Define the plain layout used in table cells, ert, etc. Note that
306 // we do this before loading any layout file, so that classes can
307 // override features of this layout if they should choose to do so.
308 if (rt == BASECLASS && !hasLayout(plain_layout_))
309 layoutlist_.push_back(createBasicLayout(plain_layout_));
311 Lexer lexrc(textClassTags);
312 lexrc.setFile(filename);
313 ReturnValues retval = read(lexrc, rt);
315 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
316 to_utf8(makeDisplayPath(filename.absFileName())));
322 bool TextClass::read(FileName const & filename, ReadType rt)
324 ReturnValues const retval = readWithoutConv(filename, rt);
325 if (retval != FORMAT_MISMATCH)
328 bool const worx = convertLayoutFormat(filename, rt);
330 LYXERR0 ("Unable to convert " << filename <<
331 " to format " << LAYOUT_FORMAT);
336 TextClass::ReturnValues TextClass::validate(std::string const & str)
339 return tc.read(str, VALIDATION);
343 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
345 Lexer lexrc(textClassTags);
346 istringstream is(str);
348 ReturnValues retval = read(lexrc, rt);
350 if (retval != FORMAT_MISMATCH)
353 // write the layout string to a temporary file
354 FileName const tempfile = FileName::tempName("TextClass_read");
355 ofstream os(tempfile.toFilesystemEncoding().c_str());
357 LYXERR0("Unable to create temporary file");
363 // now try to convert it
364 bool const worx = convertLayoutFormat(tempfile, rt);
366 LYXERR0("Unable to convert internal layout information to format "
370 tempfile.removeFile();
375 // Reads a textclass structure from file.
376 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
381 // Format of files before the 'Format' tag was introduced
386 while (lexrc.isOK() && !error) {
387 int le = lexrc.lex();
390 case Lexer::LEX_FEOF:
393 case Lexer::LEX_UNDEF:
394 lexrc.printError("Unknown TextClass tag `$$Token'");
402 // used below to track whether we are in an IfStyle or IfCounter tag.
403 bool ifstyle = false;
404 bool ifcounter = false;
406 switch (static_cast<TextClassTags>(le)) {
410 format = lexrc.getInteger();
413 case TC_OUTPUTFORMAT:
415 outputFormat_ = lexrc.getString();
419 readOutputType(lexrc);
420 switch(outputType_) {
422 outputFormat_ = "latex";
425 outputFormat_ = "docbook";
428 outputFormat_ = "literate";
433 case TC_INPUT: // Include file
435 string const inc = lexrc.getString();
436 FileName tmp = libFileSearch("layouts", inc,
440 lexrc.printError("Could not find input file: " + inc);
442 } else if (!read(tmp, MERGE)) {
443 lexrc.printError("Error reading input file: " + tmp.absFileName());
449 case TC_DEFAULTSTYLE:
451 docstring const name = from_utf8(subst(lexrc.getString(),
453 defaultlayout_ = name;
462 lexrc.printError("No name given for style: `$$Token'.");
466 docstring const name = from_utf8(subst(lexrc.getString(),
469 string s = "Could not read name for style: `$$Token' "
470 + lexrc.getString() + " is probably not valid UTF-8!";
473 // Since we couldn't read the name, we just scan the rest
474 // of the style and discard it.
475 error = !readStyle(lexrc, lay);
476 } else if (hasLayout(name)) {
477 Layout & lay = operator[](name);
478 error = !readStyle(lexrc, lay);
479 } else if (!ifstyle) {
481 layout.setName(name);
482 error = !readStyle(lexrc, layout);
484 layoutlist_.push_back(layout);
486 if (defaultlayout_.empty()) {
487 // We do not have a default layout yet, so we choose
488 // the first layout we encounter.
489 defaultlayout_ = name;
493 // this was an ifstyle where we didn't have the style
494 // scan the rest and discard it
496 readStyle(lexrc, lay);
506 docstring const style = from_utf8(subst(lexrc.getString(),
508 if (!deleteLayout(style))
509 lyxerr << "Cannot delete style `"
510 << to_utf8(style) << '\'' << endl;
516 columns_ = lexrc.getInteger();
521 switch (lexrc.getInteger()) {
522 case 1: sides_ = OneSide; break;
523 case 2: sides_ = TwoSides; break;
525 lyxerr << "Impossible number of page"
526 " sides, setting to one."
536 pagestyle_ = rtrim(lexrc.getString());
540 defaultfont_ = lyxRead(lexrc);
541 if (!defaultfont_.resolved()) {
542 lexrc.printError("Warning: defaultfont should "
543 "be fully instantiated!");
544 defaultfont_.realize(sane_font);
550 secnumdepth_ = lexrc.getInteger();
555 tocdepth_ = lexrc.getInteger();
558 // First step to support options
559 case TC_CLASSOPTIONS:
560 readClassOptions(lexrc);
564 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
567 case TC_HTMLPREAMBLE:
568 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
571 case TC_HTMLTOCSECTION:
572 html_toc_section_ = from_utf8(trim(lexrc.getString()));
575 case TC_ADDTOPREAMBLE:
576 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
579 case TC_ADDTOHTMLPREAMBLE:
580 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
585 string const feature = lexrc.getString();
587 if (lexrc.getInteger())
588 provides_.insert(feature);
590 provides_.erase(feature);
596 vector<string> const req
597 = getVectorFromString(lexrc.getString());
598 requires_.insert(req.begin(), req.end());
602 case TC_DEFAULTMODULE: {
604 string const module = lexrc.getString();
605 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
606 default_modules_.push_back(module);
610 case TC_PROVIDESMODULE: {
612 string const module = lexrc.getString();
613 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
614 provided_modules_.push_back(module);
618 case TC_EXCLUDESMODULE: {
620 string const module = lexrc.getString();
621 // modules already have their own way to exclude other modules
623 LYXERR0("ExcludesModule tag cannot be used in a module!");
626 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
627 excluded_modules_.push_back(module);
631 case TC_LEFTMARGIN: // left margin type
633 leftmargin_ = lexrc.getDocString();
636 case TC_RIGHTMARGIN: // right margin type
638 rightmargin_ = lexrc.getDocString();
641 case TC_INSETLAYOUT: {
643 lexrc.printError("No name given for InsetLayout: `$$Token'.");
647 docstring const name = subst(lexrc.getDocString(), '_', ' ');
649 string s = "Could not read name for InsetLayout: `$$Token' "
650 + lexrc.getString() + " is probably not valid UTF-8!";
653 // Since we couldn't read the name, we just scan the rest
654 // of the style and discard it.
655 il.read(lexrc, *this);
656 // Let's try to continue rather than abort.
658 } else if (hasInsetLayout(name)) {
659 InsetLayout & il = insetlayoutlist_[name];
660 error = !il.read(lexrc, *this);
664 error = !il.read(lexrc, *this);
666 insetlayoutlist_[name] = il;
672 error = !readFloat(lexrc);
676 readCiteFormat(lexrc);
681 docstring const cnt = lexrc.getDocString();
682 if (!counters_.remove(cnt))
683 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
691 docstring const name = lexrc.getDocString();
693 string s = "Could not read name for counter: `$$Token' "
694 + lexrc.getString() + " is probably not valid UTF-8!";
695 lexrc.printError(s.c_str());
697 // Since we couldn't read the name, we just scan the rest
701 error = !counters_.read(lexrc, name, !ifcounter);
704 lexrc.printError("No name given for style: `$$Token'.");
711 case TC_TITLELATEXTYPE:
712 readTitleType(lexrc);
715 case TC_TITLELATEXNAME:
717 titlename_ = lexrc.getString();
722 string const nofloat = lexrc.getString();
723 floatlist_.erase(nofloat);
728 // Note that this is triggered the first time through the loop unless
729 // we hit a format tag.
730 if (format != LAYOUT_FORMAT)
731 return FORMAT_MISMATCH;
734 // at present, we abort if we encounter an error,
735 // so there is no point continuing.
740 return (error ? ERROR : OK);
742 if (defaultlayout_.empty()) {
743 LYXERR0("Error: Textclass '" << name_
744 << "' is missing a defaultstyle.");
748 // Try to erase "stdinsets" from the provides_ set.
750 // Provides stdinsets 1
751 // declaration simply tells us that the standard insets have been
752 // defined. (It's found in stdinsets.inc but could also be used in
753 // user-defined files.) There isn't really any such package. So we
754 // might as well go ahead and erase it.
755 // If we do not succeed, then it was not there, which means that
756 // the textclass did not provide the definitions of the standard
757 // insets. So we need to try to load them.
758 int erased = provides_.erase("stdinsets");
760 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
763 frontend::Alert::warning(_("Missing File"),
764 _("Could not find stdinsets.inc! This may lead to data loss!"));
766 } else if (!read(tmp, MERGE)) {
767 frontend::Alert::warning(_("Corrupt File"),
768 _("Could not read stdinsets.inc! This may lead to data loss!"));
773 min_toclevel_ = Layout::NOT_IN_TOC;
774 max_toclevel_ = Layout::NOT_IN_TOC;
775 const_iterator lit = begin();
776 const_iterator len = end();
777 for (; lit != len; ++lit) {
778 int const toclevel = lit->toclevel;
779 if (toclevel != Layout::NOT_IN_TOC) {
780 if (min_toclevel_ == Layout::NOT_IN_TOC)
781 min_toclevel_ = toclevel;
783 min_toclevel_ = min(min_toclevel_, toclevel);
784 max_toclevel_ = max(max_toclevel_, toclevel);
787 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
788 << ", maximum is " << max_toclevel_);
790 return (error ? ERROR : OK);
794 void TextClass::readTitleType(Lexer & lexrc)
796 LexerKeyword titleTypeTags[] = {
797 { "commandafter", TITLE_COMMAND_AFTER },
798 { "environment", TITLE_ENVIRONMENT }
801 PushPopHelper pph(lexrc, titleTypeTags);
803 int le = lexrc.lex();
805 case Lexer::LEX_UNDEF:
806 lexrc.printError("Unknown output type `$$Token'");
808 case TITLE_COMMAND_AFTER:
809 case TITLE_ENVIRONMENT:
810 titletype_ = static_cast<TitleLatexType>(le);
813 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
819 void TextClass::readOutputType(Lexer & lexrc)
821 LexerKeyword outputTypeTags[] = {
822 { "docbook", DOCBOOK },
824 { "literate", LITERATE }
827 PushPopHelper pph(lexrc, outputTypeTags);
829 int le = lexrc.lex();
831 case Lexer::LEX_UNDEF:
832 lexrc.printError("Unknown output type `$$Token'");
837 outputType_ = static_cast<OutputType>(le);
840 LYXERR0("Unhandled value " << le);
846 void TextClass::readClassOptions(Lexer & lexrc)
856 LexerKeyword classOptionsTags[] = {
858 {"fontsize", CO_FONTSIZE },
859 {"header", CO_HEADER },
860 {"other", CO_OTHER },
861 {"pagestyle", CO_PAGESTYLE }
864 lexrc.pushTable(classOptionsTags);
866 while (!getout && lexrc.isOK()) {
867 int le = lexrc.lex();
869 case Lexer::LEX_UNDEF:
870 lexrc.printError("Unknown ClassOption tag `$$Token'");
878 opt_fontsize_ = rtrim(lexrc.getString());
882 opt_pagestyle_ = rtrim(lexrc.getString());
886 if (options_.empty())
887 options_ = lexrc.getString();
889 options_ += ',' + lexrc.getString();
893 class_header_ = subst(lexrc.getString(), """, "\"");
904 void TextClass::readCiteFormat(Lexer & lexrc)
908 while (lexrc.isOK()) {
910 etype = lexrc.getString();
911 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
914 definition = lexrc.getString();
915 char initchar = etype[0];
918 if (initchar == '!' || initchar == '_')
919 cite_macros_[etype] = definition;
921 cite_formats_[etype] = definition;
926 bool TextClass::readFloat(Lexer & lexrc)
946 LexerKeyword floatTags[] = {
948 { "extension", FT_EXT },
949 { "guiname", FT_NAME },
950 { "htmlattr", FT_HTMLATTR },
951 { "htmlstyle", FT_HTMLSTYLE },
952 { "htmltag", FT_HTMLTAG },
953 { "ispredefined", FT_PREDEFINED },
954 { "listcommand", FT_LISTCOMMAND },
955 { "listname", FT_LISTNAME },
956 { "numberwithin", FT_WITHIN },
957 { "placement", FT_PLACEMENT },
958 { "refprefix", FT_REFPREFIX },
959 { "style", FT_STYLE },
961 { "usesfloatpkg", FT_USESFLOAT }
964 lexrc.pushTable(floatTags);
978 bool usesfloat = true;
979 bool ispredefined = false;
982 while (!getout && lexrc.isOK()) {
983 int le = lexrc.lex();
985 case Lexer::LEX_UNDEF:
986 lexrc.printError("Unknown float tag `$$Token'");
994 type = lexrc.getString();
995 if (floatlist_.typeExist(type)) {
996 Floating const & fl = floatlist_.getType(type);
997 placement = fl.placement();
999 within = fl.within();
1002 listname = fl.listName();
1003 usesfloat = fl.usesFloatPkg();
1004 ispredefined = fl.isPredefined();
1005 listcommand = fl.listCommand();
1006 refprefix = fl.refPrefix();
1011 name = lexrc.getString();
1015 placement = lexrc.getString();
1019 ext = lexrc.getString();
1023 within = lexrc.getString();
1024 if (within == "none")
1029 style = lexrc.getString();
1031 case FT_LISTCOMMAND:
1033 listcommand = lexrc.getString();
1037 refprefix = lexrc.getString();
1041 listname = lexrc.getString();
1045 usesfloat = lexrc.getBool();
1049 ispredefined = lexrc.getBool();
1053 htmlattr = lexrc.getString();
1057 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1061 htmltag = lexrc.getString();
1071 // Here we have a full float if getout == true
1073 if (!usesfloat && listcommand.empty()) {
1074 // if this float uses the same auxfile as an existing one,
1075 // there is no need for it to provide a list command.
1076 FloatList::const_iterator it = floatlist_.begin();
1077 FloatList::const_iterator en = floatlist_.end();
1078 bool found_ext = false;
1079 for (; it != en; ++it) {
1080 if (it->second.ext() == ext) {
1086 LYXERR0("The layout does not provide a list command " <<
1087 "for the float `" << type << "'. LyX will " <<
1088 "not be able to produce a float list.");
1090 Floating fl(type, placement, ext, within, style, name,
1091 listname, listcommand, refprefix,
1092 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1093 floatlist_.newFloat(fl);
1094 // each float has its own counter
1095 counters_.newCounter(from_ascii(type), from_ascii(within),
1096 docstring(), docstring());
1097 // also define sub-float counters
1098 docstring const subtype = "sub-" + from_ascii(type);
1099 counters_.newCounter(subtype, from_ascii(type),
1100 "\\alph{" + subtype + "}", docstring());
1106 string const & TextClass::prerequisites() const
1108 if (contains(prerequisites_, ',')) {
1109 vector<string> const pres = getVectorFromString(prerequisites_);
1110 prerequisites_ = getStringFromVector(pres, "\n\t");
1112 return prerequisites_;
1115 bool TextClass::hasLayout(docstring const & n) const
1117 docstring const name = n.empty() ? defaultLayoutName() : n;
1119 return find_if(layoutlist_.begin(), layoutlist_.end(),
1120 LayoutNamesEqual(name))
1121 != layoutlist_.end();
1125 bool TextClass::hasInsetLayout(docstring const & n) const
1129 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1130 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1131 for (; it != en; ++it)
1138 Layout const & TextClass::operator[](docstring const & name) const
1140 LASSERT(!name.empty(), /**/);
1143 find_if(begin(), end(), LayoutNamesEqual(name));
1146 lyxerr << "We failed to find the layout '" << to_utf8(name)
1147 << "' in the layout list. You MUST investigate!"
1149 for (const_iterator cit = begin(); cit != end(); ++cit)
1150 lyxerr << " " << to_utf8(cit->name()) << endl;
1152 // we require the name to exist
1153 LASSERT(false, /**/);
1160 Layout & TextClass::operator[](docstring const & name)
1162 LASSERT(!name.empty(), /**/);
1164 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1167 LYXERR0("We failed to find the layout '" << to_utf8(name)
1168 << "' in the layout list. You MUST investigate!");
1169 for (const_iterator cit = begin(); cit != end(); ++cit)
1170 LYXERR0(" " << to_utf8(cit->name()));
1172 // we require the name to exist
1173 LASSERT(false, /**/);
1180 bool TextClass::deleteLayout(docstring const & name)
1182 if (name == defaultLayoutName() || name == plainLayoutName())
1185 LayoutList::iterator it =
1186 remove_if(layoutlist_.begin(), layoutlist_.end(),
1187 LayoutNamesEqual(name));
1189 LayoutList::iterator end = layoutlist_.end();
1190 bool const ret = (it != end);
1191 layoutlist_.erase(it, end);
1196 // Load textclass info if not loaded yet
1197 bool TextClass::load(string const & path) const
1202 // Read style-file, provided path is searched before the system ones
1203 // If path is a file, it is loaded directly.
1204 FileName layout_file(path);
1205 if (!path.empty() && !layout_file.isReadableFile())
1206 layout_file = FileName(addName(path, name_ + ".layout"));
1207 if (layout_file.empty() || !layout_file.exists())
1208 layout_file = libFileSearch("layouts", name_, "layout");
1209 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1212 lyxerr << "Error reading `"
1213 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1214 << "'\n(Check `" << name_
1215 << "')\nCheck your installation and "
1216 "try Options/Reconfigure..."
1224 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1227 layoutlist_.push_back(createBasicLayout(n, true));
1231 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1233 // FIXME The fix for the InsetLayout part of 4812 would be here:
1234 // Add the InsetLayout to the document class if it is not found.
1236 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1237 while (!n.empty()) {
1238 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1239 if (cit != cen && cit->first == n)
1241 size_t i = n.find(':');
1242 if (i == string::npos)
1246 return plain_insetlayout_;
1250 docstring const & TextClass::defaultLayoutName() const
1252 return defaultlayout_;
1256 Layout const & TextClass::defaultLayout() const
1258 return operator[](defaultLayoutName());
1262 bool TextClass::isDefaultLayout(Layout const & layout) const
1264 return layout.name() == defaultLayoutName();
1268 bool TextClass::isPlainLayout(Layout const & layout) const
1270 return layout.name() == plainLayoutName();
1274 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1276 static Layout * defaultLayout = NULL;
1278 if (defaultLayout) {
1279 defaultLayout->setUnknown(unknown);
1280 defaultLayout->setName(name);
1281 return *defaultLayout;
1284 static char const * s = "Margin Static\n"
1285 "LatexType Paragraph\n"
1288 "AlignPossible Left, Right, Center\n"
1289 "LabelType No_Label\n"
1291 istringstream ss(s);
1292 Lexer lex(textClassTags);
1294 defaultLayout = new Layout;
1295 defaultLayout->setUnknown(unknown);
1296 defaultLayout->setName(name);
1297 if (!readStyle(lex, *defaultLayout)) {
1298 // The only way this happens is because the hardcoded layout above
1300 LASSERT(false, /**/);
1302 return *defaultLayout;
1306 /////////////////////////////////////////////////////////////////////////
1308 // DocumentClassBundle
1310 /////////////////////////////////////////////////////////////////////////
1312 DocumentClassBundle::~DocumentClassBundle()
1314 for (size_t i = 0; i != documentClasses_.size(); ++i)
1315 delete documentClasses_[i];
1316 documentClasses_.clear();
1319 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1321 DocumentClass * dc = new DocumentClass(baseClass);
1322 documentClasses_.push_back(dc);
1323 return *documentClasses_.back();
1327 DocumentClassBundle & DocumentClassBundle::get()
1329 static DocumentClassBundle singleton;
1334 DocumentClass & DocumentClassBundle::makeDocumentClass(
1335 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1337 DocumentClass & doc_class = newClass(baseClass);
1338 LayoutModuleList::const_iterator it = modlist.begin();
1339 LayoutModuleList::const_iterator en = modlist.end();
1340 for (; it != en; it++) {
1341 string const modName = *it;
1342 LyXModule * lm = theModuleList[modName];
1344 docstring const msg =
1345 bformat(_("The module %1$s has been requested by\n"
1346 "this document but has not been found in the list of\n"
1347 "available modules. If you recently installed it, you\n"
1348 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1349 frontend::Alert::warning(_("Module not available"), msg);
1352 if (!lm->isAvailable()) {
1353 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1354 docstring const msg =
1355 bformat(_("The module %1$s requires a package that is not\n"
1356 "available in your LaTeX installation, or a converter that\n"
1357 "you have not installed. LaTeX output may not be possible.\n"
1358 "Missing prerequisites:\n"
1360 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1361 from_utf8(modName), prereqs);
1362 frontend::Alert::warning(_("Package not available"), msg, true);
1364 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1365 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1366 docstring const msg =
1367 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1368 frontend::Alert::warning(_("Read Error"), msg);
1375 /////////////////////////////////////////////////////////////////////////
1379 /////////////////////////////////////////////////////////////////////////
1381 DocumentClass::DocumentClass(LayoutFile const & tc)
1386 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1388 LayoutList::const_iterator it = layoutlist_.begin();
1389 LayoutList::const_iterator end = layoutlist_.end();
1390 for (; it != end; ++it)
1391 if (it->latexname() == lay)
1397 bool DocumentClass::provides(string const & p) const
1399 return provides_.find(p) != provides_.end();
1403 bool DocumentClass::hasTocLevels() const
1405 return min_toclevel_ != Layout::NOT_IN_TOC;
1409 Layout const & DocumentClass::htmlTOCLayout() const
1411 if (html_toc_section_.empty()) {
1412 // we're going to look for the layout with the minimum toclevel
1413 TextClass::LayoutList::const_iterator lit = begin();
1414 TextClass::LayoutList::const_iterator const len = end();
1415 int minlevel = 1000;
1416 Layout const * lay = NULL;
1417 for (; lit != len; ++lit) {
1418 int const level = lit->toclevel;
1419 // we don't want Part
1420 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1426 html_toc_section_ = lay->name();
1428 // hmm. that is very odd, so we'll do our best
1429 html_toc_section_ = defaultLayoutName();
1431 return operator[](html_toc_section_);
1435 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1437 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1439 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1440 if (it != cite_formats_.end())
1442 return default_format;
1446 string const & DocumentClass::getCiteMacro(string const & macro) const
1448 static string empty;
1449 map<string, string>::const_iterator it = cite_macros_.find(macro);
1450 if (it != cite_macros_.end())
1456 /////////////////////////////////////////////////////////////////////////
1460 /////////////////////////////////////////////////////////////////////////
1462 ostream & operator<<(ostream & os, PageSides p)