3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
17 #include "TextClass.h"
19 #include "LayoutFile.h"
23 #include "FloatList.h"
27 #include "ModuleList.h"
29 #include "frontends/alert.h"
31 #include "support/lassert.h"
32 #include "support/debug.h"
33 #include "support/ExceptionMessage.h"
34 #include "support/FileName.h"
35 #include "support/filetools.h"
36 #include "support/gettext.h"
37 #include "support/lstrings.h"
38 #include "support/os.h"
39 #include "support/TempFile.h"
50 using namespace lyx::support;
54 // Keep the changes documented in the Customization manual.
56 // If you change this format, then you MUST also make sure that
57 // your changes do not invalidate the hardcoded layout file in
58 // LayoutFile.cpp. Additions will never do so, but syntax changes
59 // could. See LayoutFileList::addEmptyClass() and, especially, the
60 // definition of the layoutpost string.
61 // You should also run the development/tools/updatelayouts.py script,
62 // to update the format of all of our layout files.
64 int const LAYOUT_FORMAT = 57; //spitz: New Layout tag ParagraphGroup
68 class LayoutNamesEqual : public unary_function<Layout, bool> {
70 LayoutNamesEqual(docstring const & name)
73 bool operator()(Layout const & c) const
75 return c.name() == name_;
82 bool layout2layout(FileName const & filename, FileName const & tempfile)
84 FileName const script = libFileSearch("scripts", "layout2layout.py");
86 LYXERR0("Could not find layout conversion "
87 "script layout2layout.py.");
91 ostringstream command;
92 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
93 << ' ' << quoteName(filename.toFilesystemEncoding())
94 << ' ' << quoteName(tempfile.toFilesystemEncoding());
95 string const command_str = command.str();
97 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
99 cmd_ret const ret = runCommand(command_str);
100 if (ret.first != 0) {
101 LYXERR0("Could not run layout conversion script layout2layout.py.");
108 string translateReadType(TextClass::ReadType rt)
111 case TextClass::BASECLASS:
113 case TextClass::MERGE:
115 case TextClass::MODULE:
116 return "module file";
117 case TextClass::VALIDATION:
127 // This string should not be translated here,
128 // because it is a layout identifier.
129 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
132 InsetLayout DocumentClass::plain_insetlayout_;
135 /////////////////////////////////////////////////////////////////////////
139 /////////////////////////////////////////////////////////////////////////
141 TextClass::TextClass()
142 : loaded_(false), tex_class_avail_(false),
143 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
144 opt_pagestyle_("empty|plain|headings|fancy"), pagestyle_("default"),
145 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3),
146 outputType_(LATEX), outputFormat_("latex"),
147 defaultfont_(sane_font),
148 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
149 min_toclevel_(0), max_toclevel_(0),
150 cite_full_author_list_(true)
155 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
157 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
158 if (!lay.read(lexrc, *this)) {
159 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
163 lay.resfont = lay.font;
164 lay.resfont.realize(defaultfont_);
165 lay.reslabelfont = lay.labelfont;
166 lay.reslabelfont.realize(defaultfont_);
167 return true; // no errors
205 TC_ADDTOHTMLPREAMBLE,
221 LexerKeyword textClassTags[] = {
222 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
223 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
224 { "addtopreamble", TC_ADDTOPREAMBLE },
225 { "citeengine", TC_CITEENGINE },
226 { "citeenginetype", TC_CITEENGINETYPE },
227 { "citeformat", TC_CITEFORMAT },
228 { "classoptions", TC_CLASSOPTIONS },
229 { "columns", TC_COLUMNS },
230 { "counter", TC_COUNTER },
231 { "defaultbiblio", TC_DEFAULTBIBLIO },
232 { "defaultfont", TC_DEFAULTFONT },
233 { "defaultmodule", TC_DEFAULTMODULE },
234 { "defaultstyle", TC_DEFAULTSTYLE },
235 { "excludesmodule", TC_EXCLUDESMODULE },
236 { "float", TC_FLOAT },
237 { "format", TC_FORMAT },
238 { "fullauthorlist", TC_FULLAUTHORLIST },
239 { "htmlpreamble", TC_HTMLPREAMBLE },
240 { "htmlstyles", TC_HTMLSTYLES },
241 { "htmltocsection", TC_HTMLTOCSECTION },
242 { "ifcounter", TC_IFCOUNTER },
243 { "ifstyle", TC_IFSTYLE },
244 { "input", TC_INPUT },
245 { "insetlayout", TC_INSETLAYOUT },
246 { "leftmargin", TC_LEFTMARGIN },
247 { "nocounter", TC_NOCOUNTER },
248 { "nofloat", TC_NOFLOAT },
249 { "noinsetlayout", TC_NOINSETLAYOUT },
250 { "nostyle", TC_NOSTYLE },
251 { "outputformat", TC_OUTPUTFORMAT },
252 { "outputtype", TC_OUTPUTTYPE },
253 { "packageoptions", TC_PKGOPTS },
254 { "pagestyle", TC_PAGESTYLE },
255 { "preamble", TC_PREAMBLE },
256 { "provides", TC_PROVIDES },
257 { "providesmodule", TC_PROVIDESMODULE },
258 { "requires", TC_REQUIRES },
259 { "rightmargin", TC_RIGHTMARGIN },
260 { "secnumdepth", TC_SECNUMDEPTH },
261 { "sides", TC_SIDES },
262 { "style", TC_STYLE },
263 { "titlelatexname", TC_TITLELATEXNAME },
264 { "titlelatextype", TC_TITLELATEXTYPE },
265 { "tocdepth", TC_TOCDEPTH }
271 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
273 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
274 TempFile tmp("convertXXXXXX.layout");
275 FileName const tempfile = tmp.name();
276 bool success = layout2layout(filename, tempfile);
278 success = readWithoutConv(tempfile, rt) == OK;
283 std::string TextClass::convert(std::string const & str)
285 TempFile tmp1("localXXXXXX.layout");
286 FileName const fn = tmp1.name();
287 ofstream os(fn.toFilesystemEncoding().c_str());
290 TempFile tmp2("convert_localXXXXXX.layout");
291 FileName const tempfile = tmp2.name();
292 bool success = layout2layout(fn, tempfile);
295 ifstream is(tempfile.toFilesystemEncoding().c_str());
307 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
309 if (!filename.isReadableFile()) {
310 lyxerr << "Cannot read layout file `" << filename << "'."
315 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
316 to_utf8(makeDisplayPath(filename.absFileName())));
318 // Define the plain layout used in table cells, ert, etc. Note that
319 // we do this before loading any layout file, so that classes can
320 // override features of this layout if they should choose to do so.
321 if (rt == BASECLASS && !hasLayout(plain_layout_))
322 layoutlist_.push_back(createBasicLayout(plain_layout_));
324 Lexer lexrc(textClassTags);
325 lexrc.setFile(filename);
326 ReturnValues retval = read(lexrc, rt);
328 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
329 to_utf8(makeDisplayPath(filename.absFileName())));
335 bool TextClass::read(FileName const & filename, ReadType rt)
337 ReturnValues const retval = readWithoutConv(filename, rt);
338 if (retval != FORMAT_MISMATCH)
341 bool const worx = convertLayoutFormat(filename, rt);
343 LYXERR0 ("Unable to convert " << filename <<
344 " to format " << LAYOUT_FORMAT);
349 TextClass::ReturnValues TextClass::validate(std::string const & str)
352 return tc.read(str, VALIDATION);
356 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
358 Lexer lexrc(textClassTags);
359 istringstream is(str);
361 ReturnValues retval = read(lexrc, rt);
363 if (retval != FORMAT_MISMATCH)
366 // write the layout string to a temporary file
367 TempFile tmp("TextClass_read");
368 FileName const tempfile = tmp.name();
369 ofstream os(tempfile.toFilesystemEncoding().c_str());
371 LYXERR0("Unable to create temporary file");
377 // now try to convert it
378 bool const worx = convertLayoutFormat(tempfile, rt);
380 LYXERR0("Unable to convert internal layout information to format "
388 // Reads a textclass structure from file.
389 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
394 // Format of files before the 'Format' tag was introduced
399 while (lexrc.isOK() && !error) {
400 int le = lexrc.lex();
403 case Lexer::LEX_FEOF:
406 case Lexer::LEX_UNDEF:
407 lexrc.printError("Unknown TextClass tag `$$Token'");
415 // used below to track whether we are in an IfStyle or IfCounter tag.
416 bool ifstyle = false;
417 bool ifcounter = false;
419 switch (static_cast<TextClassTags>(le)) {
423 format = lexrc.getInteger();
426 case TC_OUTPUTFORMAT:
428 outputFormat_ = lexrc.getString();
432 readOutputType(lexrc);
433 switch(outputType_) {
435 outputFormat_ = "latex";
438 outputFormat_ = "docbook";
441 outputFormat_ = "literate";
446 case TC_INPUT: // Include file
448 string const inc = lexrc.getString();
449 FileName tmp = libFileSearch("layouts", inc,
453 lexrc.printError("Could not find input file: " + inc);
455 } else if (!read(tmp, MERGE)) {
456 lexrc.printError("Error reading input file: " + tmp.absFileName());
462 case TC_DEFAULTSTYLE:
464 docstring const name = from_utf8(subst(lexrc.getString(),
466 defaultlayout_ = name;
475 lexrc.printError("No name given for style: `$$Token'.");
479 docstring const name = from_utf8(subst(lexrc.getString(),
482 string s = "Could not read name for style: `$$Token' "
483 + lexrc.getString() + " is probably not valid UTF-8!";
486 // Since we couldn't read the name, we just scan the rest
487 // of the style and discard it.
488 error = !readStyle(lexrc, lay);
489 } else if (hasLayout(name)) {
490 Layout & lay = operator[](name);
491 error = !readStyle(lexrc, lay);
492 } else if (!ifstyle) {
494 layout.setName(name);
495 error = !readStyle(lexrc, layout);
497 layoutlist_.push_back(layout);
499 if (defaultlayout_.empty()) {
500 // We do not have a default layout yet, so we choose
501 // the first layout we encounter.
502 defaultlayout_ = name;
506 // this was an ifstyle where we didn't have the style
507 // scan the rest and discard it
509 readStyle(lexrc, lay);
516 docstring const style = from_utf8(subst(lexrc.getString(),
518 if (!deleteLayout(style))
519 lyxerr << "Cannot delete style `"
520 << to_utf8(style) << '\'' << endl;
524 case TC_NOINSETLAYOUT:
526 docstring const style = from_utf8(subst(lexrc.getString(),
528 if (!deleteInsetLayout(style))
529 LYXERR0("Style `" << style << "' cannot be removed\n"
530 "because it was not found!");
536 columns_ = lexrc.getInteger();
541 switch (lexrc.getInteger()) {
542 case 1: sides_ = OneSide; break;
543 case 2: sides_ = TwoSides; break;
545 lyxerr << "Impossible number of page"
546 " sides, setting to one."
556 pagestyle_ = rtrim(lexrc.getString());
560 defaultfont_ = lyxRead(lexrc);
561 if (!defaultfont_.resolved()) {
562 lexrc.printError("Warning: defaultfont should "
563 "be fully instantiated!");
564 defaultfont_.realize(sane_font);
570 secnumdepth_ = lexrc.getInteger();
575 tocdepth_ = lexrc.getInteger();
578 // First step to support options
579 case TC_CLASSOPTIONS:
580 readClassOptions(lexrc);
584 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
587 case TC_HTMLPREAMBLE:
588 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
592 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
595 case TC_HTMLTOCSECTION:
596 html_toc_section_ = from_utf8(trim(lexrc.getString()));
599 case TC_ADDTOPREAMBLE:
600 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
603 case TC_ADDTOHTMLPREAMBLE:
604 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
607 case TC_ADDTOHTMLSTYLES:
608 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
613 string const feature = lexrc.getString();
615 if (lexrc.getInteger())
616 provides_.insert(feature);
618 provides_.erase(feature);
624 vector<string> const req
625 = getVectorFromString(lexrc.getString());
626 requires_.insert(req.begin(), req.end());
632 string const pkg = lexrc.getString();
634 string const options = lexrc.getString();
635 package_options_[pkg] = options;
639 case TC_DEFAULTMODULE: {
641 string const module = lexrc.getString();
642 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
643 default_modules_.push_back(module);
647 case TC_PROVIDESMODULE: {
649 string const module = lexrc.getString();
650 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
651 provided_modules_.push_back(module);
655 case TC_EXCLUDESMODULE: {
657 string const module = lexrc.getString();
658 // modules already have their own way to exclude other modules
660 LYXERR0("ExcludesModule tag cannot be used in a module!");
663 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
664 excluded_modules_.push_back(module);
668 case TC_LEFTMARGIN: // left margin type
670 leftmargin_ = lexrc.getDocString();
673 case TC_RIGHTMARGIN: // right margin type
675 rightmargin_ = lexrc.getDocString();
678 case TC_INSETLAYOUT: {
680 lexrc.printError("No name given for InsetLayout: `$$Token'.");
684 docstring const name = subst(lexrc.getDocString(), '_', ' ');
686 string s = "Could not read name for InsetLayout: `$$Token' "
687 + lexrc.getString() + " is probably not valid UTF-8!";
690 // Since we couldn't read the name, we just scan the rest
691 // of the style and discard it.
692 il.read(lexrc, *this);
693 // Let's try to continue rather than abort.
695 } else if (hasInsetLayout(name)) {
696 InsetLayout & il = insetlayoutlist_[name];
697 error = !il.read(lexrc, *this);
701 error = !il.read(lexrc, *this);
703 insetlayoutlist_[name] = il;
709 error = !readFloat(lexrc);
713 error = !readCiteEngine(lexrc);
716 case TC_CITEENGINETYPE:
718 opt_enginetype_ = rtrim(lexrc.getString());
722 error = !readCiteFormat(lexrc);
725 case TC_DEFAULTBIBLIO:
727 cite_default_biblio_style_ = rtrim(lexrc.getString());
730 case TC_FULLAUTHORLIST:
732 cite_full_author_list_ &= lexrc.getBool();
737 docstring const cnt = lexrc.getDocString();
738 if (!counters_.remove(cnt))
739 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
748 docstring const name = lexrc.getDocString();
750 string s = "Could not read name for counter: `$$Token' "
751 + lexrc.getString() + " is probably not valid UTF-8!";
752 lexrc.printError(s.c_str());
754 // Since we couldn't read the name, we just scan the rest
758 error = !counters_.read(lexrc, name, !ifcounter);
761 lexrc.printError("No name given for style: `$$Token'.");
766 case TC_TITLELATEXTYPE:
767 readTitleType(lexrc);
770 case TC_TITLELATEXNAME:
772 titlename_ = lexrc.getString();
777 string const nofloat = lexrc.getString();
778 floatlist_.erase(nofloat);
783 // Note that this is triggered the first time through the loop unless
784 // we hit a format tag.
785 if (format != LAYOUT_FORMAT)
786 return FORMAT_MISMATCH;
789 // at present, we abort if we encounter an error,
790 // so there is no point continuing.
797 if (defaultlayout_.empty()) {
798 LYXERR0("Error: Textclass '" << name_
799 << "' is missing a defaultstyle.");
803 // Try to erase "stdinsets" from the provides_ set.
805 // Provides stdinsets 1
806 // declaration simply tells us that the standard insets have been
807 // defined. (It's found in stdinsets.inc but could also be used in
808 // user-defined files.) There isn't really any such package. So we
809 // might as well go ahead and erase it.
810 // If we do not succeed, then it was not there, which means that
811 // the textclass did not provide the definitions of the standard
812 // insets. So we need to try to load them.
813 int erased = provides_.erase("stdinsets");
815 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
818 frontend::Alert::warning(_("Missing File"),
819 _("Could not find stdinsets.inc! This may lead to data loss!"));
821 } else if (!read(tmp, MERGE)) {
822 frontend::Alert::warning(_("Corrupt File"),
823 _("Could not read stdinsets.inc! This may lead to data loss!"));
828 min_toclevel_ = Layout::NOT_IN_TOC;
829 max_toclevel_ = Layout::NOT_IN_TOC;
830 const_iterator lit = begin();
831 const_iterator len = end();
832 for (; lit != len; ++lit) {
833 int const toclevel = lit->toclevel;
834 if (toclevel != Layout::NOT_IN_TOC) {
835 if (min_toclevel_ == Layout::NOT_IN_TOC)
836 min_toclevel_ = toclevel;
838 min_toclevel_ = min(min_toclevel_, toclevel);
839 max_toclevel_ = max(max_toclevel_, toclevel);
842 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
843 << ", maximum is " << max_toclevel_);
845 return (error ? ERROR : OK);
849 void TextClass::readTitleType(Lexer & lexrc)
851 LexerKeyword titleTypeTags[] = {
852 { "commandafter", TITLE_COMMAND_AFTER },
853 { "environment", TITLE_ENVIRONMENT }
856 PushPopHelper pph(lexrc, titleTypeTags);
858 int le = lexrc.lex();
860 case Lexer::LEX_UNDEF:
861 lexrc.printError("Unknown output type `$$Token'");
863 case TITLE_COMMAND_AFTER:
864 case TITLE_ENVIRONMENT:
865 titletype_ = static_cast<TitleLatexType>(le);
868 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
874 void TextClass::readOutputType(Lexer & lexrc)
876 LexerKeyword outputTypeTags[] = {
877 { "docbook", DOCBOOK },
879 { "literate", LITERATE }
882 PushPopHelper pph(lexrc, outputTypeTags);
884 int le = lexrc.lex();
886 case Lexer::LEX_UNDEF:
887 lexrc.printError("Unknown output type `$$Token'");
892 outputType_ = static_cast<OutputType>(le);
895 LYXERR0("Unhandled value " << le);
901 void TextClass::readClassOptions(Lexer & lexrc)
911 LexerKeyword classOptionsTags[] = {
913 {"fontsize", CO_FONTSIZE },
914 {"header", CO_HEADER },
915 {"other", CO_OTHER },
916 {"pagestyle", CO_PAGESTYLE }
919 lexrc.pushTable(classOptionsTags);
921 while (!getout && lexrc.isOK()) {
922 int le = lexrc.lex();
924 case Lexer::LEX_UNDEF:
925 lexrc.printError("Unknown ClassOption tag `$$Token'");
933 opt_fontsize_ = rtrim(lexrc.getString());
937 opt_pagestyle_ = rtrim(lexrc.getString());
941 if (options_.empty())
942 options_ = lexrc.getString();
944 options_ += ',' + lexrc.getString();
948 class_header_ = subst(lexrc.getString(), """, "\"");
959 bool TextClass::readCiteEngine(Lexer & lexrc)
961 int const type = readCiteEngineType(lexrc);
962 if (type & ENGINE_TYPE_AUTHORYEAR)
963 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
964 if (type & ENGINE_TYPE_NUMERICAL)
965 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
966 if (type & ENGINE_TYPE_DEFAULT)
967 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
970 while (!getout && lexrc.isOK()) {
972 def = lexrc.getString();
973 def = subst(def, " ", "");
974 def = subst(def, "\t", "");
975 if (compare_ascii_no_case(def, "end") == 0) {
985 cs.forceUpperCase = true;
989 size_t const n = def.size();
990 for (size_t i = 0; i != n; ++i) {
993 cs.fullAuthorList = true;
994 else if (ichar == '[' && cs.textAfter)
995 cs.textBefore = true;
996 else if (ichar == '[')
998 else if (ichar != ']')
1003 if (type & ENGINE_TYPE_AUTHORYEAR)
1004 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1005 if (type & ENGINE_TYPE_NUMERICAL)
1006 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1007 if (type & ENGINE_TYPE_DEFAULT)
1008 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1014 int TextClass::readCiteEngineType(Lexer & lexrc) const
1016 LATTEST(ENGINE_TYPE_DEFAULT ==
1017 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1018 if (!lexrc.next()) {
1019 lexrc.printError("No cite engine type given for token: `$$Token'.");
1020 return ENGINE_TYPE_DEFAULT;
1022 string const type = rtrim(lexrc.getString());
1023 if (compare_ascii_no_case(type, "authoryear") == 0)
1024 return ENGINE_TYPE_AUTHORYEAR;
1025 else if (compare_ascii_no_case(type, "numerical") == 0)
1026 return ENGINE_TYPE_NUMERICAL;
1027 else if (compare_ascii_no_case(type, "default") != 0) {
1028 string const s = "Unknown cite engine type `" + type
1029 + "' given for token: `$$Token',";
1030 lexrc.printError(s);
1032 return ENGINE_TYPE_DEFAULT;
1036 bool TextClass::readCiteFormat(Lexer & lexrc)
1038 int const type = readCiteEngineType(lexrc);
1041 while (lexrc.isOK()) {
1043 etype = lexrc.getString();
1044 if (compare_ascii_no_case(etype, "end") == 0)
1049 definition = lexrc.getString();
1050 char initchar = etype[0];
1051 if (initchar == '#')
1053 if (initchar == '!' || initchar == '_') {
1054 if (type & ENGINE_TYPE_AUTHORYEAR)
1055 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1056 if (type & ENGINE_TYPE_NUMERICAL)
1057 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1058 if (type & ENGINE_TYPE_DEFAULT)
1059 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1061 if (type & ENGINE_TYPE_AUTHORYEAR)
1062 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1063 if (type & ENGINE_TYPE_NUMERICAL)
1064 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1065 if (type & ENGINE_TYPE_DEFAULT)
1066 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1073 bool TextClass::readFloat(Lexer & lexrc)
1090 FT_ALLOWED_PLACEMENT,
1096 LexerKeyword floatTags[] = {
1097 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1098 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1099 { "allowswide", FT_ALLOWS_WIDE },
1101 { "extension", FT_EXT },
1102 { "guiname", FT_NAME },
1103 { "htmlattr", FT_HTMLATTR },
1104 { "htmlstyle", FT_HTMLSTYLE },
1105 { "htmltag", FT_HTMLTAG },
1106 { "ispredefined", FT_PREDEFINED },
1107 { "listcommand", FT_LISTCOMMAND },
1108 { "listname", FT_LISTNAME },
1109 { "numberwithin", FT_WITHIN },
1110 { "placement", FT_PLACEMENT },
1111 { "refprefix", FT_REFPREFIX },
1112 { "style", FT_STYLE },
1113 { "type", FT_TYPE },
1114 { "usesfloatpkg", FT_USESFLOAT }
1117 lexrc.pushTable(floatTags);
1127 string allowed_placement = "!htbpH";
1132 bool usesfloat = true;
1133 bool ispredefined = false;
1134 bool allowswide = true;
1135 bool allowssideways = true;
1137 bool getout = false;
1138 while (!getout && lexrc.isOK()) {
1139 int le = lexrc.lex();
1141 case Lexer::LEX_UNDEF:
1142 lexrc.printError("Unknown float tag `$$Token'");
1150 type = lexrc.getString();
1151 if (floatlist_.typeExist(type)) {
1152 Floating const & fl = floatlist_.getType(type);
1153 placement = fl.placement();
1155 within = fl.within();
1158 listname = fl.listName();
1159 usesfloat = fl.usesFloatPkg();
1160 ispredefined = fl.isPredefined();
1161 listcommand = fl.listCommand();
1162 refprefix = fl.refPrefix();
1167 name = lexrc.getString();
1171 placement = lexrc.getString();
1173 case FT_ALLOWED_PLACEMENT:
1175 allowed_placement = lexrc.getString();
1179 ext = lexrc.getString();
1183 within = lexrc.getString();
1184 if (within == "none")
1189 style = lexrc.getString();
1191 case FT_LISTCOMMAND:
1193 listcommand = lexrc.getString();
1197 refprefix = lexrc.getString();
1201 listname = lexrc.getString();
1205 usesfloat = lexrc.getBool();
1209 ispredefined = lexrc.getBool();
1211 case FT_ALLOWS_SIDEWAYS:
1213 allowssideways = lexrc.getBool();
1215 case FT_ALLOWS_WIDE:
1217 allowswide = lexrc.getBool();
1221 htmlattr = lexrc.getString();
1225 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1229 htmltag = lexrc.getString();
1239 // Here we have a full float if getout == true
1241 if (!usesfloat && listcommand.empty()) {
1242 // if this float uses the same auxfile as an existing one,
1243 // there is no need for it to provide a list command.
1244 FloatList::const_iterator it = floatlist_.begin();
1245 FloatList::const_iterator en = floatlist_.end();
1246 bool found_ext = false;
1247 for (; it != en; ++it) {
1248 if (it->second.ext() == ext) {
1254 LYXERR0("The layout does not provide a list command " <<
1255 "for the float `" << type << "'. LyX will " <<
1256 "not be able to produce a float list.");
1258 Floating fl(type, placement, ext, within, style, name,
1259 listname, listcommand, refprefix, allowed_placement,
1260 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1261 allowswide, allowssideways);
1262 floatlist_.newFloat(fl);
1263 // each float has its own counter
1264 counters_.newCounter(from_ascii(type), from_ascii(within),
1265 docstring(), docstring());
1266 // also define sub-float counters
1267 docstring const subtype = "sub-" + from_ascii(type);
1268 counters_.newCounter(subtype, from_ascii(type),
1269 "\\alph{" + subtype + "}", docstring());
1275 string const & TextClass::prerequisites(string const & sep) const
1277 if (contains(prerequisites_, ',')) {
1278 vector<string> const pres = getVectorFromString(prerequisites_);
1279 prerequisites_ = getStringFromVector(pres, sep);
1281 return prerequisites_;
1285 bool TextClass::hasLayout(docstring const & n) const
1287 docstring const name = n.empty() ? defaultLayoutName() : n;
1289 return find_if(layoutlist_.begin(), layoutlist_.end(),
1290 LayoutNamesEqual(name))
1291 != layoutlist_.end();
1295 bool TextClass::hasInsetLayout(docstring const & n) const
1299 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1300 return it != insetlayoutlist_.end();
1304 Layout const & TextClass::operator[](docstring const & name) const
1306 LATTEST(!name.empty());
1309 find_if(begin(), end(), LayoutNamesEqual(name));
1312 LYXERR0("We failed to find the layout '" << name
1313 << "' in the layout list. You MUST investigate!");
1314 for (const_iterator cit = begin(); cit != end(); ++cit)
1315 lyxerr << " " << to_utf8(cit->name()) << endl;
1317 // We require the name to exist
1318 static const Layout dummy;
1319 LASSERT(false, return dummy);
1326 Layout & TextClass::operator[](docstring const & name)
1328 LATTEST(!name.empty());
1329 // Safe to continue, given what we do below.
1331 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1334 LYXERR0("We failed to find the layout '" << to_utf8(name)
1335 << "' in the layout list. You MUST investigate!");
1336 for (const_iterator cit = begin(); cit != end(); ++cit)
1337 LYXERR0(" " << to_utf8(cit->name()));
1339 // we require the name to exist
1341 // we are here only in release mode
1342 layoutlist_.push_back(createBasicLayout(name, true));
1343 it = find_if(begin(), end(), LayoutNamesEqual(name));
1350 bool TextClass::deleteLayout(docstring const & name)
1352 if (name == defaultLayoutName() || name == plainLayoutName())
1355 LayoutList::iterator it =
1356 remove_if(layoutlist_.begin(), layoutlist_.end(),
1357 LayoutNamesEqual(name));
1359 LayoutList::iterator end = layoutlist_.end();
1360 bool const ret = (it != end);
1361 layoutlist_.erase(it, end);
1366 bool TextClass::deleteInsetLayout(docstring const & name)
1368 return insetlayoutlist_.erase(name);
1372 // Load textclass info if not loaded yet
1373 bool TextClass::load(string const & path) const
1378 // Read style-file, provided path is searched before the system ones
1379 // If path is a file, it is loaded directly.
1380 FileName layout_file(path);
1381 if (!path.empty() && !layout_file.isReadableFile())
1382 layout_file = FileName(addName(path, name_ + ".layout"));
1383 if (layout_file.empty() || !layout_file.exists())
1384 layout_file = libFileSearch("layouts", name_, "layout");
1385 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1388 lyxerr << "Error reading `"
1389 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1390 << "'\n(Check `" << name_
1391 << "')\nCheck your installation and "
1392 "try Options/Reconfigure..."
1400 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1405 layoutlist_.push_back(createBasicLayout(n, true));
1410 string DocumentClass::forcedLayouts() const
1414 const_iterator const e = end();
1415 for (const_iterator i = begin(); i != e; ++i) {
1416 if (i->forcelocal > 0) {
1418 os << "Format " << LAYOUT_FORMAT << '\n';
1428 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1430 // FIXME The fix for the InsetLayout part of 4812 would be here:
1431 // Add the InsetLayout to the document class if it is not found.
1433 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1434 while (!n.empty()) {
1435 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1436 if (cit != cen && cit->first == n) {
1437 if (cit->second.obsoleted_by().empty())
1439 n = cit->second.obsoleted_by();
1440 return insetLayout(n);
1442 // If we have a generic prefix (e.g., "Note:"),
1443 // try if this one alone is found.
1444 size_t i = n.find(':');
1445 if (i == string::npos)
1449 // Layout "name" not found.
1450 return plain_insetlayout_;
1454 docstring const & TextClass::defaultLayoutName() const
1456 return defaultlayout_;
1460 Layout const & TextClass::defaultLayout() const
1462 return operator[](defaultLayoutName());
1466 bool TextClass::isDefaultLayout(Layout const & layout) const
1468 return layout.name() == defaultLayoutName();
1472 bool TextClass::isPlainLayout(Layout const & layout) const
1474 return layout.name() == plainLayoutName();
1478 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1480 static Layout * defaultLayout = NULL;
1482 if (defaultLayout) {
1483 defaultLayout->setUnknown(unknown);
1484 defaultLayout->setName(name);
1485 return *defaultLayout;
1488 static char const * s = "Margin Static\n"
1489 "LatexType Paragraph\n"
1492 "AlignPossible Left, Right, Center\n"
1493 "LabelType No_Label\n"
1495 istringstream ss(s);
1496 Lexer lex(textClassTags);
1498 defaultLayout = new Layout;
1499 defaultLayout->setUnknown(unknown);
1500 defaultLayout->setName(name);
1501 if (!readStyle(lex, *defaultLayout)) {
1502 // The only way this happens is because the hardcoded layout above
1506 return *defaultLayout;
1510 DocumentClassPtr getDocumentClass(
1511 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1514 DocumentClassPtr doc_class =
1515 DocumentClassPtr(new DocumentClass(baseClass));
1516 LayoutModuleList::const_iterator it = modlist.begin();
1517 LayoutModuleList::const_iterator en = modlist.end();
1518 for (; it != en; ++it) {
1519 string const modName = *it;
1520 LyXModule * lm = theModuleList[modName];
1522 docstring const msg =
1523 bformat(_("The module %1$s has been requested by\n"
1524 "this document but has not been found in the list of\n"
1525 "available modules. If you recently installed it, you\n"
1526 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1528 frontend::Alert::warning(_("Module not available"), msg);
1531 if (!lm->isAvailable() && !clone) {
1532 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1533 docstring const msg =
1534 bformat(_("The module %1$s requires a package that is not\n"
1535 "available in your LaTeX installation, or a converter that\n"
1536 "you have not installed. LaTeX output may not be possible.\n"
1537 "Missing prerequisites:\n"
1539 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1540 from_utf8(modName), prereqs);
1541 frontend::Alert::warning(_("Package not available"), msg, true);
1543 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1544 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1545 docstring const msg =
1546 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1547 frontend::Alert::warning(_("Read Error"), msg);
1554 /////////////////////////////////////////////////////////////////////////
1558 /////////////////////////////////////////////////////////////////////////
1560 DocumentClass::DocumentClass(LayoutFile const & tc)
1565 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1567 LayoutList::const_iterator it = layoutlist_.begin();
1568 LayoutList::const_iterator end = layoutlist_.end();
1569 for (; it != end; ++it)
1570 if (it->latexname() == lay)
1576 bool DocumentClass::provides(string const & p) const
1578 return provides_.find(p) != provides_.end();
1582 bool DocumentClass::hasTocLevels() const
1584 return min_toclevel_ != Layout::NOT_IN_TOC;
1588 Layout const & DocumentClass::getTOCLayout() const
1590 // we're going to look for the layout with the minimum toclevel
1591 TextClass::LayoutList::const_iterator lit = begin();
1592 TextClass::LayoutList::const_iterator const len = end();
1593 int minlevel = 1000;
1594 Layout const * lay = NULL;
1595 for (; lit != len; ++lit) {
1596 int const level = lit->toclevel;
1597 // we don't want Part or unnumbered sections
1598 if (level == Layout::NOT_IN_TOC || level < 0
1599 || level >= minlevel || lit->counter.empty())
1606 // hmm. that is very odd, so we'll do our best.
1607 return operator[](defaultLayoutName());
1611 Layout const & DocumentClass::htmlTOCLayout() const
1613 if (html_toc_section_.empty())
1614 html_toc_section_ = getTOCLayout().name();
1615 return operator[](html_toc_section_);
1619 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1620 string const & entry, string const & fallback) const
1622 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1624 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1625 if (itype == cite_formats_.end())
1626 return default_format;
1627 map<string, string>::const_iterator it = itype->second.find(entry);
1628 if (it == itype->second.end() && !fallback.empty())
1629 it = itype->second.find(fallback);
1630 if (it == itype->second.end())
1631 return default_format;
1636 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1637 string const & macro) const
1639 static string empty;
1640 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1641 if (itype == cite_macros_.end())
1643 map<string, string>::const_iterator it = itype->second.find(macro);
1644 if (it == itype->second.end())
1650 vector<string> const DocumentClass::citeCommands(
1651 CiteEngineType const & type) const
1653 vector<CitationStyle> const styles = citeStyles(type);
1654 vector<CitationStyle>::const_iterator it = styles.begin();
1655 vector<CitationStyle>::const_iterator end = styles.end();
1656 vector<string> cmds;
1657 for (; it != end; ++it) {
1658 CitationStyle const cite = *it;
1659 cmds.push_back(cite.cmd);
1665 vector<CitationStyle> const & DocumentClass::citeStyles(
1666 CiteEngineType const & type) const
1668 static vector<CitationStyle> empty;
1669 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1670 if (it == cite_styles_.end())
1676 /////////////////////////////////////////////////////////////////////////
1680 /////////////////////////////////////////////////////////////////////////
1682 ostream & operator<<(ostream & os, PageSides p)