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 (or ask someone who has bash to run) the
62 // development/tools/updatelayouts.sh script, to update the format of
63 // all of our layout files.
65 int const LAYOUT_FORMAT = 51; //spitz: add ToggleIndent tag
69 class LayoutNamesEqual : public unary_function<Layout, bool> {
71 LayoutNamesEqual(docstring const & name)
74 bool operator()(Layout const & c) const
76 return c.name() == name_;
83 bool layout2layout(FileName const & filename, FileName const & tempfile)
85 FileName const script = libFileSearch("scripts", "layout2layout.py");
87 LYXERR0("Could not find layout conversion "
88 "script layout2layout.py.");
92 ostringstream command;
93 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
94 << ' ' << quoteName(filename.toFilesystemEncoding())
95 << ' ' << quoteName(tempfile.toFilesystemEncoding());
96 string const command_str = command.str();
98 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
100 cmd_ret const ret = runCommand(command_str);
101 if (ret.first != 0) {
102 LYXERR0("Could not run layout conversion script layout2layout.py.");
109 string translateReadType(TextClass::ReadType rt)
112 case TextClass::BASECLASS:
114 case TextClass::MERGE:
116 case TextClass::MODULE:
117 return "module file";
118 case TextClass::VALIDATION:
128 // This string should not be translated here,
129 // because it is a layout identifier.
130 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
133 InsetLayout DocumentClass::plain_insetlayout_;
136 /////////////////////////////////////////////////////////////////////////
140 /////////////////////////////////////////////////////////////////////////
142 TextClass::TextClass()
145 outputFormat_ = "latex";
150 pagestyle_ = "default";
151 defaultfont_ = sane_font;
152 opt_enginetype_ = "authoryear|numerical";
153 opt_fontsize_ = "10|11|12";
154 opt_pagestyle_ = "empty|plain|headings|fancy";
155 cite_full_author_list_ = true;
156 titletype_ = TITLE_COMMAND_AFTER;
157 titlename_ = "maketitle";
159 _("Plain Layout"); // a hack to make this translatable
163 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
165 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
166 if (!lay.read(lexrc, *this)) {
167 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
171 lay.resfont = lay.font;
172 lay.resfont.realize(defaultfont_);
173 lay.reslabelfont = lay.labelfont;
174 lay.reslabelfont.realize(defaultfont_);
175 return true; // no errors
213 TC_ADDTOHTMLPREAMBLE,
229 LexerKeyword textClassTags[] = {
230 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
231 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
232 { "addtopreamble", TC_ADDTOPREAMBLE },
233 { "citeengine", TC_CITEENGINE },
234 { "citeenginetype", TC_CITEENGINETYPE },
235 { "citeformat", TC_CITEFORMAT },
236 { "classoptions", TC_CLASSOPTIONS },
237 { "columns", TC_COLUMNS },
238 { "counter", TC_COUNTER },
239 { "defaultbiblio", TC_DEFAULTBIBLIO },
240 { "defaultfont", TC_DEFAULTFONT },
241 { "defaultmodule", TC_DEFAULTMODULE },
242 { "defaultstyle", TC_DEFAULTSTYLE },
243 { "excludesmodule", TC_EXCLUDESMODULE },
244 { "float", TC_FLOAT },
245 { "format", TC_FORMAT },
246 { "fullauthorlist", TC_FULLAUTHORLIST },
247 { "htmlpreamble", TC_HTMLPREAMBLE },
248 { "htmlstyles", TC_HTMLSTYLES },
249 { "htmltocsection", TC_HTMLTOCSECTION },
250 { "ifcounter", TC_IFCOUNTER },
251 { "ifstyle", TC_IFSTYLE },
252 { "input", TC_INPUT },
253 { "insetlayout", TC_INSETLAYOUT },
254 { "leftmargin", TC_LEFTMARGIN },
255 { "nocounter", TC_NOCOUNTER },
256 { "nofloat", TC_NOFLOAT },
257 { "noinsetlayout", TC_NOINSETLAYOUT },
258 { "nostyle", TC_NOSTYLE },
259 { "outputformat", TC_OUTPUTFORMAT },
260 { "outputtype", TC_OUTPUTTYPE },
261 { "packageoptions", TC_PKGOPTS },
262 { "pagestyle", TC_PAGESTYLE },
263 { "preamble", TC_PREAMBLE },
264 { "provides", TC_PROVIDES },
265 { "providesmodule", TC_PROVIDESMODULE },
266 { "requires", TC_REQUIRES },
267 { "rightmargin", TC_RIGHTMARGIN },
268 { "secnumdepth", TC_SECNUMDEPTH },
269 { "sides", TC_SIDES },
270 { "style", TC_STYLE },
271 { "titlelatexname", TC_TITLELATEXNAME },
272 { "titlelatextype", TC_TITLELATEXTYPE },
273 { "tocdepth", TC_TOCDEPTH }
279 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
281 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
282 TempFile tmp("convertXXXXXX.layout");
283 FileName const tempfile = tmp.name();
284 bool success = layout2layout(filename, tempfile);
286 success = readWithoutConv(tempfile, rt) == OK;
291 std::string TextClass::convert(std::string const & str)
293 TempFile tmp1("localXXXXXX.layout");
294 FileName const fn = tmp1.name();
295 ofstream os(fn.toFilesystemEncoding().c_str());
298 TempFile tmp2("convert_localXXXXXX.layout");
299 FileName const tempfile = tmp2.name();
300 bool success = layout2layout(fn, tempfile);
303 ifstream is(tempfile.toFilesystemEncoding().c_str());
315 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
317 if (!filename.isReadableFile()) {
318 lyxerr << "Cannot read layout file `" << filename << "'."
323 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
324 to_utf8(makeDisplayPath(filename.absFileName())));
326 // Define the plain layout used in table cells, ert, etc. Note that
327 // we do this before loading any layout file, so that classes can
328 // override features of this layout if they should choose to do so.
329 if (rt == BASECLASS && !hasLayout(plain_layout_))
330 layoutlist_.push_back(createBasicLayout(plain_layout_));
332 Lexer lexrc(textClassTags);
333 lexrc.setFile(filename);
334 ReturnValues retval = read(lexrc, rt);
336 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
337 to_utf8(makeDisplayPath(filename.absFileName())));
343 bool TextClass::read(FileName const & filename, ReadType rt)
345 ReturnValues const retval = readWithoutConv(filename, rt);
346 if (retval != FORMAT_MISMATCH)
349 bool const worx = convertLayoutFormat(filename, rt);
351 LYXERR0 ("Unable to convert " << filename <<
352 " to format " << LAYOUT_FORMAT);
357 TextClass::ReturnValues TextClass::validate(std::string const & str)
360 return tc.read(str, VALIDATION);
364 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
366 Lexer lexrc(textClassTags);
367 istringstream is(str);
369 ReturnValues retval = read(lexrc, rt);
371 if (retval != FORMAT_MISMATCH)
374 // write the layout string to a temporary file
375 TempFile tmp("TextClass_read");
376 FileName const tempfile = tmp.name();
377 ofstream os(tempfile.toFilesystemEncoding().c_str());
379 LYXERR0("Unable to create temporary file");
385 // now try to convert it
386 bool const worx = convertLayoutFormat(tempfile, rt);
388 LYXERR0("Unable to convert internal layout information to format "
396 // Reads a textclass structure from file.
397 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
402 // Format of files before the 'Format' tag was introduced
407 while (lexrc.isOK() && !error) {
408 int le = lexrc.lex();
411 case Lexer::LEX_FEOF:
414 case Lexer::LEX_UNDEF:
415 lexrc.printError("Unknown TextClass tag `$$Token'");
423 // used below to track whether we are in an IfStyle or IfCounter tag.
424 bool ifstyle = false;
425 bool ifcounter = false;
427 switch (static_cast<TextClassTags>(le)) {
431 format = lexrc.getInteger();
434 case TC_OUTPUTFORMAT:
436 outputFormat_ = lexrc.getString();
440 readOutputType(lexrc);
441 switch(outputType_) {
443 outputFormat_ = "latex";
446 outputFormat_ = "docbook";
449 outputFormat_ = "literate";
454 case TC_INPUT: // Include file
456 string const inc = lexrc.getString();
457 FileName tmp = libFileSearch("layouts", inc,
461 lexrc.printError("Could not find input file: " + inc);
463 } else if (!read(tmp, MERGE)) {
464 lexrc.printError("Error reading input file: " + tmp.absFileName());
470 case TC_DEFAULTSTYLE:
472 docstring const name = from_utf8(subst(lexrc.getString(),
474 defaultlayout_ = name;
483 lexrc.printError("No name given for style: `$$Token'.");
487 docstring const name = from_utf8(subst(lexrc.getString(),
490 string s = "Could not read name for style: `$$Token' "
491 + lexrc.getString() + " is probably not valid UTF-8!";
494 // Since we couldn't read the name, we just scan the rest
495 // of the style and discard it.
496 error = !readStyle(lexrc, lay);
497 } else if (hasLayout(name)) {
498 Layout & lay = operator[](name);
499 error = !readStyle(lexrc, lay);
500 } else if (!ifstyle) {
502 layout.setName(name);
503 error = !readStyle(lexrc, layout);
505 layoutlist_.push_back(layout);
507 if (defaultlayout_.empty()) {
508 // We do not have a default layout yet, so we choose
509 // the first layout we encounter.
510 defaultlayout_ = name;
514 // this was an ifstyle where we didn't have the style
515 // scan the rest and discard it
517 readStyle(lexrc, lay);
527 docstring const style = from_utf8(subst(lexrc.getString(),
529 if (!deleteLayout(style))
530 lyxerr << "Cannot delete style `"
531 << to_utf8(style) << '\'' << endl;
535 case TC_NOINSETLAYOUT:
537 docstring const style = from_utf8(subst(lexrc.getString(),
539 if (!deleteInsetLayout(style))
540 LYXERR0("Style `" << style << "' cannot be removed\n"
541 "because it was not found!");
547 columns_ = lexrc.getInteger();
552 switch (lexrc.getInteger()) {
553 case 1: sides_ = OneSide; break;
554 case 2: sides_ = TwoSides; break;
556 lyxerr << "Impossible number of page"
557 " sides, setting to one."
567 pagestyle_ = rtrim(lexrc.getString());
571 defaultfont_ = lyxRead(lexrc);
572 if (!defaultfont_.resolved()) {
573 lexrc.printError("Warning: defaultfont should "
574 "be fully instantiated!");
575 defaultfont_.realize(sane_font);
581 secnumdepth_ = lexrc.getInteger();
586 tocdepth_ = lexrc.getInteger();
589 // First step to support options
590 case TC_CLASSOPTIONS:
591 readClassOptions(lexrc);
595 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
598 case TC_HTMLPREAMBLE:
599 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
603 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
606 case TC_HTMLTOCSECTION:
607 html_toc_section_ = from_utf8(trim(lexrc.getString()));
610 case TC_ADDTOPREAMBLE:
611 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
614 case TC_ADDTOHTMLPREAMBLE:
615 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
618 case TC_ADDTOHTMLSTYLES:
619 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
624 string const feature = lexrc.getString();
626 if (lexrc.getInteger())
627 provides_.insert(feature);
629 provides_.erase(feature);
635 vector<string> const req
636 = getVectorFromString(lexrc.getString());
637 requires_.insert(req.begin(), req.end());
643 string const pkg = lexrc.getString();
645 string const options = lexrc.getString();
646 package_options_[pkg] = options;
650 case TC_DEFAULTMODULE: {
652 string const module = lexrc.getString();
653 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
654 default_modules_.push_back(module);
658 case TC_PROVIDESMODULE: {
660 string const module = lexrc.getString();
661 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
662 provided_modules_.push_back(module);
666 case TC_EXCLUDESMODULE: {
668 string const module = lexrc.getString();
669 // modules already have their own way to exclude other modules
671 LYXERR0("ExcludesModule tag cannot be used in a module!");
674 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
675 excluded_modules_.push_back(module);
679 case TC_LEFTMARGIN: // left margin type
681 leftmargin_ = lexrc.getDocString();
684 case TC_RIGHTMARGIN: // right margin type
686 rightmargin_ = lexrc.getDocString();
689 case TC_INSETLAYOUT: {
691 lexrc.printError("No name given for InsetLayout: `$$Token'.");
695 docstring const name = subst(lexrc.getDocString(), '_', ' ');
697 string s = "Could not read name for InsetLayout: `$$Token' "
698 + lexrc.getString() + " is probably not valid UTF-8!";
701 // Since we couldn't read the name, we just scan the rest
702 // of the style and discard it.
703 il.read(lexrc, *this);
704 // Let's try to continue rather than abort.
706 } else if (hasInsetLayout(name)) {
707 InsetLayout & il = insetlayoutlist_[name];
708 error = !il.read(lexrc, *this);
712 error = !il.read(lexrc, *this);
714 insetlayoutlist_[name] = il;
720 error = !readFloat(lexrc);
724 error = !readCiteEngine(lexrc);
727 case TC_CITEENGINETYPE:
729 opt_enginetype_ = rtrim(lexrc.getString());
733 error = !readCiteFormat(lexrc);
736 case TC_DEFAULTBIBLIO:
738 cite_default_biblio_style_ = rtrim(lexrc.getString());
741 case TC_FULLAUTHORLIST:
743 cite_full_author_list_ &= lexrc.getBool();
748 docstring const cnt = lexrc.getDocString();
749 if (!counters_.remove(cnt))
750 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
758 docstring const name = lexrc.getDocString();
760 string s = "Could not read name for counter: `$$Token' "
761 + lexrc.getString() + " is probably not valid UTF-8!";
762 lexrc.printError(s.c_str());
764 // Since we couldn't read the name, we just scan the rest
768 error = !counters_.read(lexrc, name, !ifcounter);
771 lexrc.printError("No name given for style: `$$Token'.");
778 case TC_TITLELATEXTYPE:
779 readTitleType(lexrc);
782 case TC_TITLELATEXNAME:
784 titlename_ = lexrc.getString();
789 string const nofloat = lexrc.getString();
790 floatlist_.erase(nofloat);
795 // Note that this is triggered the first time through the loop unless
796 // we hit a format tag.
797 if (format != LAYOUT_FORMAT)
798 return FORMAT_MISMATCH;
801 // at present, we abort if we encounter an error,
802 // so there is no point continuing.
807 return (error ? ERROR : OK);
809 if (defaultlayout_.empty()) {
810 LYXERR0("Error: Textclass '" << name_
811 << "' is missing a defaultstyle.");
815 // Try to erase "stdinsets" from the provides_ set.
817 // Provides stdinsets 1
818 // declaration simply tells us that the standard insets have been
819 // defined. (It's found in stdinsets.inc but could also be used in
820 // user-defined files.) There isn't really any such package. So we
821 // might as well go ahead and erase it.
822 // If we do not succeed, then it was not there, which means that
823 // the textclass did not provide the definitions of the standard
824 // insets. So we need to try to load them.
825 int erased = provides_.erase("stdinsets");
827 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
830 frontend::Alert::warning(_("Missing File"),
831 _("Could not find stdinsets.inc! This may lead to data loss!"));
833 } else if (!read(tmp, MERGE)) {
834 frontend::Alert::warning(_("Corrupt File"),
835 _("Could not read stdinsets.inc! This may lead to data loss!"));
840 min_toclevel_ = Layout::NOT_IN_TOC;
841 max_toclevel_ = Layout::NOT_IN_TOC;
842 const_iterator lit = begin();
843 const_iterator len = end();
844 for (; lit != len; ++lit) {
845 int const toclevel = lit->toclevel;
846 if (toclevel != Layout::NOT_IN_TOC) {
847 if (min_toclevel_ == Layout::NOT_IN_TOC)
848 min_toclevel_ = toclevel;
850 min_toclevel_ = min(min_toclevel_, toclevel);
851 max_toclevel_ = max(max_toclevel_, toclevel);
854 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
855 << ", maximum is " << max_toclevel_);
857 return (error ? ERROR : OK);
861 void TextClass::readTitleType(Lexer & lexrc)
863 LexerKeyword titleTypeTags[] = {
864 { "commandafter", TITLE_COMMAND_AFTER },
865 { "environment", TITLE_ENVIRONMENT }
868 PushPopHelper pph(lexrc, titleTypeTags);
870 int le = lexrc.lex();
872 case Lexer::LEX_UNDEF:
873 lexrc.printError("Unknown output type `$$Token'");
875 case TITLE_COMMAND_AFTER:
876 case TITLE_ENVIRONMENT:
877 titletype_ = static_cast<TitleLatexType>(le);
880 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
886 void TextClass::readOutputType(Lexer & lexrc)
888 LexerKeyword outputTypeTags[] = {
889 { "docbook", DOCBOOK },
891 { "literate", LITERATE }
894 PushPopHelper pph(lexrc, outputTypeTags);
896 int le = lexrc.lex();
898 case Lexer::LEX_UNDEF:
899 lexrc.printError("Unknown output type `$$Token'");
904 outputType_ = static_cast<OutputType>(le);
907 LYXERR0("Unhandled value " << le);
913 void TextClass::readClassOptions(Lexer & lexrc)
923 LexerKeyword classOptionsTags[] = {
925 {"fontsize", CO_FONTSIZE },
926 {"header", CO_HEADER },
927 {"other", CO_OTHER },
928 {"pagestyle", CO_PAGESTYLE }
931 lexrc.pushTable(classOptionsTags);
933 while (!getout && lexrc.isOK()) {
934 int le = lexrc.lex();
936 case Lexer::LEX_UNDEF:
937 lexrc.printError("Unknown ClassOption tag `$$Token'");
945 opt_fontsize_ = rtrim(lexrc.getString());
949 opt_pagestyle_ = rtrim(lexrc.getString());
953 if (options_.empty())
954 options_ = lexrc.getString();
956 options_ += ',' + lexrc.getString();
960 class_header_ = subst(lexrc.getString(), """, "\"");
971 bool TextClass::readCiteEngine(Lexer & lexrc)
973 int const type = readCiteEngineType(lexrc);
974 if (type & ENGINE_TYPE_AUTHORYEAR)
975 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
976 if (type & ENGINE_TYPE_NUMERICAL)
977 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
978 if (type & ENGINE_TYPE_DEFAULT)
979 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
982 while (!getout && lexrc.isOK()) {
984 def = lexrc.getString();
985 def = subst(def, " ", "");
986 def = subst(def, "\t", "");
987 if (compare_ascii_no_case(def, "end") == 0) {
997 cs.forceUpperCase = true;
1001 size_t const n = def.size();
1002 for (size_t i = 0; i != n; ++i) {
1005 cs.fullAuthorList = true;
1006 else if (ichar == '[' && cs.textAfter)
1007 cs.textBefore = true;
1008 else if (ichar == '[')
1009 cs.textAfter = true;
1010 else if (ichar != ']')
1015 if (type & ENGINE_TYPE_AUTHORYEAR)
1016 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1017 if (type & ENGINE_TYPE_NUMERICAL)
1018 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1019 if (type & ENGINE_TYPE_DEFAULT)
1020 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1026 int TextClass::readCiteEngineType(Lexer & lexrc) const
1028 LATTEST(ENGINE_TYPE_DEFAULT ==
1029 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1030 if (!lexrc.next()) {
1031 lexrc.printError("No cite engine type given for token: `$$Token'.");
1032 return ENGINE_TYPE_DEFAULT;
1034 string const type = rtrim(lexrc.getString());
1035 if (compare_ascii_no_case(type, "authoryear") == 0)
1036 return ENGINE_TYPE_AUTHORYEAR;
1037 else if (compare_ascii_no_case(type, "numerical") == 0)
1038 return ENGINE_TYPE_NUMERICAL;
1039 else if (compare_ascii_no_case(type, "default") != 0) {
1040 string const s = "Unknown cite engine type `" + type
1041 + "' given for token: `$$Token',";
1042 lexrc.printError(s);
1044 return ENGINE_TYPE_DEFAULT;
1048 bool TextClass::readCiteFormat(Lexer & lexrc)
1050 int const type = readCiteEngineType(lexrc);
1053 while (lexrc.isOK()) {
1055 etype = lexrc.getString();
1056 if (compare_ascii_no_case(etype, "end") == 0)
1061 definition = lexrc.getString();
1062 char initchar = etype[0];
1063 if (initchar == '#')
1065 if (initchar == '!' || initchar == '_') {
1066 if (type & ENGINE_TYPE_AUTHORYEAR)
1067 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1068 if (type & ENGINE_TYPE_NUMERICAL)
1069 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1070 if (type & ENGINE_TYPE_DEFAULT)
1071 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1073 if (type & ENGINE_TYPE_AUTHORYEAR)
1074 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1075 if (type & ENGINE_TYPE_NUMERICAL)
1076 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1077 if (type & ENGINE_TYPE_DEFAULT)
1078 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1085 bool TextClass::readFloat(Lexer & lexrc)
1105 LexerKeyword floatTags[] = {
1107 { "extension", FT_EXT },
1108 { "guiname", FT_NAME },
1109 { "htmlattr", FT_HTMLATTR },
1110 { "htmlstyle", FT_HTMLSTYLE },
1111 { "htmltag", FT_HTMLTAG },
1112 { "ispredefined", FT_PREDEFINED },
1113 { "listcommand", FT_LISTCOMMAND },
1114 { "listname", FT_LISTNAME },
1115 { "numberwithin", FT_WITHIN },
1116 { "placement", FT_PLACEMENT },
1117 { "refprefix", FT_REFPREFIX },
1118 { "style", FT_STYLE },
1119 { "type", FT_TYPE },
1120 { "usesfloatpkg", FT_USESFLOAT }
1123 lexrc.pushTable(floatTags);
1137 bool usesfloat = true;
1138 bool ispredefined = false;
1140 bool getout = false;
1141 while (!getout && lexrc.isOK()) {
1142 int le = lexrc.lex();
1144 case Lexer::LEX_UNDEF:
1145 lexrc.printError("Unknown float tag `$$Token'");
1153 type = lexrc.getString();
1154 if (floatlist_.typeExist(type)) {
1155 Floating const & fl = floatlist_.getType(type);
1156 placement = fl.placement();
1158 within = fl.within();
1161 listname = fl.listName();
1162 usesfloat = fl.usesFloatPkg();
1163 ispredefined = fl.isPredefined();
1164 listcommand = fl.listCommand();
1165 refprefix = fl.refPrefix();
1170 name = lexrc.getString();
1174 placement = lexrc.getString();
1178 ext = lexrc.getString();
1182 within = lexrc.getString();
1183 if (within == "none")
1188 style = lexrc.getString();
1190 case FT_LISTCOMMAND:
1192 listcommand = lexrc.getString();
1196 refprefix = lexrc.getString();
1200 listname = lexrc.getString();
1204 usesfloat = lexrc.getBool();
1208 ispredefined = lexrc.getBool();
1212 htmlattr = lexrc.getString();
1216 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1220 htmltag = lexrc.getString();
1230 // Here we have a full float if getout == true
1232 if (!usesfloat && listcommand.empty()) {
1233 // if this float uses the same auxfile as an existing one,
1234 // there is no need for it to provide a list command.
1235 FloatList::const_iterator it = floatlist_.begin();
1236 FloatList::const_iterator en = floatlist_.end();
1237 bool found_ext = false;
1238 for (; it != en; ++it) {
1239 if (it->second.ext() == ext) {
1245 LYXERR0("The layout does not provide a list command " <<
1246 "for the float `" << type << "'. LyX will " <<
1247 "not be able to produce a float list.");
1249 Floating fl(type, placement, ext, within, style, name,
1250 listname, listcommand, refprefix,
1251 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1252 floatlist_.newFloat(fl);
1253 // each float has its own counter
1254 counters_.newCounter(from_ascii(type), from_ascii(within),
1255 docstring(), docstring());
1256 // also define sub-float counters
1257 docstring const subtype = "sub-" + from_ascii(type);
1258 counters_.newCounter(subtype, from_ascii(type),
1259 "\\alph{" + subtype + "}", docstring());
1265 string const & TextClass::prerequisites(string const & sep) const
1267 if (contains(prerequisites_, ',')) {
1268 vector<string> const pres = getVectorFromString(prerequisites_);
1269 prerequisites_ = getStringFromVector(pres, sep);
1271 return prerequisites_;
1275 bool TextClass::hasLayout(docstring const & n) const
1277 docstring const name = n.empty() ? defaultLayoutName() : n;
1279 return find_if(layoutlist_.begin(), layoutlist_.end(),
1280 LayoutNamesEqual(name))
1281 != layoutlist_.end();
1285 bool TextClass::hasInsetLayout(docstring const & n) const
1289 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1290 return it != insetlayoutlist_.end();
1294 Layout const & TextClass::operator[](docstring const & name) const
1296 LATTEST(!name.empty());
1299 find_if(begin(), end(), LayoutNamesEqual(name));
1302 LYXERR0("We failed to find the layout '" << name
1303 << "' in the layout list. You MUST investigate!");
1304 for (const_iterator cit = begin(); cit != end(); ++cit)
1305 lyxerr << " " << to_utf8(cit->name()) << endl;
1307 // We require the name to exist
1308 static const Layout dummy;
1309 LASSERT(false, return dummy);
1316 Layout & TextClass::operator[](docstring const & name)
1318 LATTEST(!name.empty());
1319 // Safe to continue, given what we do below.
1321 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1324 LYXERR0("We failed to find the layout '" << to_utf8(name)
1325 << "' in the layout list. You MUST investigate!");
1326 for (const_iterator cit = begin(); cit != end(); ++cit)
1327 LYXERR0(" " << to_utf8(cit->name()));
1329 // we require the name to exist
1331 // we are here only in release mode
1332 layoutlist_.push_back(createBasicLayout(name, true));
1333 it = find_if(begin(), end(), LayoutNamesEqual(name));
1340 bool TextClass::deleteLayout(docstring const & name)
1342 if (name == defaultLayoutName() || name == plainLayoutName())
1345 LayoutList::iterator it =
1346 remove_if(layoutlist_.begin(), layoutlist_.end(),
1347 LayoutNamesEqual(name));
1349 LayoutList::iterator end = layoutlist_.end();
1350 bool const ret = (it != end);
1351 layoutlist_.erase(it, end);
1356 bool TextClass::deleteInsetLayout(docstring const & name)
1358 return insetlayoutlist_.erase(name);
1362 // Load textclass info if not loaded yet
1363 bool TextClass::load(string const & path) const
1368 // Read style-file, provided path is searched before the system ones
1369 // If path is a file, it is loaded directly.
1370 FileName layout_file(path);
1371 if (!path.empty() && !layout_file.isReadableFile())
1372 layout_file = FileName(addName(path, name_ + ".layout"));
1373 if (layout_file.empty() || !layout_file.exists())
1374 layout_file = libFileSearch("layouts", name_, "layout");
1375 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1378 lyxerr << "Error reading `"
1379 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1380 << "'\n(Check `" << name_
1381 << "')\nCheck your installation and "
1382 "try Options/Reconfigure..."
1390 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1395 layoutlist_.push_back(createBasicLayout(n, true));
1400 string DocumentClass::forcedLayouts() const
1404 const_iterator const e = end();
1405 for (const_iterator i = begin(); i != e; ++i) {
1406 if (i->forcelocal > 0) {
1408 os << "Format " << LAYOUT_FORMAT << '\n';
1418 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1420 // FIXME The fix for the InsetLayout part of 4812 would be here:
1421 // Add the InsetLayout to the document class if it is not found.
1423 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1424 while (!n.empty()) {
1425 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1426 if (cit != cen && cit->first == n)
1428 size_t i = n.find(':');
1429 if (i == string::npos)
1433 return plain_insetlayout_;
1437 docstring const & TextClass::defaultLayoutName() const
1439 return defaultlayout_;
1443 Layout const & TextClass::defaultLayout() const
1445 return operator[](defaultLayoutName());
1449 bool TextClass::isDefaultLayout(Layout const & layout) const
1451 return layout.name() == defaultLayoutName();
1455 bool TextClass::isPlainLayout(Layout const & layout) const
1457 return layout.name() == plainLayoutName();
1461 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1463 static Layout * defaultLayout = NULL;
1465 if (defaultLayout) {
1466 defaultLayout->setUnknown(unknown);
1467 defaultLayout->setName(name);
1468 return *defaultLayout;
1471 static char const * s = "Margin Static\n"
1472 "LatexType Paragraph\n"
1475 "AlignPossible Left, Right, Center\n"
1476 "LabelType No_Label\n"
1478 istringstream ss(s);
1479 Lexer lex(textClassTags);
1481 defaultLayout = new Layout;
1482 defaultLayout->setUnknown(unknown);
1483 defaultLayout->setName(name);
1484 if (!readStyle(lex, *defaultLayout)) {
1485 // The only way this happens is because the hardcoded layout above
1489 return *defaultLayout;
1493 DocumentClassPtr getDocumentClass(
1494 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1497 DocumentClassPtr doc_class =
1498 DocumentClassPtr(new DocumentClass(baseClass));
1499 LayoutModuleList::const_iterator it = modlist.begin();
1500 LayoutModuleList::const_iterator en = modlist.end();
1501 for (; it != en; ++it) {
1502 string const modName = *it;
1503 LyXModule * lm = theModuleList[modName];
1505 docstring const msg =
1506 bformat(_("The module %1$s has been requested by\n"
1507 "this document but has not been found in the list of\n"
1508 "available modules. If you recently installed it, you\n"
1509 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1511 frontend::Alert::warning(_("Module not available"), msg);
1514 if (!lm->isAvailable() && !clone) {
1515 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1516 docstring const msg =
1517 bformat(_("The module %1$s requires a package that is not\n"
1518 "available in your LaTeX installation, or a converter that\n"
1519 "you have not installed. LaTeX output may not be possible.\n"
1520 "Missing prerequisites:\n"
1522 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1523 from_utf8(modName), prereqs);
1524 frontend::Alert::warning(_("Package not available"), msg, true);
1526 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1527 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1528 docstring const msg =
1529 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1530 frontend::Alert::warning(_("Read Error"), msg);
1537 /////////////////////////////////////////////////////////////////////////
1541 /////////////////////////////////////////////////////////////////////////
1543 DocumentClass::DocumentClass(LayoutFile const & tc)
1548 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1550 LayoutList::const_iterator it = layoutlist_.begin();
1551 LayoutList::const_iterator end = layoutlist_.end();
1552 for (; it != end; ++it)
1553 if (it->latexname() == lay)
1559 bool DocumentClass::provides(string const & p) const
1561 return provides_.find(p) != provides_.end();
1565 bool DocumentClass::hasTocLevels() const
1567 return min_toclevel_ != Layout::NOT_IN_TOC;
1571 Layout const & DocumentClass::getTOCLayout() const
1573 // we're going to look for the layout with the minimum toclevel
1574 TextClass::LayoutList::const_iterator lit = begin();
1575 TextClass::LayoutList::const_iterator const len = end();
1576 int minlevel = 1000;
1577 Layout const * lay = NULL;
1578 for (; lit != len; ++lit) {
1579 int const level = lit->toclevel;
1580 // we don't want Part or unnumbered sections
1581 if (level == Layout::NOT_IN_TOC || level < 0
1582 || level >= minlevel || lit->counter.empty())
1589 // hmm. that is very odd, so we'll do our best.
1590 return operator[](defaultLayoutName());
1594 Layout const & DocumentClass::htmlTOCLayout() const
1596 if (html_toc_section_.empty())
1597 html_toc_section_ = getTOCLayout().name();
1598 return operator[](html_toc_section_);
1602 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1603 string const & entry, string const & fallback) const
1605 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1607 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1608 if (itype == cite_formats_.end())
1609 return default_format;
1610 map<string, string>::const_iterator it = itype->second.find(entry);
1611 if (it == itype->second.end() && !fallback.empty())
1612 it = itype->second.find(fallback);
1613 if (it == itype->second.end())
1614 return default_format;
1619 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1620 string const & macro) const
1622 static string empty;
1623 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1624 if (itype == cite_macros_.end())
1626 map<string, string>::const_iterator it = itype->second.find(macro);
1627 if (it == itype->second.end())
1633 vector<string> const DocumentClass::citeCommands(
1634 CiteEngineType const & type) const
1636 vector<CitationStyle> const styles = citeStyles(type);
1637 vector<CitationStyle>::const_iterator it = styles.begin();
1638 vector<CitationStyle>::const_iterator end = styles.end();
1639 vector<string> cmds;
1640 for (; it != end; ++it) {
1641 CitationStyle const cite = *it;
1642 cmds.push_back(cite.cmd);
1648 vector<CitationStyle> const & DocumentClass::citeStyles(
1649 CiteEngineType const & type) const
1651 static vector<CitationStyle> empty;
1652 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1653 if (it == cite_styles_.end())
1659 /////////////////////////////////////////////////////////////////////////
1663 /////////////////////////////////////////////////////////////////////////
1665 ostream & operator<<(ostream & os, PageSides p)