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.
60 int const LAYOUT_FORMAT = 27;
64 class LayoutNamesEqual : public unary_function<Layout, bool> {
66 LayoutNamesEqual(docstring const & name)
69 bool operator()(Layout const & c) const
71 return c.name() == name_;
78 bool layout2layout(FileName const & filename, FileName const & tempfile)
80 FileName const script = libFileSearch("scripts", "layout2layout.py");
82 LYXERR0("Could not find layout conversion "
83 "script layout2layout.py.");
87 ostringstream command;
88 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
89 << ' ' << quoteName(filename.toFilesystemEncoding())
90 << ' ' << quoteName(tempfile.toFilesystemEncoding());
91 string const command_str = command.str();
93 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
95 cmd_ret const ret = runCommand(command_str);
97 LYXERR0("Could not run layout conversion script layout2layout.py.");
104 string translateRT(TextClass::ReadType rt)
107 case TextClass::BASECLASS:
109 case TextClass::MERGE:
111 case TextClass::MODULE:
112 return "module file";
113 case TextClass::VALIDATION:
123 // This string should not be translated here,
124 // because it is a layout identifier.
125 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
128 InsetLayout DocumentClass::plain_insetlayout_;
131 /////////////////////////////////////////////////////////////////////////
135 /////////////////////////////////////////////////////////////////////////
137 TextClass::TextClass()
140 outputFormat_ = "latex";
145 pagestyle_ = "default";
146 defaultfont_ = sane_font;
147 opt_fontsize_ = "10|11|12";
148 opt_pagestyle_ = "empty|plain|headings|fancy";
149 titletype_ = TITLE_COMMAND_AFTER;
150 titlename_ = "maketitle";
152 _("Plain Layout"); // a hack to make this translatable
156 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
158 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
159 if (!lay.read(lexrc, *this)) {
160 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
164 lay.resfont = lay.font;
165 lay.resfont.realize(defaultfont_);
166 lay.reslabelfont = lay.labelfont;
167 lay.reslabelfont.realize(defaultfont_);
168 return true; // no errors
202 TC_ADDTOHTMLPREAMBLE,
213 LexerKeyword textClassTags[] = {
214 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
215 { "addtopreamble", TC_ADDTOPREAMBLE },
216 { "citeformat", TC_CITEFORMAT },
217 { "classoptions", TC_CLASSOPTIONS },
218 { "columns", TC_COLUMNS },
219 { "counter", TC_COUNTER },
220 { "defaultfont", TC_DEFAULTFONT },
221 { "defaultmodule", TC_DEFAULTMODULE },
222 { "defaultstyle", TC_DEFAULTSTYLE },
223 { "excludesmodule", TC_EXCLUDESMODULE },
224 { "float", TC_FLOAT },
225 { "format", TC_FORMAT },
226 { "htmlpreamble", TC_HTMLPREAMBLE },
227 { "htmltocsection", TC_HTMLTOCSECTION },
228 { "ifcounter", TC_IFCOUNTER },
229 { "ifstyle", TC_IFSTYLE },
230 { "input", TC_INPUT },
231 { "insetlayout", TC_INSETLAYOUT },
232 { "leftmargin", TC_LEFTMARGIN },
233 { "nofloat", TC_NOFLOAT },
234 { "nostyle", TC_NOSTYLE },
235 { "outputformat", TC_OUTPUTFORMAT },
236 { "outputtype", TC_OUTPUTTYPE },
237 { "pagestyle", TC_PAGESTYLE },
238 { "preamble", TC_PREAMBLE },
239 { "provides", TC_PROVIDES },
240 { "providesmodule", TC_PROVIDESMODULE },
241 { "requires", TC_REQUIRES },
242 { "rightmargin", TC_RIGHTMARGIN },
243 { "secnumdepth", TC_SECNUMDEPTH },
244 { "sides", TC_SIDES },
245 { "style", TC_STYLE },
246 { "titlelatexname", TC_TITLELATEXNAME },
247 { "titlelatextype", TC_TITLELATEXTYPE },
248 { "tocdepth", TC_TOCDEPTH }
254 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
256 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
257 FileName const tempfile = FileName::tempName("convert_layout");
258 bool success = layout2layout(filename, tempfile);
260 success = readWithoutConv(tempfile, rt) == OK;
261 tempfile.removeFile();
266 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
268 if (!filename.isReadableFile()) {
269 lyxerr << "Cannot read layout file `" << filename << "'."
274 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
275 to_utf8(makeDisplayPath(filename.absFileName())));
277 // Define the plain layout used in table cells, ert, etc. Note that
278 // we do this before loading any layout file, so that classes can
279 // override features of this layout if they should choose to do so.
280 if (rt == BASECLASS && !hasLayout(plain_layout_))
281 layoutlist_.push_back(createBasicLayout(plain_layout_));
283 Lexer lexrc(textClassTags);
284 lexrc.setFile(filename);
285 ReturnValues retval = read(lexrc, rt);
287 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
288 to_utf8(makeDisplayPath(filename.absFileName())));
294 bool TextClass::read(FileName const & filename, ReadType rt)
296 ReturnValues const retval = readWithoutConv(filename, rt);
297 if (retval != FORMAT_MISMATCH)
300 bool const worx = convertLayoutFormat(filename, rt);
302 LYXERR0 ("Unable to convert " << filename <<
303 " to format " << LAYOUT_FORMAT);
310 bool TextClass::validate(std::string const & str)
313 return tc.read(str, VALIDATION);
317 bool TextClass::read(std::string const & str, ReadType rt)
319 Lexer lexrc(textClassTags);
320 istringstream is(str);
322 ReturnValues retval = read(lexrc, rt);
324 if (retval != FORMAT_MISMATCH)
327 // write the layout string to a temporary file
328 FileName const tempfile = FileName::tempName("TextClass_read");
329 ofstream os(tempfile.toFilesystemEncoding().c_str());
331 LYXERR0("Unable to create temporary file");
337 // now try to convert it
338 bool const worx = convertLayoutFormat(tempfile, rt);
340 LYXERR0("Unable to convert internal layout information to format "
343 tempfile.removeFile();
348 // Reads a textclass structure from file.
349 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
354 // Format of files before the 'Format' tag was introduced
359 while (lexrc.isOK() && !error) {
360 int le = lexrc.lex();
363 case Lexer::LEX_FEOF:
366 case Lexer::LEX_UNDEF:
367 lexrc.printError("Unknown TextClass tag `$$Token'");
375 // used below to track whether we are in an IfStyle or IfCounter tag.
376 bool ifstyle = false;
377 bool ifcounter = false;
379 switch (static_cast<TextClassTags>(le)) {
383 format = lexrc.getInteger();
386 case TC_OUTPUTFORMAT:
388 outputFormat_ = lexrc.getString();
392 readOutputType(lexrc);
393 switch(outputType_) {
395 outputFormat_ = "latex";
398 outputFormat_ = "docbook";
401 outputFormat_ = "literate";
406 case TC_INPUT: // Include file
408 string const inc = lexrc.getString();
409 FileName tmp = libFileSearch("layouts", inc,
413 lexrc.printError("Could not find input file: " + inc);
415 } else if (!read(tmp, MERGE)) {
416 lexrc.printError("Error reading input file: " + tmp.absFileName());
422 case TC_DEFAULTSTYLE:
424 docstring const name = from_utf8(subst(lexrc.getString(),
426 defaultlayout_ = name;
435 lexrc.printError("No name given for style: `$$Token'.");
439 docstring const name = from_utf8(subst(lexrc.getString(),
442 string s = "Could not read name for style: `$$Token' "
443 + lexrc.getString() + " is probably not valid UTF-8!";
446 // Since we couldn't read the name, we just scan the rest
447 // of the style and discard it.
448 error = !readStyle(lexrc, lay);
449 } else if (hasLayout(name)) {
450 Layout & lay = operator[](name);
451 error = !readStyle(lexrc, lay);
452 } else if (!ifstyle) {
454 layout.setName(name);
455 error = !readStyle(lexrc, layout);
457 layoutlist_.push_back(layout);
459 if (defaultlayout_.empty()) {
460 // We do not have a default layout yet, so we choose
461 // the first layout we encounter.
462 defaultlayout_ = name;
466 // this was an ifstyle where we didn't have the style
467 // scan the rest and discard it
469 readStyle(lexrc, lay);
479 docstring const style = from_utf8(subst(lexrc.getString(),
481 if (!deleteLayout(style))
482 lyxerr << "Cannot delete style `"
483 << to_utf8(style) << '\'' << endl;
489 columns_ = lexrc.getInteger();
494 switch (lexrc.getInteger()) {
495 case 1: sides_ = OneSide; break;
496 case 2: sides_ = TwoSides; break;
498 lyxerr << "Impossible number of page"
499 " sides, setting to one."
509 pagestyle_ = rtrim(lexrc.getString());
513 defaultfont_ = lyxRead(lexrc);
514 if (!defaultfont_.resolved()) {
515 lexrc.printError("Warning: defaultfont should "
516 "be fully instantiated!");
517 defaultfont_.realize(sane_font);
523 secnumdepth_ = lexrc.getInteger();
528 tocdepth_ = lexrc.getInteger();
531 // First step to support options
532 case TC_CLASSOPTIONS:
533 readClassOptions(lexrc);
537 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
540 case TC_HTMLPREAMBLE:
541 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
544 case TC_HTMLTOCSECTION:
545 html_toc_section_ = from_utf8(trim(lexrc.getString()));
548 case TC_ADDTOPREAMBLE:
549 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
552 case TC_ADDTOHTMLPREAMBLE:
553 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
558 string const feature = lexrc.getString();
560 if (lexrc.getInteger())
561 provides_.insert(feature);
563 provides_.erase(feature);
569 vector<string> const req
570 = getVectorFromString(lexrc.getString());
571 requires_.insert(req.begin(), req.end());
575 case TC_DEFAULTMODULE: {
577 string const module = lexrc.getString();
578 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
579 default_modules_.push_back(module);
583 case TC_PROVIDESMODULE: {
585 string const module = lexrc.getString();
586 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
587 provided_modules_.push_back(module);
591 case TC_EXCLUDESMODULE: {
593 string const module = lexrc.getString();
594 // modules already have their own way to exclude other modules
596 LYXERR0("ExcludesModule tag cannot be used in a module!");
599 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
600 excluded_modules_.push_back(module);
604 case TC_LEFTMARGIN: // left margin type
606 leftmargin_ = lexrc.getDocString();
609 case TC_RIGHTMARGIN: // right margin type
611 rightmargin_ = lexrc.getDocString();
614 case TC_INSETLAYOUT: {
616 lexrc.printError("No name given for InsetLayout: `$$Token'.");
620 docstring const name = subst(lexrc.getDocString(), '_', ' ');
622 string s = "Could not read name for InsetLayout: `$$Token' "
623 + lexrc.getString() + " is probably not valid UTF-8!";
626 // Since we couldn't read the name, we just scan the rest
627 // of the style and discard it.
628 il.read(lexrc, *this);
629 // Let's try to continue rather than abort.
631 } else if (hasInsetLayout(name)) {
632 InsetLayout & il = insetlayoutlist_[name];
633 error = !il.read(lexrc, *this);
637 error = !il.read(lexrc, *this);
639 insetlayoutlist_[name] = il;
645 error = !readFloat(lexrc);
649 readCiteFormat(lexrc);
656 docstring const name = lexrc.getDocString();
658 string s = "Could not read name for counter: `$$Token' "
659 + lexrc.getString() + " is probably not valid UTF-8!";
660 lexrc.printError(s.c_str());
662 // Since we couldn't read the name, we just scan the rest
666 error = !counters_.read(lexrc, name, !ifcounter);
669 lexrc.printError("No name given for style: `$$Token'.");
676 case TC_TITLELATEXTYPE:
677 readTitleType(lexrc);
680 case TC_TITLELATEXNAME:
682 titlename_ = lexrc.getString();
687 string const nofloat = lexrc.getString();
688 floatlist_.erase(nofloat);
693 // Note that this is triggered the first time through the loop unless
694 // we hit a format tag.
695 if (format != LAYOUT_FORMAT)
696 return FORMAT_MISMATCH;
699 // at present, we abort if we encounter an error,
700 // so there is no point continuing.
705 return (error ? ERROR : OK);
707 if (defaultlayout_.empty()) {
708 LYXERR0("Error: Textclass '" << name_
709 << "' is missing a defaultstyle.");
713 // Try to erase "stdinsets" from the provides_ set.
715 // Provides stdinsets 1
716 // declaration simply tells us that the standard insets have been
717 // defined. (It's found in stdinsets.inc but could also be used in
718 // user-defined files.) There isn't really any such package. So we
719 // might as well go ahead and erase it.
720 // If we do not succeed, then it was not there, which means that
721 // the textclass did not provide the definitions of the standard
722 // insets. So we need to try to load them.
723 int erased = provides_.erase("stdinsets");
725 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
728 frontend::Alert::warning(_("Missing File"),
729 _("Could not find stdinsets.inc! This may lead to data loss!"));
731 } else if (!read(tmp, MERGE)) {
732 frontend::Alert::warning(_("Corrupt File"),
733 _("Could not read stdinsets.inc! This may lead to data loss!"));
738 min_toclevel_ = Layout::NOT_IN_TOC;
739 max_toclevel_ = Layout::NOT_IN_TOC;
740 const_iterator lit = begin();
741 const_iterator len = end();
742 for (; lit != len; ++lit) {
743 int const toclevel = lit->toclevel;
744 if (toclevel != Layout::NOT_IN_TOC) {
745 if (min_toclevel_ == Layout::NOT_IN_TOC)
746 min_toclevel_ = toclevel;
748 min_toclevel_ = min(min_toclevel_, toclevel);
749 max_toclevel_ = max(max_toclevel_, toclevel);
752 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
753 << ", maximum is " << max_toclevel_);
755 return (error ? ERROR : OK);
759 void TextClass::readTitleType(Lexer & lexrc)
761 LexerKeyword titleTypeTags[] = {
762 { "commandafter", TITLE_COMMAND_AFTER },
763 { "environment", TITLE_ENVIRONMENT }
766 PushPopHelper pph(lexrc, titleTypeTags);
768 int le = lexrc.lex();
770 case Lexer::LEX_UNDEF:
771 lexrc.printError("Unknown output type `$$Token'");
773 case TITLE_COMMAND_AFTER:
774 case TITLE_ENVIRONMENT:
775 titletype_ = static_cast<TitleLatexType>(le);
778 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
784 void TextClass::readOutputType(Lexer & lexrc)
786 LexerKeyword outputTypeTags[] = {
787 { "docbook", DOCBOOK },
789 { "literate", LITERATE }
792 PushPopHelper pph(lexrc, outputTypeTags);
794 int le = lexrc.lex();
796 case Lexer::LEX_UNDEF:
797 lexrc.printError("Unknown output type `$$Token'");
802 outputType_ = static_cast<OutputType>(le);
805 LYXERR0("Unhandled value " << le);
811 void TextClass::readClassOptions(Lexer & lexrc)
821 LexerKeyword classOptionsTags[] = {
823 {"fontsize", CO_FONTSIZE },
824 {"header", CO_HEADER },
825 {"other", CO_OTHER },
826 {"pagestyle", CO_PAGESTYLE }
829 lexrc.pushTable(classOptionsTags);
831 while (!getout && lexrc.isOK()) {
832 int le = lexrc.lex();
834 case Lexer::LEX_UNDEF:
835 lexrc.printError("Unknown ClassOption tag `$$Token'");
843 opt_fontsize_ = rtrim(lexrc.getString());
847 opt_pagestyle_ = rtrim(lexrc.getString());
851 if (options_.empty())
852 options_ = lexrc.getString();
854 options_ += ',' + lexrc.getString();
858 class_header_ = subst(lexrc.getString(), """, "\"");
869 void TextClass::readCiteFormat(Lexer & lexrc)
873 while (lexrc.isOK()) {
875 etype = lexrc.getString();
876 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
879 definition = lexrc.getString();
880 char initchar = etype[0];
883 if (initchar == '!' || initchar == '_')
884 cite_macros_[etype] = definition;
886 cite_formats_[etype] = definition;
891 bool TextClass::readFloat(Lexer & lexrc)
910 LexerKeyword floatTags[] = {
912 { "extension", FT_EXT },
913 { "guiname", FT_NAME },
914 { "htmlattr", FT_HTMLATTR },
915 { "htmlstyle", FT_HTMLSTYLE },
916 { "htmltag", FT_HTMLTAG },
917 { "listcommand", FT_LISTCOMMAND },
918 { "listname", FT_LISTNAME },
919 { "needsfloatpkg", FT_NEEDSFLOAT },
920 { "numberwithin", FT_WITHIN },
921 { "placement", FT_PLACEMENT },
922 { "refprefix", FT_REFPREFIX },
923 { "style", FT_STYLE },
927 lexrc.pushTable(floatTags);
941 bool needsfloat = true;
944 while (!getout && lexrc.isOK()) {
945 int le = lexrc.lex();
947 case Lexer::LEX_UNDEF:
948 lexrc.printError("Unknown float tag `$$Token'");
956 type = lexrc.getString();
957 if (floatlist_.typeExist(type)) {
958 Floating const & fl = floatlist_.getType(type);
959 placement = fl.placement();
961 within = fl.within();
964 listname = fl.listName();
965 needsfloat = fl.needsFloatPkg();
966 listcommand = fl.listCommand();
967 refprefix = fl.refPrefix();
972 name = lexrc.getString();
976 placement = lexrc.getString();
980 ext = lexrc.getString();
984 within = lexrc.getString();
985 if (within == "none")
990 style = lexrc.getString();
994 listcommand = lexrc.getString();
998 refprefix = lexrc.getString();
1002 listname = lexrc.getString();
1006 needsfloat = lexrc.getBool();
1010 htmlattr = lexrc.getString();
1014 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1018 htmltag = lexrc.getString();
1028 // Here we have a full float if getout == true
1030 if (!needsfloat && listcommand.empty())
1031 LYXERR0("The layout does not provide a list command " <<
1032 "for the builtin float `" << type << "'. LyX will " <<
1033 "not be able to produce a float list.");
1034 Floating fl(type, placement, ext, within, style, name,
1035 listname, listcommand, refprefix,
1036 htmltag, htmlattr, htmlstyle, needsfloat);
1037 floatlist_.newFloat(fl);
1038 // each float has its own counter
1039 counters_.newCounter(from_ascii(type), from_ascii(within),
1040 docstring(), docstring());
1041 // also define sub-float counters
1042 docstring const subtype = "sub-" + from_ascii(type);
1043 counters_.newCounter(subtype, from_ascii(type),
1044 "\\alph{" + subtype + "}", docstring());
1050 string const & TextClass::prerequisites() const
1052 if (contains(prerequisites_, ',')) {
1053 vector<string> const pres = getVectorFromString(prerequisites_);
1054 prerequisites_ = getStringFromVector(pres, "\n\t");
1056 return prerequisites_;
1059 bool TextClass::hasLayout(docstring const & n) const
1061 docstring const name = n.empty() ? defaultLayoutName() : n;
1063 return find_if(layoutlist_.begin(), layoutlist_.end(),
1064 LayoutNamesEqual(name))
1065 != layoutlist_.end();
1069 bool TextClass::hasInsetLayout(docstring const & n) const
1073 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1074 InsetLayouts::const_iterator en = insetlayoutlist_.end();
1075 for (; it != en; ++it)
1082 Layout const & TextClass::operator[](docstring const & name) const
1084 LASSERT(!name.empty(), /**/);
1087 find_if(begin(), end(), LayoutNamesEqual(name));
1090 lyxerr << "We failed to find the layout '" << to_utf8(name)
1091 << "' in the layout list. You MUST investigate!"
1093 for (const_iterator cit = begin(); cit != end(); ++cit)
1094 lyxerr << " " << to_utf8(cit->name()) << endl;
1096 // we require the name to exist
1097 LASSERT(false, /**/);
1104 Layout & TextClass::operator[](docstring const & name)
1106 LASSERT(!name.empty(), /**/);
1108 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1111 LYXERR0("We failed to find the layout '" << to_utf8(name)
1112 << "' in the layout list. You MUST investigate!");
1113 for (const_iterator cit = begin(); cit != end(); ++cit)
1114 LYXERR0(" " << to_utf8(cit->name()));
1116 // we require the name to exist
1117 LASSERT(false, /**/);
1124 bool TextClass::deleteLayout(docstring const & name)
1126 if (name == defaultLayoutName() || name == plainLayoutName())
1129 LayoutList::iterator it =
1130 remove_if(layoutlist_.begin(), layoutlist_.end(),
1131 LayoutNamesEqual(name));
1133 LayoutList::iterator end = layoutlist_.end();
1134 bool const ret = (it != end);
1135 layoutlist_.erase(it, end);
1140 // Load textclass info if not loaded yet
1141 bool TextClass::load(string const & path) const
1146 // Read style-file, provided path is searched before the system ones
1147 // If path is a file, it is loaded directly.
1148 FileName layout_file(path);
1149 if (!path.empty() && !layout_file.isReadableFile())
1150 layout_file = FileName(addName(path, name_ + ".layout"));
1151 if (layout_file.empty() || !layout_file.exists())
1152 layout_file = libFileSearch("layouts", name_, "layout");
1153 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1156 lyxerr << "Error reading `"
1157 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1158 << "'\n(Check `" << name_
1159 << "')\nCheck your installation and "
1160 "try Options/Reconfigure..."
1168 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1171 layoutlist_.push_back(createBasicLayout(n, true));
1175 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1177 // FIXME The fix for the InsetLayout part of 4812 would be here:
1178 // Add the InsetLayout to the document class if it is not found.
1180 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1181 while (!n.empty()) {
1182 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1183 if (cit != cen && cit->first == n)
1185 size_t i = n.find(':');
1186 if (i == string::npos)
1190 return plain_insetlayout_;
1194 docstring const & TextClass::defaultLayoutName() const
1196 // This really should come from the actual layout... (Lgb)
1197 return defaultlayout_;
1201 Layout const & TextClass::defaultLayout() const
1203 return operator[](defaultLayoutName());
1207 bool TextClass::isDefaultLayout(Layout const & layout) const
1209 return layout.name() == defaultLayoutName();
1213 bool TextClass::isPlainLayout(Layout const & layout) const
1215 return layout.name() == plainLayoutName();
1219 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1221 static Layout * defaultLayout = NULL;
1223 if (defaultLayout) {
1224 defaultLayout->setUnknown(unknown);
1225 defaultLayout->setName(name);
1226 return *defaultLayout;
1229 static char const * s = "Margin Static\n"
1230 "LatexType Paragraph\n"
1233 "AlignPossible Left, Right, Center\n"
1234 "LabelType No_Label\n"
1236 istringstream ss(s);
1237 Lexer lex(textClassTags);
1239 defaultLayout = new Layout;
1240 defaultLayout->setUnknown(unknown);
1241 defaultLayout->setName(name);
1242 if (!readStyle(lex, *defaultLayout)) {
1243 // The only way this happens is because the hardcoded layout above
1245 LASSERT(false, /**/);
1247 return *defaultLayout;
1251 /////////////////////////////////////////////////////////////////////////
1253 // DocumentClassBundle
1255 /////////////////////////////////////////////////////////////////////////
1257 DocumentClassBundle::~DocumentClassBundle()
1259 for (size_t i = 0; i != documentClasses_.size(); ++i)
1260 delete documentClasses_[i];
1261 documentClasses_.clear();
1264 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1266 DocumentClass * dc = new DocumentClass(baseClass);
1267 documentClasses_.push_back(dc);
1268 return *documentClasses_.back();
1272 DocumentClassBundle & DocumentClassBundle::get()
1274 static DocumentClassBundle singleton;
1279 DocumentClass & DocumentClassBundle::makeDocumentClass(
1280 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1282 DocumentClass & doc_class = newClass(baseClass);
1283 LayoutModuleList::const_iterator it = modlist.begin();
1284 LayoutModuleList::const_iterator en = modlist.end();
1285 for (; it != en; it++) {
1286 string const modName = *it;
1287 LyXModule * lm = theModuleList[modName];
1289 docstring const msg =
1290 bformat(_("The module %1$s has been requested by\n"
1291 "this document but has not been found in the list of\n"
1292 "available modules. If you recently installed it, you\n"
1293 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1294 frontend::Alert::warning(_("Module not available"), msg);
1297 if (!lm->isAvailable()) {
1298 docstring const msg =
1299 bformat(_("The module %1$s requires a package that is\n"
1300 "not available in your LaTeX installation. LaTeX output\n"
1301 "may not be possible.\n"), from_utf8(modName));
1302 frontend::Alert::warning(_("Package not available"), msg);
1304 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1305 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1306 docstring const msg =
1307 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1308 frontend::Alert::warning(_("Read Error"), msg);
1315 /////////////////////////////////////////////////////////////////////////
1319 /////////////////////////////////////////////////////////////////////////
1321 DocumentClass::DocumentClass(LayoutFile const & tc)
1326 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1328 LayoutList::const_iterator it = layoutlist_.begin();
1329 LayoutList::const_iterator end = layoutlist_.end();
1330 for (; it != end; ++it)
1331 if (it->latexname() == lay)
1337 bool DocumentClass::provides(string const & p) const
1339 return provides_.find(p) != provides_.end();
1343 bool DocumentClass::hasTocLevels() const
1345 return min_toclevel_ != Layout::NOT_IN_TOC;
1349 Layout const & DocumentClass::htmlTOCLayout() const
1351 if (html_toc_section_.empty()) {
1352 // we're going to look for the layout with the minimum toclevel
1353 TextClass::LayoutList::const_iterator lit = begin();
1354 TextClass::LayoutList::const_iterator const len = end();
1355 int minlevel = 1000;
1356 Layout const * lay = NULL;
1357 for (; lit != len; ++lit) {
1358 int const level = lit->toclevel;
1359 // we don't want Part
1360 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1366 html_toc_section_ = lay->name();
1368 // hmm. that is very odd, so we'll do our best
1369 html_toc_section_ = defaultLayoutName();
1371 return operator[](html_toc_section_);
1375 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1377 static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1379 map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1380 if (it != cite_formats_.end())
1382 return default_format;
1386 string const & DocumentClass::getCiteMacro(string const & macro) const
1388 static string empty;
1389 map<string, string>::const_iterator it = cite_macros_.find(macro);
1390 if (it != cite_macros_.end())
1396 /////////////////////////////////////////////////////////////////////////
1400 /////////////////////////////////////////////////////////////////////////
1402 ostream & operator<<(ostream & os, PageSides p)