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);
519 docstring const style = from_utf8(subst(lexrc.getString(),
521 if (!deleteLayout(style))
522 lyxerr << "Cannot delete style `"
523 << to_utf8(style) << '\'' << endl;
527 case TC_NOINSETLAYOUT:
529 docstring const style = from_utf8(subst(lexrc.getString(),
531 if (!deleteInsetLayout(style))
532 LYXERR0("Style `" << style << "' cannot be removed\n"
533 "because it was not found!");
539 columns_ = lexrc.getInteger();
544 switch (lexrc.getInteger()) {
545 case 1: sides_ = OneSide; break;
546 case 2: sides_ = TwoSides; break;
548 lyxerr << "Impossible number of page"
549 " sides, setting to one."
559 pagestyle_ = rtrim(lexrc.getString());
563 defaultfont_ = lyxRead(lexrc);
564 if (!defaultfont_.resolved()) {
565 lexrc.printError("Warning: defaultfont should "
566 "be fully instantiated!");
567 defaultfont_.realize(sane_font);
573 secnumdepth_ = lexrc.getInteger();
578 tocdepth_ = lexrc.getInteger();
581 // First step to support options
582 case TC_CLASSOPTIONS:
583 readClassOptions(lexrc);
587 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
590 case TC_HTMLPREAMBLE:
591 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
595 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
598 case TC_HTMLTOCSECTION:
599 html_toc_section_ = from_utf8(trim(lexrc.getString()));
602 case TC_ADDTOPREAMBLE:
603 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
606 case TC_ADDTOHTMLPREAMBLE:
607 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
610 case TC_ADDTOHTMLSTYLES:
611 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
616 string const feature = lexrc.getString();
618 if (lexrc.getInteger())
619 provides_.insert(feature);
621 provides_.erase(feature);
627 vector<string> const req
628 = getVectorFromString(lexrc.getString());
629 requires_.insert(req.begin(), req.end());
635 string const pkg = lexrc.getString();
637 string const options = lexrc.getString();
638 package_options_[pkg] = options;
642 case TC_DEFAULTMODULE: {
644 string const module = lexrc.getString();
645 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
646 default_modules_.push_back(module);
650 case TC_PROVIDESMODULE: {
652 string const module = lexrc.getString();
653 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
654 provided_modules_.push_back(module);
658 case TC_EXCLUDESMODULE: {
660 string const module = lexrc.getString();
661 // modules already have their own way to exclude other modules
663 LYXERR0("ExcludesModule tag cannot be used in a module!");
666 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
667 excluded_modules_.push_back(module);
671 case TC_LEFTMARGIN: // left margin type
673 leftmargin_ = lexrc.getDocString();
676 case TC_RIGHTMARGIN: // right margin type
678 rightmargin_ = lexrc.getDocString();
681 case TC_INSETLAYOUT: {
683 lexrc.printError("No name given for InsetLayout: `$$Token'.");
687 docstring const name = subst(lexrc.getDocString(), '_', ' ');
689 string s = "Could not read name for InsetLayout: `$$Token' "
690 + lexrc.getString() + " is probably not valid UTF-8!";
693 // Since we couldn't read the name, we just scan the rest
694 // of the style and discard it.
695 il.read(lexrc, *this);
696 // Let's try to continue rather than abort.
698 } else if (hasInsetLayout(name)) {
699 InsetLayout & il = insetlayoutlist_[name];
700 error = !il.read(lexrc, *this);
704 error = !il.read(lexrc, *this);
706 insetlayoutlist_[name] = il;
712 error = !readFloat(lexrc);
716 error = !readCiteEngine(lexrc);
719 case TC_CITEENGINETYPE:
721 opt_enginetype_ = rtrim(lexrc.getString());
725 error = !readCiteFormat(lexrc);
728 case TC_DEFAULTBIBLIO:
730 cite_default_biblio_style_ = rtrim(lexrc.getString());
733 case TC_FULLAUTHORLIST:
735 cite_full_author_list_ &= lexrc.getBool();
740 docstring const cnt = lexrc.getDocString();
741 if (!counters_.remove(cnt))
742 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
751 docstring const name = lexrc.getDocString();
753 string s = "Could not read name for counter: `$$Token' "
754 + lexrc.getString() + " is probably not valid UTF-8!";
755 lexrc.printError(s.c_str());
757 // Since we couldn't read the name, we just scan the rest
761 error = !counters_.read(lexrc, name, !ifcounter);
764 lexrc.printError("No name given for style: `$$Token'.");
771 case TC_TITLELATEXTYPE:
772 readTitleType(lexrc);
775 case TC_TITLELATEXNAME:
777 titlename_ = lexrc.getString();
782 string const nofloat = lexrc.getString();
783 floatlist_.erase(nofloat);
788 // Note that this is triggered the first time through the loop unless
789 // we hit a format tag.
790 if (format != LAYOUT_FORMAT)
791 return FORMAT_MISMATCH;
794 // at present, we abort if we encounter an error,
795 // so there is no point continuing.
802 if (defaultlayout_.empty()) {
803 LYXERR0("Error: Textclass '" << name_
804 << "' is missing a defaultstyle.");
808 // Try to erase "stdinsets" from the provides_ set.
810 // Provides stdinsets 1
811 // declaration simply tells us that the standard insets have been
812 // defined. (It's found in stdinsets.inc but could also be used in
813 // user-defined files.) There isn't really any such package. So we
814 // might as well go ahead and erase it.
815 // If we do not succeed, then it was not there, which means that
816 // the textclass did not provide the definitions of the standard
817 // insets. So we need to try to load them.
818 int erased = provides_.erase("stdinsets");
820 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
823 frontend::Alert::warning(_("Missing File"),
824 _("Could not find stdinsets.inc! This may lead to data loss!"));
826 } else if (!read(tmp, MERGE)) {
827 frontend::Alert::warning(_("Corrupt File"),
828 _("Could not read stdinsets.inc! This may lead to data loss!"));
833 min_toclevel_ = Layout::NOT_IN_TOC;
834 max_toclevel_ = Layout::NOT_IN_TOC;
835 const_iterator lit = begin();
836 const_iterator len = end();
837 for (; lit != len; ++lit) {
838 int const toclevel = lit->toclevel;
839 if (toclevel != Layout::NOT_IN_TOC) {
840 if (min_toclevel_ == Layout::NOT_IN_TOC)
841 min_toclevel_ = toclevel;
843 min_toclevel_ = min(min_toclevel_, toclevel);
844 max_toclevel_ = max(max_toclevel_, toclevel);
847 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
848 << ", maximum is " << max_toclevel_);
850 return (error ? ERROR : OK);
854 void TextClass::readTitleType(Lexer & lexrc)
856 LexerKeyword titleTypeTags[] = {
857 { "commandafter", TITLE_COMMAND_AFTER },
858 { "environment", TITLE_ENVIRONMENT }
861 PushPopHelper pph(lexrc, titleTypeTags);
863 int le = lexrc.lex();
865 case Lexer::LEX_UNDEF:
866 lexrc.printError("Unknown output type `$$Token'");
868 case TITLE_COMMAND_AFTER:
869 case TITLE_ENVIRONMENT:
870 titletype_ = static_cast<TitleLatexType>(le);
873 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
879 void TextClass::readOutputType(Lexer & lexrc)
881 LexerKeyword outputTypeTags[] = {
882 { "docbook", DOCBOOK },
884 { "literate", LITERATE }
887 PushPopHelper pph(lexrc, outputTypeTags);
889 int le = lexrc.lex();
891 case Lexer::LEX_UNDEF:
892 lexrc.printError("Unknown output type `$$Token'");
897 outputType_ = static_cast<OutputType>(le);
900 LYXERR0("Unhandled value " << le);
906 void TextClass::readClassOptions(Lexer & lexrc)
916 LexerKeyword classOptionsTags[] = {
918 {"fontsize", CO_FONTSIZE },
919 {"header", CO_HEADER },
920 {"other", CO_OTHER },
921 {"pagestyle", CO_PAGESTYLE }
924 lexrc.pushTable(classOptionsTags);
926 while (!getout && lexrc.isOK()) {
927 int le = lexrc.lex();
929 case Lexer::LEX_UNDEF:
930 lexrc.printError("Unknown ClassOption tag `$$Token'");
938 opt_fontsize_ = rtrim(lexrc.getString());
942 opt_pagestyle_ = rtrim(lexrc.getString());
946 if (options_.empty())
947 options_ = lexrc.getString();
949 options_ += ',' + lexrc.getString();
953 class_header_ = subst(lexrc.getString(), """, "\"");
964 bool TextClass::readCiteEngine(Lexer & lexrc)
966 int const type = readCiteEngineType(lexrc);
967 if (type & ENGINE_TYPE_AUTHORYEAR)
968 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
969 if (type & ENGINE_TYPE_NUMERICAL)
970 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
971 if (type & ENGINE_TYPE_DEFAULT)
972 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
975 while (!getout && lexrc.isOK()) {
977 def = lexrc.getString();
978 def = subst(def, " ", "");
979 def = subst(def, "\t", "");
980 if (compare_ascii_no_case(def, "end") == 0) {
990 cs.forceUpperCase = true;
994 size_t const n = def.size();
995 for (size_t i = 0; i != n; ++i) {
998 cs.fullAuthorList = true;
999 else if (ichar == '[' && cs.textAfter)
1000 cs.textBefore = true;
1001 else if (ichar == '[')
1002 cs.textAfter = true;
1003 else if (ichar != ']')
1008 if (type & ENGINE_TYPE_AUTHORYEAR)
1009 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1010 if (type & ENGINE_TYPE_NUMERICAL)
1011 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1012 if (type & ENGINE_TYPE_DEFAULT)
1013 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1019 int TextClass::readCiteEngineType(Lexer & lexrc) const
1021 LATTEST(ENGINE_TYPE_DEFAULT ==
1022 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1023 if (!lexrc.next()) {
1024 lexrc.printError("No cite engine type given for token: `$$Token'.");
1025 return ENGINE_TYPE_DEFAULT;
1027 string const type = rtrim(lexrc.getString());
1028 if (compare_ascii_no_case(type, "authoryear") == 0)
1029 return ENGINE_TYPE_AUTHORYEAR;
1030 else if (compare_ascii_no_case(type, "numerical") == 0)
1031 return ENGINE_TYPE_NUMERICAL;
1032 else if (compare_ascii_no_case(type, "default") != 0) {
1033 string const s = "Unknown cite engine type `" + type
1034 + "' given for token: `$$Token',";
1035 lexrc.printError(s);
1037 return ENGINE_TYPE_DEFAULT;
1041 bool TextClass::readCiteFormat(Lexer & lexrc)
1043 int const type = readCiteEngineType(lexrc);
1046 while (lexrc.isOK()) {
1048 etype = lexrc.getString();
1049 if (compare_ascii_no_case(etype, "end") == 0)
1054 definition = lexrc.getString();
1055 char initchar = etype[0];
1056 if (initchar == '#')
1058 if (initchar == '!' || initchar == '_') {
1059 if (type & ENGINE_TYPE_AUTHORYEAR)
1060 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1061 if (type & ENGINE_TYPE_NUMERICAL)
1062 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1063 if (type & ENGINE_TYPE_DEFAULT)
1064 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1066 if (type & ENGINE_TYPE_AUTHORYEAR)
1067 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1068 if (type & ENGINE_TYPE_NUMERICAL)
1069 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1070 if (type & ENGINE_TYPE_DEFAULT)
1071 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1078 bool TextClass::readFloat(Lexer & lexrc)
1095 FT_ALLOWED_PLACEMENT,
1101 LexerKeyword floatTags[] = {
1102 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1103 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1104 { "allowswide", FT_ALLOWS_WIDE },
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);
1132 string allowed_placement = "!htbpH";
1137 bool usesfloat = true;
1138 bool ispredefined = false;
1139 bool allowswide = true;
1140 bool allowssideways = true;
1142 bool getout = false;
1143 while (!getout && lexrc.isOK()) {
1144 int le = lexrc.lex();
1146 case Lexer::LEX_UNDEF:
1147 lexrc.printError("Unknown float tag `$$Token'");
1155 type = lexrc.getString();
1156 if (floatlist_.typeExist(type)) {
1157 Floating const & fl = floatlist_.getType(type);
1158 placement = fl.placement();
1160 within = fl.within();
1163 listname = fl.listName();
1164 usesfloat = fl.usesFloatPkg();
1165 ispredefined = fl.isPredefined();
1166 listcommand = fl.listCommand();
1167 refprefix = fl.refPrefix();
1172 name = lexrc.getString();
1176 placement = lexrc.getString();
1178 case FT_ALLOWED_PLACEMENT:
1180 allowed_placement = lexrc.getString();
1184 ext = lexrc.getString();
1188 within = lexrc.getString();
1189 if (within == "none")
1194 style = lexrc.getString();
1196 case FT_LISTCOMMAND:
1198 listcommand = lexrc.getString();
1202 refprefix = lexrc.getString();
1206 listname = lexrc.getString();
1210 usesfloat = lexrc.getBool();
1214 ispredefined = lexrc.getBool();
1216 case FT_ALLOWS_SIDEWAYS:
1218 allowssideways = lexrc.getBool();
1220 case FT_ALLOWS_WIDE:
1222 allowswide = lexrc.getBool();
1226 htmlattr = lexrc.getString();
1230 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1234 htmltag = lexrc.getString();
1244 // Here we have a full float if getout == true
1246 if (!usesfloat && listcommand.empty()) {
1247 // if this float uses the same auxfile as an existing one,
1248 // there is no need for it to provide a list command.
1249 FloatList::const_iterator it = floatlist_.begin();
1250 FloatList::const_iterator en = floatlist_.end();
1251 bool found_ext = false;
1252 for (; it != en; ++it) {
1253 if (it->second.ext() == ext) {
1259 LYXERR0("The layout does not provide a list command " <<
1260 "for the float `" << type << "'. LyX will " <<
1261 "not be able to produce a float list.");
1263 Floating fl(type, placement, ext, within, style, name,
1264 listname, listcommand, refprefix, allowed_placement,
1265 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1266 allowswide, allowssideways);
1267 floatlist_.newFloat(fl);
1268 // each float has its own counter
1269 counters_.newCounter(from_ascii(type), from_ascii(within),
1270 docstring(), docstring());
1271 // also define sub-float counters
1272 docstring const subtype = "sub-" + from_ascii(type);
1273 counters_.newCounter(subtype, from_ascii(type),
1274 "\\alph{" + subtype + "}", docstring());
1280 string const & TextClass::prerequisites(string const & sep) const
1282 if (contains(prerequisites_, ',')) {
1283 vector<string> const pres = getVectorFromString(prerequisites_);
1284 prerequisites_ = getStringFromVector(pres, sep);
1286 return prerequisites_;
1290 bool TextClass::hasLayout(docstring const & n) const
1292 docstring const name = n.empty() ? defaultLayoutName() : n;
1294 return find_if(layoutlist_.begin(), layoutlist_.end(),
1295 LayoutNamesEqual(name))
1296 != layoutlist_.end();
1300 bool TextClass::hasInsetLayout(docstring const & n) const
1304 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1305 return it != insetlayoutlist_.end();
1309 Layout const & TextClass::operator[](docstring const & name) const
1311 LATTEST(!name.empty());
1314 find_if(begin(), end(), LayoutNamesEqual(name));
1317 LYXERR0("We failed to find the layout '" << name
1318 << "' in the layout list. You MUST investigate!");
1319 for (const_iterator cit = begin(); cit != end(); ++cit)
1320 lyxerr << " " << to_utf8(cit->name()) << endl;
1322 // We require the name to exist
1323 static const Layout dummy;
1324 LASSERT(false, return dummy);
1331 Layout & TextClass::operator[](docstring const & name)
1333 LATTEST(!name.empty());
1334 // Safe to continue, given what we do below.
1336 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1339 LYXERR0("We failed to find the layout '" << to_utf8(name)
1340 << "' in the layout list. You MUST investigate!");
1341 for (const_iterator cit = begin(); cit != end(); ++cit)
1342 LYXERR0(" " << to_utf8(cit->name()));
1344 // we require the name to exist
1346 // we are here only in release mode
1347 layoutlist_.push_back(createBasicLayout(name, true));
1348 it = find_if(begin(), end(), LayoutNamesEqual(name));
1355 bool TextClass::deleteLayout(docstring const & name)
1357 if (name == defaultLayoutName() || name == plainLayoutName())
1360 LayoutList::iterator it =
1361 remove_if(layoutlist_.begin(), layoutlist_.end(),
1362 LayoutNamesEqual(name));
1364 LayoutList::iterator end = layoutlist_.end();
1365 bool const ret = (it != end);
1366 layoutlist_.erase(it, end);
1371 bool TextClass::deleteInsetLayout(docstring const & name)
1373 return insetlayoutlist_.erase(name);
1377 // Load textclass info if not loaded yet
1378 bool TextClass::load(string const & path) const
1383 // Read style-file, provided path is searched before the system ones
1384 // If path is a file, it is loaded directly.
1385 FileName layout_file(path);
1386 if (!path.empty() && !layout_file.isReadableFile())
1387 layout_file = FileName(addName(path, name_ + ".layout"));
1388 if (layout_file.empty() || !layout_file.exists())
1389 layout_file = libFileSearch("layouts", name_, "layout");
1390 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1393 lyxerr << "Error reading `"
1394 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1395 << "'\n(Check `" << name_
1396 << "')\nCheck your installation and "
1397 "try Options/Reconfigure..."
1405 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1410 layoutlist_.push_back(createBasicLayout(n, true));
1415 string DocumentClass::forcedLayouts() const
1419 const_iterator const e = end();
1420 for (const_iterator i = begin(); i != e; ++i) {
1421 if (i->forcelocal > 0) {
1423 os << "Format " << LAYOUT_FORMAT << '\n';
1433 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1435 // FIXME The fix for the InsetLayout part of 4812 would be here:
1436 // Add the InsetLayout to the document class if it is not found.
1438 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1439 while (!n.empty()) {
1440 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1441 if (cit != cen && cit->first == n) {
1442 if (cit->second.obsoleted_by().empty())
1444 n = cit->second.obsoleted_by();
1445 return insetLayout(n);
1447 // If we have a generic prefix (e.g., "Note:"),
1448 // try if this one alone is found.
1449 size_t i = n.find(':');
1450 if (i == string::npos)
1454 // Layout "name" not found.
1455 return plain_insetlayout_;
1459 docstring const & TextClass::defaultLayoutName() const
1461 return defaultlayout_;
1465 Layout const & TextClass::defaultLayout() const
1467 return operator[](defaultLayoutName());
1471 bool TextClass::isDefaultLayout(Layout const & layout) const
1473 return layout.name() == defaultLayoutName();
1477 bool TextClass::isPlainLayout(Layout const & layout) const
1479 return layout.name() == plainLayoutName();
1483 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1485 static Layout * defaultLayout = NULL;
1487 if (defaultLayout) {
1488 defaultLayout->setUnknown(unknown);
1489 defaultLayout->setName(name);
1490 return *defaultLayout;
1493 static char const * s = "Margin Static\n"
1494 "LatexType Paragraph\n"
1497 "AlignPossible Left, Right, Center\n"
1498 "LabelType No_Label\n"
1500 istringstream ss(s);
1501 Lexer lex(textClassTags);
1503 defaultLayout = new Layout;
1504 defaultLayout->setUnknown(unknown);
1505 defaultLayout->setName(name);
1506 if (!readStyle(lex, *defaultLayout)) {
1507 // The only way this happens is because the hardcoded layout above
1511 return *defaultLayout;
1515 DocumentClassPtr getDocumentClass(
1516 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1519 DocumentClassPtr doc_class =
1520 DocumentClassPtr(new DocumentClass(baseClass));
1521 LayoutModuleList::const_iterator it = modlist.begin();
1522 LayoutModuleList::const_iterator en = modlist.end();
1523 for (; it != en; ++it) {
1524 string const modName = *it;
1525 LyXModule * lm = theModuleList[modName];
1527 docstring const msg =
1528 bformat(_("The module %1$s has been requested by\n"
1529 "this document but has not been found in the list of\n"
1530 "available modules. If you recently installed it, you\n"
1531 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1533 frontend::Alert::warning(_("Module not available"), msg);
1536 if (!lm->isAvailable() && !clone) {
1537 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1538 docstring const msg =
1539 bformat(_("The module %1$s requires a package that is not\n"
1540 "available in your LaTeX installation, or a converter that\n"
1541 "you have not installed. LaTeX output may not be possible.\n"
1542 "Missing prerequisites:\n"
1544 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1545 from_utf8(modName), prereqs);
1546 frontend::Alert::warning(_("Package not available"), msg, true);
1548 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1549 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1550 docstring const msg =
1551 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1552 frontend::Alert::warning(_("Read Error"), msg);
1559 /////////////////////////////////////////////////////////////////////////
1563 /////////////////////////////////////////////////////////////////////////
1565 DocumentClass::DocumentClass(LayoutFile const & tc)
1570 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1572 LayoutList::const_iterator it = layoutlist_.begin();
1573 LayoutList::const_iterator end = layoutlist_.end();
1574 for (; it != end; ++it)
1575 if (it->latexname() == lay)
1581 bool DocumentClass::provides(string const & p) const
1583 return provides_.find(p) != provides_.end();
1587 bool DocumentClass::hasTocLevels() const
1589 return min_toclevel_ != Layout::NOT_IN_TOC;
1593 Layout const & DocumentClass::getTOCLayout() const
1595 // we're going to look for the layout with the minimum toclevel
1596 TextClass::LayoutList::const_iterator lit = begin();
1597 TextClass::LayoutList::const_iterator const len = end();
1598 int minlevel = 1000;
1599 Layout const * lay = NULL;
1600 for (; lit != len; ++lit) {
1601 int const level = lit->toclevel;
1602 // we don't want Part or unnumbered sections
1603 if (level == Layout::NOT_IN_TOC || level < 0
1604 || level >= minlevel || lit->counter.empty())
1611 // hmm. that is very odd, so we'll do our best.
1612 return operator[](defaultLayoutName());
1616 Layout const & DocumentClass::htmlTOCLayout() const
1618 if (html_toc_section_.empty())
1619 html_toc_section_ = getTOCLayout().name();
1620 return operator[](html_toc_section_);
1624 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1625 string const & entry, string const & fallback) const
1627 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1629 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1630 if (itype == cite_formats_.end())
1631 return default_format;
1632 map<string, string>::const_iterator it = itype->second.find(entry);
1633 if (it == itype->second.end() && !fallback.empty())
1634 it = itype->second.find(fallback);
1635 if (it == itype->second.end())
1636 return default_format;
1641 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1642 string const & macro) const
1644 static string empty;
1645 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1646 if (itype == cite_macros_.end())
1648 map<string, string>::const_iterator it = itype->second.find(macro);
1649 if (it == itype->second.end())
1655 vector<string> const DocumentClass::citeCommands(
1656 CiteEngineType const & type) const
1658 vector<CitationStyle> const styles = citeStyles(type);
1659 vector<CitationStyle>::const_iterator it = styles.begin();
1660 vector<CitationStyle>::const_iterator end = styles.end();
1661 vector<string> cmds;
1662 for (; it != end; ++it) {
1663 CitationStyle const cite = *it;
1664 cmds.push_back(cite.cmd);
1670 vector<CitationStyle> const & DocumentClass::citeStyles(
1671 CiteEngineType const & type) const
1673 static vector<CitationStyle> empty;
1674 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1675 if (it == cite_styles_.end())
1681 /////////////////////////////////////////////////////////////////////////
1685 /////////////////////////////////////////////////////////////////////////
1687 ostream & operator<<(ostream & os, PageSides p)