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 = 52; //spitz: add ForceOwnlines tag
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("Plain Layout");
132 InsetLayout DocumentClass::plain_insetlayout_;
135 /////////////////////////////////////////////////////////////////////////
139 /////////////////////////////////////////////////////////////////////////
141 TextClass::TextClass()
144 outputFormat_ = "latex";
149 pagestyle_ = "default";
150 defaultfont_ = sane_font;
151 opt_enginetype_ = "authoryear|numerical";
152 opt_fontsize_ = "10|11|12";
153 opt_pagestyle_ = "empty|plain|headings|fancy";
154 cite_full_author_list_ = true;
155 titletype_ = TITLE_COMMAND_AFTER;
156 titlename_ = "maketitle";
158 _("Plain Layout"); // a hack to make this translatable
162 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
164 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
165 if (!lay.read(lexrc, *this)) {
166 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
170 lay.resfont = lay.font;
171 lay.resfont.realize(defaultfont_);
172 lay.reslabelfont = lay.labelfont;
173 lay.reslabelfont.realize(defaultfont_);
174 return true; // no errors
212 TC_ADDTOHTMLPREAMBLE,
228 LexerKeyword textClassTags[] = {
229 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
230 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
231 { "addtopreamble", TC_ADDTOPREAMBLE },
232 { "citeengine", TC_CITEENGINE },
233 { "citeenginetype", TC_CITEENGINETYPE },
234 { "citeformat", TC_CITEFORMAT },
235 { "classoptions", TC_CLASSOPTIONS },
236 { "columns", TC_COLUMNS },
237 { "counter", TC_COUNTER },
238 { "defaultbiblio", TC_DEFAULTBIBLIO },
239 { "defaultfont", TC_DEFAULTFONT },
240 { "defaultmodule", TC_DEFAULTMODULE },
241 { "defaultstyle", TC_DEFAULTSTYLE },
242 { "excludesmodule", TC_EXCLUDESMODULE },
243 { "float", TC_FLOAT },
244 { "format", TC_FORMAT },
245 { "fullauthorlist", TC_FULLAUTHORLIST },
246 { "htmlpreamble", TC_HTMLPREAMBLE },
247 { "htmlstyles", TC_HTMLSTYLES },
248 { "htmltocsection", TC_HTMLTOCSECTION },
249 { "ifcounter", TC_IFCOUNTER },
250 { "ifstyle", TC_IFSTYLE },
251 { "input", TC_INPUT },
252 { "insetlayout", TC_INSETLAYOUT },
253 { "leftmargin", TC_LEFTMARGIN },
254 { "nocounter", TC_NOCOUNTER },
255 { "nofloat", TC_NOFLOAT },
256 { "noinsetlayout", TC_NOINSETLAYOUT },
257 { "nostyle", TC_NOSTYLE },
258 { "outputformat", TC_OUTPUTFORMAT },
259 { "outputtype", TC_OUTPUTTYPE },
260 { "packageoptions", TC_PKGOPTS },
261 { "pagestyle", TC_PAGESTYLE },
262 { "preamble", TC_PREAMBLE },
263 { "provides", TC_PROVIDES },
264 { "providesmodule", TC_PROVIDESMODULE },
265 { "requires", TC_REQUIRES },
266 { "rightmargin", TC_RIGHTMARGIN },
267 { "secnumdepth", TC_SECNUMDEPTH },
268 { "sides", TC_SIDES },
269 { "style", TC_STYLE },
270 { "titlelatexname", TC_TITLELATEXNAME },
271 { "titlelatextype", TC_TITLELATEXTYPE },
272 { "tocdepth", TC_TOCDEPTH }
278 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
280 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
281 TempFile tmp("convertXXXXXX.layout");
282 FileName const tempfile = tmp.name();
283 bool success = layout2layout(filename, tempfile);
285 success = readWithoutConv(tempfile, rt) == OK;
290 std::string TextClass::convert(std::string const & str)
292 TempFile tmp1("localXXXXXX.layout");
293 FileName const fn = tmp1.name();
294 ofstream os(fn.toFilesystemEncoding().c_str());
297 TempFile tmp2("convert_localXXXXXX.layout");
298 FileName const tempfile = tmp2.name();
299 bool success = layout2layout(fn, tempfile);
302 ifstream is(tempfile.toFilesystemEncoding().c_str());
314 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
316 if (!filename.isReadableFile()) {
317 lyxerr << "Cannot read layout file `" << filename << "'."
322 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
323 to_utf8(makeDisplayPath(filename.absFileName())));
325 // Define the plain layout used in table cells, ert, etc. Note that
326 // we do this before loading any layout file, so that classes can
327 // override features of this layout if they should choose to do so.
328 if (rt == BASECLASS && !hasLayout(plain_layout_))
329 layoutlist_.push_back(createBasicLayout(plain_layout_));
331 Lexer lexrc(textClassTags);
332 lexrc.setFile(filename);
333 ReturnValues retval = read(lexrc, rt);
335 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
336 to_utf8(makeDisplayPath(filename.absFileName())));
342 bool TextClass::read(FileName const & filename, ReadType rt)
344 ReturnValues const retval = readWithoutConv(filename, rt);
345 if (retval != FORMAT_MISMATCH)
348 bool const worx = convertLayoutFormat(filename, rt);
350 LYXERR0 ("Unable to convert " << filename <<
351 " to format " << LAYOUT_FORMAT);
356 TextClass::ReturnValues TextClass::validate(std::string const & str)
359 return tc.read(str, VALIDATION);
363 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
365 Lexer lexrc(textClassTags);
366 istringstream is(str);
368 ReturnValues retval = read(lexrc, rt);
370 if (retval != FORMAT_MISMATCH)
373 // write the layout string to a temporary file
374 TempFile tmp("TextClass_read");
375 FileName const tempfile = tmp.name();
376 ofstream os(tempfile.toFilesystemEncoding().c_str());
378 LYXERR0("Unable to create temporary file");
384 // now try to convert it
385 bool const worx = convertLayoutFormat(tempfile, rt);
387 LYXERR0("Unable to convert internal layout information to format "
395 // Reads a textclass structure from file.
396 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
401 // Format of files before the 'Format' tag was introduced
406 while (lexrc.isOK() && !error) {
407 int le = lexrc.lex();
410 case Lexer::LEX_FEOF:
413 case Lexer::LEX_UNDEF:
414 lexrc.printError("Unknown TextClass tag `$$Token'");
422 // used below to track whether we are in an IfStyle or IfCounter tag.
423 bool ifstyle = false;
424 bool ifcounter = false;
426 switch (static_cast<TextClassTags>(le)) {
430 format = lexrc.getInteger();
433 case TC_OUTPUTFORMAT:
435 outputFormat_ = lexrc.getString();
439 readOutputType(lexrc);
440 switch(outputType_) {
442 outputFormat_ = "latex";
445 outputFormat_ = "docbook";
448 outputFormat_ = "literate";
453 case TC_INPUT: // Include file
455 string const inc = lexrc.getString();
456 FileName tmp = libFileSearch("layouts", inc,
460 lexrc.printError("Could not find input file: " + inc);
462 } else if (!read(tmp, MERGE)) {
463 lexrc.printError("Error reading input file: " + tmp.absFileName());
469 case TC_DEFAULTSTYLE:
471 docstring const name = from_utf8(subst(lexrc.getString(),
473 defaultlayout_ = name;
482 lexrc.printError("No name given for style: `$$Token'.");
486 docstring const name = from_utf8(subst(lexrc.getString(),
489 string s = "Could not read name for style: `$$Token' "
490 + lexrc.getString() + " is probably not valid UTF-8!";
493 // Since we couldn't read the name, we just scan the rest
494 // of the style and discard it.
495 error = !readStyle(lexrc, lay);
496 } else if (hasLayout(name)) {
497 Layout & lay = operator[](name);
498 error = !readStyle(lexrc, lay);
499 } else if (!ifstyle) {
501 layout.setName(name);
502 error = !readStyle(lexrc, layout);
504 layoutlist_.push_back(layout);
506 if (defaultlayout_.empty()) {
507 // We do not have a default layout yet, so we choose
508 // the first layout we encounter.
509 defaultlayout_ = name;
513 // this was an ifstyle where we didn't have the style
514 // scan the rest and discard it
516 readStyle(lexrc, lay);
526 docstring const style = from_utf8(subst(lexrc.getString(),
528 if (!deleteLayout(style))
529 lyxerr << "Cannot delete style `"
530 << to_utf8(style) << '\'' << endl;
534 case TC_NOINSETLAYOUT:
536 docstring const style = from_utf8(subst(lexrc.getString(),
538 if (!deleteInsetLayout(style))
539 LYXERR0("Style `" << style << "' cannot be removed\n"
540 "because it was not found!");
546 columns_ = lexrc.getInteger();
551 switch (lexrc.getInteger()) {
552 case 1: sides_ = OneSide; break;
553 case 2: sides_ = TwoSides; break;
555 lyxerr << "Impossible number of page"
556 " sides, setting to one."
566 pagestyle_ = rtrim(lexrc.getString());
570 defaultfont_ = lyxRead(lexrc);
571 if (!defaultfont_.resolved()) {
572 lexrc.printError("Warning: defaultfont should "
573 "be fully instantiated!");
574 defaultfont_.realize(sane_font);
580 secnumdepth_ = lexrc.getInteger();
585 tocdepth_ = lexrc.getInteger();
588 // First step to support options
589 case TC_CLASSOPTIONS:
590 readClassOptions(lexrc);
594 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
597 case TC_HTMLPREAMBLE:
598 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
602 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
605 case TC_HTMLTOCSECTION:
606 html_toc_section_ = from_utf8(trim(lexrc.getString()));
609 case TC_ADDTOPREAMBLE:
610 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
613 case TC_ADDTOHTMLPREAMBLE:
614 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
617 case TC_ADDTOHTMLSTYLES:
618 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
623 string const feature = lexrc.getString();
625 if (lexrc.getInteger())
626 provides_.insert(feature);
628 provides_.erase(feature);
634 vector<string> const req
635 = getVectorFromString(lexrc.getString());
636 requires_.insert(req.begin(), req.end());
642 string const pkg = lexrc.getString();
644 string const options = lexrc.getString();
645 package_options_[pkg] = options;
649 case TC_DEFAULTMODULE: {
651 string const module = lexrc.getString();
652 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
653 default_modules_.push_back(module);
657 case TC_PROVIDESMODULE: {
659 string const module = lexrc.getString();
660 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
661 provided_modules_.push_back(module);
665 case TC_EXCLUDESMODULE: {
667 string const module = lexrc.getString();
668 // modules already have their own way to exclude other modules
670 LYXERR0("ExcludesModule tag cannot be used in a module!");
673 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
674 excluded_modules_.push_back(module);
678 case TC_LEFTMARGIN: // left margin type
680 leftmargin_ = lexrc.getDocString();
683 case TC_RIGHTMARGIN: // right margin type
685 rightmargin_ = lexrc.getDocString();
688 case TC_INSETLAYOUT: {
690 lexrc.printError("No name given for InsetLayout: `$$Token'.");
694 docstring const name = subst(lexrc.getDocString(), '_', ' ');
696 string s = "Could not read name for InsetLayout: `$$Token' "
697 + lexrc.getString() + " is probably not valid UTF-8!";
700 // Since we couldn't read the name, we just scan the rest
701 // of the style and discard it.
702 il.read(lexrc, *this);
703 // Let's try to continue rather than abort.
705 } else if (hasInsetLayout(name)) {
706 InsetLayout & il = insetlayoutlist_[name];
707 error = !il.read(lexrc, *this);
711 error = !il.read(lexrc, *this);
713 insetlayoutlist_[name] = il;
719 error = !readFloat(lexrc);
723 error = !readCiteEngine(lexrc);
726 case TC_CITEENGINETYPE:
728 opt_enginetype_ = rtrim(lexrc.getString());
732 error = !readCiteFormat(lexrc);
735 case TC_DEFAULTBIBLIO:
737 cite_default_biblio_style_ = rtrim(lexrc.getString());
740 case TC_FULLAUTHORLIST:
742 cite_full_author_list_ &= lexrc.getBool();
747 docstring const cnt = lexrc.getDocString();
748 if (!counters_.remove(cnt))
749 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
757 docstring const name = lexrc.getDocString();
759 string s = "Could not read name for counter: `$$Token' "
760 + lexrc.getString() + " is probably not valid UTF-8!";
761 lexrc.printError(s.c_str());
763 // Since we couldn't read the name, we just scan the rest
767 error = !counters_.read(lexrc, name, !ifcounter);
770 lexrc.printError("No name given for style: `$$Token'.");
777 case TC_TITLELATEXTYPE:
778 readTitleType(lexrc);
781 case TC_TITLELATEXNAME:
783 titlename_ = lexrc.getString();
788 string const nofloat = lexrc.getString();
789 floatlist_.erase(nofloat);
794 // Note that this is triggered the first time through the loop unless
795 // we hit a format tag.
796 if (format != LAYOUT_FORMAT)
797 return FORMAT_MISMATCH;
800 // at present, we abort if we encounter an error,
801 // so there is no point continuing.
806 return (error ? ERROR : OK);
808 if (defaultlayout_.empty()) {
809 LYXERR0("Error: Textclass '" << name_
810 << "' is missing a defaultstyle.");
814 // Try to erase "stdinsets" from the provides_ set.
816 // Provides stdinsets 1
817 // declaration simply tells us that the standard insets have been
818 // defined. (It's found in stdinsets.inc but could also be used in
819 // user-defined files.) There isn't really any such package. So we
820 // might as well go ahead and erase it.
821 // If we do not succeed, then it was not there, which means that
822 // the textclass did not provide the definitions of the standard
823 // insets. So we need to try to load them.
824 int erased = provides_.erase("stdinsets");
826 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
829 frontend::Alert::warning(_("Missing File"),
830 _("Could not find stdinsets.inc! This may lead to data loss!"));
832 } else if (!read(tmp, MERGE)) {
833 frontend::Alert::warning(_("Corrupt File"),
834 _("Could not read stdinsets.inc! This may lead to data loss!"));
839 min_toclevel_ = Layout::NOT_IN_TOC;
840 max_toclevel_ = Layout::NOT_IN_TOC;
841 const_iterator lit = begin();
842 const_iterator len = end();
843 for (; lit != len; ++lit) {
844 int const toclevel = lit->toclevel;
845 if (toclevel != Layout::NOT_IN_TOC) {
846 if (min_toclevel_ == Layout::NOT_IN_TOC)
847 min_toclevel_ = toclevel;
849 min_toclevel_ = min(min_toclevel_, toclevel);
850 max_toclevel_ = max(max_toclevel_, toclevel);
853 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
854 << ", maximum is " << max_toclevel_);
856 return (error ? ERROR : OK);
860 void TextClass::readTitleType(Lexer & lexrc)
862 LexerKeyword titleTypeTags[] = {
863 { "commandafter", TITLE_COMMAND_AFTER },
864 { "environment", TITLE_ENVIRONMENT }
867 PushPopHelper pph(lexrc, titleTypeTags);
869 int le = lexrc.lex();
871 case Lexer::LEX_UNDEF:
872 lexrc.printError("Unknown output type `$$Token'");
874 case TITLE_COMMAND_AFTER:
875 case TITLE_ENVIRONMENT:
876 titletype_ = static_cast<TitleLatexType>(le);
879 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
885 void TextClass::readOutputType(Lexer & lexrc)
887 LexerKeyword outputTypeTags[] = {
888 { "docbook", DOCBOOK },
890 { "literate", LITERATE }
893 PushPopHelper pph(lexrc, outputTypeTags);
895 int le = lexrc.lex();
897 case Lexer::LEX_UNDEF:
898 lexrc.printError("Unknown output type `$$Token'");
903 outputType_ = static_cast<OutputType>(le);
906 LYXERR0("Unhandled value " << le);
912 void TextClass::readClassOptions(Lexer & lexrc)
922 LexerKeyword classOptionsTags[] = {
924 {"fontsize", CO_FONTSIZE },
925 {"header", CO_HEADER },
926 {"other", CO_OTHER },
927 {"pagestyle", CO_PAGESTYLE }
930 lexrc.pushTable(classOptionsTags);
932 while (!getout && lexrc.isOK()) {
933 int le = lexrc.lex();
935 case Lexer::LEX_UNDEF:
936 lexrc.printError("Unknown ClassOption tag `$$Token'");
944 opt_fontsize_ = rtrim(lexrc.getString());
948 opt_pagestyle_ = rtrim(lexrc.getString());
952 if (options_.empty())
953 options_ = lexrc.getString();
955 options_ += ',' + lexrc.getString();
959 class_header_ = subst(lexrc.getString(), """, "\"");
970 bool TextClass::readCiteEngine(Lexer & lexrc)
972 int const type = readCiteEngineType(lexrc);
973 if (type & ENGINE_TYPE_AUTHORYEAR)
974 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
975 if (type & ENGINE_TYPE_NUMERICAL)
976 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
977 if (type & ENGINE_TYPE_DEFAULT)
978 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
981 while (!getout && lexrc.isOK()) {
983 def = lexrc.getString();
984 def = subst(def, " ", "");
985 def = subst(def, "\t", "");
986 if (compare_ascii_no_case(def, "end") == 0) {
996 cs.forceUpperCase = true;
1000 size_t const n = def.size();
1001 for (size_t i = 0; i != n; ++i) {
1004 cs.fullAuthorList = true;
1005 else if (ichar == '[' && cs.textAfter)
1006 cs.textBefore = true;
1007 else if (ichar == '[')
1008 cs.textAfter = true;
1009 else if (ichar != ']')
1014 if (type & ENGINE_TYPE_AUTHORYEAR)
1015 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1016 if (type & ENGINE_TYPE_NUMERICAL)
1017 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1018 if (type & ENGINE_TYPE_DEFAULT)
1019 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1025 int TextClass::readCiteEngineType(Lexer & lexrc) const
1027 LATTEST(ENGINE_TYPE_DEFAULT ==
1028 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1029 if (!lexrc.next()) {
1030 lexrc.printError("No cite engine type given for token: `$$Token'.");
1031 return ENGINE_TYPE_DEFAULT;
1033 string const type = rtrim(lexrc.getString());
1034 if (compare_ascii_no_case(type, "authoryear") == 0)
1035 return ENGINE_TYPE_AUTHORYEAR;
1036 else if (compare_ascii_no_case(type, "numerical") == 0)
1037 return ENGINE_TYPE_NUMERICAL;
1038 else if (compare_ascii_no_case(type, "default") != 0) {
1039 string const s = "Unknown cite engine type `" + type
1040 + "' given for token: `$$Token',";
1041 lexrc.printError(s);
1043 return ENGINE_TYPE_DEFAULT;
1047 bool TextClass::readCiteFormat(Lexer & lexrc)
1049 int const type = readCiteEngineType(lexrc);
1052 while (lexrc.isOK()) {
1054 etype = lexrc.getString();
1055 if (compare_ascii_no_case(etype, "end") == 0)
1060 definition = lexrc.getString();
1061 char initchar = etype[0];
1062 if (initchar == '#')
1064 if (initchar == '!' || initchar == '_') {
1065 if (type & ENGINE_TYPE_AUTHORYEAR)
1066 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1067 if (type & ENGINE_TYPE_NUMERICAL)
1068 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1069 if (type & ENGINE_TYPE_DEFAULT)
1070 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1072 if (type & ENGINE_TYPE_AUTHORYEAR)
1073 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1074 if (type & ENGINE_TYPE_NUMERICAL)
1075 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1076 if (type & ENGINE_TYPE_DEFAULT)
1077 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1084 bool TextClass::readFloat(Lexer & lexrc)
1104 LexerKeyword floatTags[] = {
1106 { "extension", FT_EXT },
1107 { "guiname", FT_NAME },
1108 { "htmlattr", FT_HTMLATTR },
1109 { "htmlstyle", FT_HTMLSTYLE },
1110 { "htmltag", FT_HTMLTAG },
1111 { "ispredefined", FT_PREDEFINED },
1112 { "listcommand", FT_LISTCOMMAND },
1113 { "listname", FT_LISTNAME },
1114 { "numberwithin", FT_WITHIN },
1115 { "placement", FT_PLACEMENT },
1116 { "refprefix", FT_REFPREFIX },
1117 { "style", FT_STYLE },
1118 { "type", FT_TYPE },
1119 { "usesfloatpkg", FT_USESFLOAT }
1122 lexrc.pushTable(floatTags);
1136 bool usesfloat = true;
1137 bool ispredefined = false;
1139 bool getout = false;
1140 while (!getout && lexrc.isOK()) {
1141 int le = lexrc.lex();
1143 case Lexer::LEX_UNDEF:
1144 lexrc.printError("Unknown float tag `$$Token'");
1152 type = lexrc.getString();
1153 if (floatlist_.typeExist(type)) {
1154 Floating const & fl = floatlist_.getType(type);
1155 placement = fl.placement();
1157 within = fl.within();
1160 listname = fl.listName();
1161 usesfloat = fl.usesFloatPkg();
1162 ispredefined = fl.isPredefined();
1163 listcommand = fl.listCommand();
1164 refprefix = fl.refPrefix();
1169 name = lexrc.getString();
1173 placement = lexrc.getString();
1177 ext = lexrc.getString();
1181 within = lexrc.getString();
1182 if (within == "none")
1187 style = lexrc.getString();
1189 case FT_LISTCOMMAND:
1191 listcommand = lexrc.getString();
1195 refprefix = lexrc.getString();
1199 listname = lexrc.getString();
1203 usesfloat = lexrc.getBool();
1207 ispredefined = lexrc.getBool();
1211 htmlattr = lexrc.getString();
1215 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1219 htmltag = lexrc.getString();
1229 // Here we have a full float if getout == true
1231 if (!usesfloat && listcommand.empty()) {
1232 // if this float uses the same auxfile as an existing one,
1233 // there is no need for it to provide a list command.
1234 FloatList::const_iterator it = floatlist_.begin();
1235 FloatList::const_iterator en = floatlist_.end();
1236 bool found_ext = false;
1237 for (; it != en; ++it) {
1238 if (it->second.ext() == ext) {
1244 LYXERR0("The layout does not provide a list command " <<
1245 "for the float `" << type << "'. LyX will " <<
1246 "not be able to produce a float list.");
1248 Floating fl(type, placement, ext, within, style, name,
1249 listname, listcommand, refprefix,
1250 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1251 floatlist_.newFloat(fl);
1252 // each float has its own counter
1253 counters_.newCounter(from_ascii(type), from_ascii(within),
1254 docstring(), docstring());
1255 // also define sub-float counters
1256 docstring const subtype = "sub-" + from_ascii(type);
1257 counters_.newCounter(subtype, from_ascii(type),
1258 "\\alph{" + subtype + "}", docstring());
1264 string const & TextClass::prerequisites(string const & sep) const
1266 if (contains(prerequisites_, ',')) {
1267 vector<string> const pres = getVectorFromString(prerequisites_);
1268 prerequisites_ = getStringFromVector(pres, sep);
1270 return prerequisites_;
1274 bool TextClass::hasLayout(docstring const & n) const
1276 docstring const name = n.empty() ? defaultLayoutName() : n;
1278 return find_if(layoutlist_.begin(), layoutlist_.end(),
1279 LayoutNamesEqual(name))
1280 != layoutlist_.end();
1284 bool TextClass::hasInsetLayout(docstring const & n) const
1288 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1289 return it != insetlayoutlist_.end();
1293 Layout const & TextClass::operator[](docstring const & name) const
1295 LATTEST(!name.empty());
1298 find_if(begin(), end(), LayoutNamesEqual(name));
1301 LYXERR0("We failed to find the layout '" << name
1302 << "' in the layout list. You MUST investigate!");
1303 for (const_iterator cit = begin(); cit != end(); ++cit)
1304 lyxerr << " " << to_utf8(cit->name()) << endl;
1306 // We require the name to exist
1307 static const Layout dummy;
1308 LASSERT(false, return dummy);
1315 Layout & TextClass::operator[](docstring const & name)
1317 LATTEST(!name.empty());
1318 // Safe to continue, given what we do below.
1320 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1323 LYXERR0("We failed to find the layout '" << to_utf8(name)
1324 << "' in the layout list. You MUST investigate!");
1325 for (const_iterator cit = begin(); cit != end(); ++cit)
1326 LYXERR0(" " << to_utf8(cit->name()));
1328 // we require the name to exist
1330 // we are here only in release mode
1331 layoutlist_.push_back(createBasicLayout(name, true));
1332 it = find_if(begin(), end(), LayoutNamesEqual(name));
1339 bool TextClass::deleteLayout(docstring const & name)
1341 if (name == defaultLayoutName() || name == plainLayoutName())
1344 LayoutList::iterator it =
1345 remove_if(layoutlist_.begin(), layoutlist_.end(),
1346 LayoutNamesEqual(name));
1348 LayoutList::iterator end = layoutlist_.end();
1349 bool const ret = (it != end);
1350 layoutlist_.erase(it, end);
1355 bool TextClass::deleteInsetLayout(docstring const & name)
1357 return insetlayoutlist_.erase(name);
1361 // Load textclass info if not loaded yet
1362 bool TextClass::load(string const & path) const
1367 // Read style-file, provided path is searched before the system ones
1368 // If path is a file, it is loaded directly.
1369 FileName layout_file(path);
1370 if (!path.empty() && !layout_file.isReadableFile())
1371 layout_file = FileName(addName(path, name_ + ".layout"));
1372 if (layout_file.empty() || !layout_file.exists())
1373 layout_file = libFileSearch("layouts", name_, "layout");
1374 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1377 lyxerr << "Error reading `"
1378 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1379 << "'\n(Check `" << name_
1380 << "')\nCheck your installation and "
1381 "try Options/Reconfigure..."
1389 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1394 layoutlist_.push_back(createBasicLayout(n, true));
1399 string DocumentClass::forcedLayouts() const
1403 const_iterator const e = end();
1404 for (const_iterator i = begin(); i != e; ++i) {
1405 if (i->forcelocal > 0) {
1407 os << "Format " << LAYOUT_FORMAT << '\n';
1417 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1419 // FIXME The fix for the InsetLayout part of 4812 would be here:
1420 // Add the InsetLayout to the document class if it is not found.
1422 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1423 while (!n.empty()) {
1424 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1425 if (cit != cen && cit->first == n)
1427 size_t i = n.find(':');
1428 if (i == string::npos)
1432 return plain_insetlayout_;
1436 docstring const & TextClass::defaultLayoutName() const
1438 return defaultlayout_;
1442 Layout const & TextClass::defaultLayout() const
1444 return operator[](defaultLayoutName());
1448 bool TextClass::isDefaultLayout(Layout const & layout) const
1450 return layout.name() == defaultLayoutName();
1454 bool TextClass::isPlainLayout(Layout const & layout) const
1456 return layout.name() == plainLayoutName();
1460 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1462 static Layout * defaultLayout = NULL;
1464 if (defaultLayout) {
1465 defaultLayout->setUnknown(unknown);
1466 defaultLayout->setName(name);
1467 return *defaultLayout;
1470 static char const * s = "Margin Static\n"
1471 "LatexType Paragraph\n"
1474 "AlignPossible Left, Right, Center\n"
1475 "LabelType No_Label\n"
1477 istringstream ss(s);
1478 Lexer lex(textClassTags);
1480 defaultLayout = new Layout;
1481 defaultLayout->setUnknown(unknown);
1482 defaultLayout->setName(name);
1483 if (!readStyle(lex, *defaultLayout)) {
1484 // The only way this happens is because the hardcoded layout above
1488 return *defaultLayout;
1492 DocumentClassPtr getDocumentClass(
1493 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1496 DocumentClassPtr doc_class =
1497 DocumentClassPtr(new DocumentClass(baseClass));
1498 LayoutModuleList::const_iterator it = modlist.begin();
1499 LayoutModuleList::const_iterator en = modlist.end();
1500 for (; it != en; ++it) {
1501 string const modName = *it;
1502 LyXModule * lm = theModuleList[modName];
1504 docstring const msg =
1505 bformat(_("The module %1$s has been requested by\n"
1506 "this document but has not been found in the list of\n"
1507 "available modules. If you recently installed it, you\n"
1508 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1510 frontend::Alert::warning(_("Module not available"), msg);
1513 if (!lm->isAvailable() && !clone) {
1514 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1515 docstring const msg =
1516 bformat(_("The module %1$s requires a package that is not\n"
1517 "available in your LaTeX installation, or a converter that\n"
1518 "you have not installed. LaTeX output may not be possible.\n"
1519 "Missing prerequisites:\n"
1521 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1522 from_utf8(modName), prereqs);
1523 frontend::Alert::warning(_("Package not available"), msg, true);
1525 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1526 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1527 docstring const msg =
1528 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1529 frontend::Alert::warning(_("Read Error"), msg);
1536 /////////////////////////////////////////////////////////////////////////
1540 /////////////////////////////////////////////////////////////////////////
1542 DocumentClass::DocumentClass(LayoutFile const & tc)
1547 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1549 LayoutList::const_iterator it = layoutlist_.begin();
1550 LayoutList::const_iterator end = layoutlist_.end();
1551 for (; it != end; ++it)
1552 if (it->latexname() == lay)
1558 bool DocumentClass::provides(string const & p) const
1560 return provides_.find(p) != provides_.end();
1564 bool DocumentClass::hasTocLevels() const
1566 return min_toclevel_ != Layout::NOT_IN_TOC;
1570 Layout const & DocumentClass::getTOCLayout() const
1572 // we're going to look for the layout with the minimum toclevel
1573 TextClass::LayoutList::const_iterator lit = begin();
1574 TextClass::LayoutList::const_iterator const len = end();
1575 int minlevel = 1000;
1576 Layout const * lay = NULL;
1577 for (; lit != len; ++lit) {
1578 int const level = lit->toclevel;
1579 // we don't want Part or unnumbered sections
1580 if (level == Layout::NOT_IN_TOC || level < 0
1581 || level >= minlevel || lit->counter.empty())
1588 // hmm. that is very odd, so we'll do our best.
1589 return operator[](defaultLayoutName());
1593 Layout const & DocumentClass::htmlTOCLayout() const
1595 if (html_toc_section_.empty())
1596 html_toc_section_ = getTOCLayout().name();
1597 return operator[](html_toc_section_);
1601 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1602 string const & entry, string const & fallback) const
1604 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1606 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1607 if (itype == cite_formats_.end())
1608 return default_format;
1609 map<string, string>::const_iterator it = itype->second.find(entry);
1610 if (it == itype->second.end() && !fallback.empty())
1611 it = itype->second.find(fallback);
1612 if (it == itype->second.end())
1613 return default_format;
1618 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1619 string const & macro) const
1621 static string empty;
1622 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1623 if (itype == cite_macros_.end())
1625 map<string, string>::const_iterator it = itype->second.find(macro);
1626 if (it == itype->second.end())
1632 vector<string> const DocumentClass::citeCommands(
1633 CiteEngineType const & type) const
1635 vector<CitationStyle> const styles = citeStyles(type);
1636 vector<CitationStyle>::const_iterator it = styles.begin();
1637 vector<CitationStyle>::const_iterator end = styles.end();
1638 vector<string> cmds;
1639 for (; it != end; ++it) {
1640 CitationStyle const cite = *it;
1641 cmds.push_back(cite.cmd);
1647 vector<CitationStyle> const & DocumentClass::citeStyles(
1648 CiteEngineType const & type) const
1650 static vector<CitationStyle> empty;
1651 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1652 if (it == cite_styles_.end())
1658 /////////////////////////////////////////////////////////////////////////
1662 /////////////////////////////////////////////////////////////////////////
1664 ostream & operator<<(ostream & os, PageSides p)