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"
20 #include "CiteEnginesList.h"
24 #include "FloatList.h"
28 #include "ModuleList.h"
30 #include "frontends/alert.h"
32 #include "support/lassert.h"
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/FileName.h"
36 #include "support/filetools.h"
37 #include "support/gettext.h"
38 #include "support/lstrings.h"
39 #include "support/os.h"
40 #include "support/TempFile.h"
51 using namespace lyx::support;
55 // Keep the changes documented in the Customization manual.
57 // If you change this format, then you MUST also make sure that
58 // your changes do not invalidate the hardcoded layout file in
59 // LayoutFile.cpp. Additions will never do so, but syntax changes
60 // could. See LayoutFileList::addEmptyClass() and, especially, the
61 // definition of the layoutpost string.
62 // You should also run the development/tools/updatelayouts.py script,
63 // to update the format of all of our layout files.
65 int const LAYOUT_FORMAT = 62; //spitz PassThru for arguments.
68 // Layout format for the current lyx file format. Controls which format is
69 // targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
70 int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
75 class LayoutNamesEqual : public unary_function<Layout, bool> {
77 LayoutNamesEqual(docstring const & name)
80 bool operator()(Layout const & c) const
82 return c.name() == name_;
89 bool layout2layout(FileName const & filename, FileName const & tempfile,
90 int const format = LAYOUT_FORMAT)
92 FileName const script = libFileSearch("scripts", "layout2layout.py");
94 LYXERR0("Could not find layout conversion "
95 "script layout2layout.py.");
99 ostringstream command;
100 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
102 << ' ' << quoteName(filename.toFilesystemEncoding())
103 << ' ' << quoteName(tempfile.toFilesystemEncoding());
104 string const command_str = command.str();
106 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
108 cmd_ret const ret = runCommand(command_str);
109 if (ret.first != 0) {
110 if (format == LAYOUT_FORMAT)
111 LYXERR0("Conversion of layout with layout2layout.py has failed.");
118 string translateReadType(TextClass::ReadType rt)
121 case TextClass::BASECLASS:
123 case TextClass::MERGE:
125 case TextClass::MODULE:
126 return "module file";
127 case TextClass::CITE_ENGINE:
128 return "cite engine";
129 case TextClass::VALIDATION:
139 // This string should not be translated here,
140 // because it is a layout identifier.
141 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
144 /////////////////////////////////////////////////////////////////////////
148 /////////////////////////////////////////////////////////////////////////
150 TextClass::TextClass()
151 : loaded_(false), tex_class_avail_(false),
152 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
153 opt_pagestyle_("empty|plain|headings|fancy"), pagestyle_("default"),
154 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3),
155 outputType_(LATEX), outputFormat_("latex"),
156 defaultfont_(sane_font),
157 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
158 min_toclevel_(0), max_toclevel_(0),
159 cite_full_author_list_(true)
164 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
166 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
167 if (!lay.read(lexrc, *this)) {
168 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
172 lay.resfont = lay.font;
173 lay.resfont.realize(defaultfont_);
174 lay.reslabelfont = lay.labelfont;
175 lay.reslabelfont.realize(defaultfont_);
176 return true; // no errors
215 TC_ADDTOHTMLPREAMBLE,
232 LexerKeyword textClassTags[] = {
233 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
234 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
235 { "addtopreamble", TC_ADDTOPREAMBLE },
236 { "citeengine", TC_CITEENGINE },
237 { "citeenginetype", TC_CITEENGINETYPE },
238 { "citeformat", TC_CITEFORMAT },
239 { "classoptions", TC_CLASSOPTIONS },
240 { "columns", TC_COLUMNS },
241 { "counter", TC_COUNTER },
242 { "defaultbiblio", TC_DEFAULTBIBLIO },
243 { "defaultfont", TC_DEFAULTFONT },
244 { "defaultmodule", TC_DEFAULTMODULE },
245 { "defaultstyle", TC_DEFAULTSTYLE },
246 { "excludesmodule", TC_EXCLUDESMODULE },
247 { "float", TC_FLOAT },
248 { "format", TC_FORMAT },
249 { "fullauthorlist", TC_FULLAUTHORLIST },
250 { "htmlpreamble", TC_HTMLPREAMBLE },
251 { "htmlstyles", TC_HTMLSTYLES },
252 { "htmltocsection", TC_HTMLTOCSECTION },
253 { "ifcounter", TC_IFCOUNTER },
254 { "input", TC_INPUT },
255 { "insetlayout", TC_INSETLAYOUT },
256 { "leftmargin", TC_LEFTMARGIN },
257 { "modifystyle", TC_MODIFYSTYLE },
258 { "nocounter", TC_NOCOUNTER },
259 { "nofloat", TC_NOFLOAT },
260 { "noinsetlayout", TC_NOINSETLAYOUT },
261 { "nostyle", TC_NOSTYLE },
262 { "outlinername", TC_OUTLINERNAME },
263 { "outputformat", TC_OUTPUTFORMAT },
264 { "outputtype", TC_OUTPUTTYPE },
265 { "packageoptions", TC_PKGOPTS },
266 { "pagestyle", TC_PAGESTYLE },
267 { "preamble", TC_PREAMBLE },
268 { "provides", TC_PROVIDES },
269 { "providesmodule", TC_PROVIDESMODULE },
270 { "providestyle", TC_PROVIDESTYLE },
271 { "requires", TC_REQUIRES },
272 { "rightmargin", TC_RIGHTMARGIN },
273 { "secnumdepth", TC_SECNUMDEPTH },
274 { "sides", TC_SIDES },
275 { "style", TC_STYLE },
276 { "titlelatexname", TC_TITLELATEXNAME },
277 { "titlelatextype", TC_TITLELATEXTYPE },
278 { "tocdepth", TC_TOCDEPTH }
284 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
286 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
287 TempFile tmp("convertXXXXXX.layout");
288 FileName const tempfile = tmp.name();
289 bool success = layout2layout(filename, tempfile);
291 success = readWithoutConv(tempfile, rt) == OK;
296 std::string TextClass::convert(std::string const & str)
298 TempFile tmp1("localXXXXXX.layout");
299 FileName const fn = tmp1.name();
300 ofstream os(fn.toFilesystemEncoding().c_str());
303 TempFile tmp2("convert_localXXXXXX.layout");
304 FileName const tempfile = tmp2.name();
305 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
308 ifstream is(tempfile.toFilesystemEncoding().c_str());
320 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
322 if (!filename.isReadableFile()) {
323 lyxerr << "Cannot read layout file `" << filename << "'."
328 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
329 to_utf8(makeDisplayPath(filename.absFileName())));
331 // Define the plain layout used in table cells, ert, etc. Note that
332 // we do this before loading any layout file, so that classes can
333 // override features of this layout if they should choose to do so.
334 if (rt == BASECLASS && !hasLayout(plain_layout_))
335 layoutlist_.push_back(createBasicLayout(plain_layout_));
337 Lexer lexrc(textClassTags);
338 lexrc.setFile(filename);
339 ReturnValues retval = read(lexrc, rt);
341 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
342 to_utf8(makeDisplayPath(filename.absFileName())));
348 bool TextClass::read(FileName const & filename, ReadType rt)
350 ReturnValues const retval = readWithoutConv(filename, rt);
351 if (retval != FORMAT_MISMATCH)
354 bool const worx = convertLayoutFormat(filename, rt);
356 LYXERR0 ("Unable to convert " << filename <<
357 " to format " << LAYOUT_FORMAT);
362 TextClass::ReturnValues TextClass::validate(std::string const & str)
365 return tc.read(str, VALIDATION);
369 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
371 Lexer lexrc(textClassTags);
372 istringstream is(str);
374 ReturnValues retval = read(lexrc, rt);
376 if (retval != FORMAT_MISMATCH)
379 // write the layout string to a temporary file
380 TempFile tmp("TextClass_read");
381 FileName const tempfile = tmp.name();
382 ofstream os(tempfile.toFilesystemEncoding().c_str());
384 LYXERR0("Unable to create temporary file");
390 // now try to convert it to LAYOUT_FORMAT
391 if (!convertLayoutFormat(tempfile, rt)) {
392 LYXERR0("Unable to convert internal layout information to format "
401 // Reads a textclass structure from file.
402 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
407 // Format of files before the 'Format' tag was introduced
412 while (lexrc.isOK() && !error) {
413 int le = lexrc.lex();
416 case Lexer::LEX_FEOF:
419 case Lexer::LEX_UNDEF:
420 lexrc.printError("Unknown TextClass tag `$$Token'");
428 // used below to track whether we are in an IfStyle or IfCounter tag.
429 bool modifystyle = false;
430 bool providestyle = false;
431 bool ifcounter = false;
433 switch (static_cast<TextClassTags>(le)) {
437 format = lexrc.getInteger();
440 case TC_OUTPUTFORMAT:
442 outputFormat_ = lexrc.getString();
446 readOutputType(lexrc);
447 switch(outputType_) {
449 outputFormat_ = "latex";
452 outputFormat_ = "docbook";
455 outputFormat_ = "literate";
460 case TC_INPUT: // Include file
462 string const inc = lexrc.getString();
463 FileName tmp = libFileSearch("layouts", inc,
467 lexrc.printError("Could not find input file: " + inc);
469 } else if (!read(tmp, MERGE)) {
470 lexrc.printError("Error reading input file: " + tmp.absFileName());
476 case TC_DEFAULTSTYLE:
478 docstring const name = from_utf8(subst(lexrc.getString(),
480 defaultlayout_ = name;
487 case TC_PROVIDESTYLE:
488 // if modifystyle is true, then we got here by falling through
489 // so we are not in an ProvideStyle block
495 lexrc.printError("No name given for style: `$$Token'.");
499 docstring const name = from_utf8(subst(lexrc.getString(),
502 string s = "Could not read name for style: `$$Token' "
503 + lexrc.getString() + " is probably not valid UTF-8!";
506 // Since we couldn't read the name, we just scan the rest
507 // of the style and discard it.
508 error = !readStyle(lexrc, lay);
512 bool const have_layout = hasLayout(name);
514 // If the layout already exists, then we want to add it to
515 // the existing layout, as long as we are not in an ProvideStyle
517 if (have_layout && !providestyle) {
518 Layout & lay = operator[](name);
519 error = !readStyle(lexrc, lay);
521 // If the layout does not exist, then we want to create a new
522 // one, but not if we are in a ModifyStyle block.
523 else if (!have_layout && !modifystyle) {
525 layout.setName(name);
526 error = !readStyle(lexrc, layout);
528 layoutlist_.push_back(layout);
530 if (defaultlayout_.empty()) {
531 // We do not have a default layout yet, so we choose
532 // the first layout we encounter.
533 defaultlayout_ = name;
536 // There are two ways to get here:
537 // (i) The layout exists but we are in an ProvideStyle block
538 // (ii) The layout doesn't exist, but we are in an ModifyStyle
540 // Either way, we just scan the rest and discard it
543 // false positive from coverity
544 // coverity[CHECKED_RETURN]
545 readStyle(lexrc, lay);
552 docstring const style = from_utf8(subst(lexrc.getString(),
554 if (!deleteLayout(style))
555 lyxerr << "Cannot delete style `"
556 << to_utf8(style) << '\'' << endl;
560 case TC_NOINSETLAYOUT:
562 docstring const style = from_utf8(subst(lexrc.getString(),
564 if (!deleteInsetLayout(style))
565 LYXERR0("Style `" << style << "' cannot be removed\n"
566 "because it was not found!");
572 columns_ = lexrc.getInteger();
577 switch (lexrc.getInteger()) {
578 case 1: sides_ = OneSide; break;
579 case 2: sides_ = TwoSides; break;
581 lyxerr << "Impossible number of page"
582 " sides, setting to one."
592 pagestyle_ = rtrim(lexrc.getString());
596 defaultfont_ = lyxRead(lexrc);
597 if (!defaultfont_.resolved()) {
598 lexrc.printError("Warning: defaultfont should "
599 "be fully instantiated!");
600 defaultfont_.realize(sane_font);
606 secnumdepth_ = lexrc.getInteger();
611 tocdepth_ = lexrc.getInteger();
614 // First step to support options
615 case TC_CLASSOPTIONS:
616 readClassOptions(lexrc);
620 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
623 case TC_HTMLPREAMBLE:
624 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
628 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
631 case TC_HTMLTOCSECTION:
632 html_toc_section_ = from_utf8(trim(lexrc.getString()));
635 case TC_ADDTOPREAMBLE:
636 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
639 case TC_ADDTOHTMLPREAMBLE:
640 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
643 case TC_ADDTOHTMLSTYLES:
644 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
649 string const feature = lexrc.getString();
651 if (lexrc.getInteger())
652 provides_.insert(feature);
654 provides_.erase(feature);
660 vector<string> const req
661 = getVectorFromString(lexrc.getString());
662 requires_.insert(req.begin(), req.end());
668 string const pkg = lexrc.getString();
670 string const options = lexrc.getString();
671 package_options_[pkg] = options;
675 case TC_DEFAULTMODULE: {
677 string const module = lexrc.getString();
678 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
679 default_modules_.push_back(module);
683 case TC_PROVIDESMODULE: {
685 string const module = lexrc.getString();
686 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
687 provided_modules_.push_back(module);
691 case TC_EXCLUDESMODULE: {
693 string const module = lexrc.getString();
694 // modules already have their own way to exclude other modules
696 LYXERR0("ExcludesModule tag cannot be used in a module!");
699 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
700 excluded_modules_.push_back(module);
704 case TC_LEFTMARGIN: // left margin type
706 leftmargin_ = lexrc.getDocString();
709 case TC_RIGHTMARGIN: // right margin type
711 rightmargin_ = lexrc.getDocString();
714 case TC_INSETLAYOUT: {
716 lexrc.printError("No name given for InsetLayout: `$$Token'.");
720 docstring const name = subst(lexrc.getDocString(), '_', ' ');
722 string s = "Could not read name for InsetLayout: `$$Token' "
723 + lexrc.getString() + " is probably not valid UTF-8!";
726 // Since we couldn't read the name, we just scan the rest
727 // of the style and discard it.
728 il.read(lexrc, *this);
729 // Let's try to continue rather than abort.
731 } else if (hasInsetLayout(name)) {
732 InsetLayout & il = insetlayoutlist_[name];
733 error = !il.read(lexrc, *this);
737 error = !il.read(lexrc, *this);
739 insetlayoutlist_[name] = il;
745 error = !readFloat(lexrc);
749 error = !readCiteEngine(lexrc);
752 case TC_CITEENGINETYPE:
754 opt_enginetype_ = rtrim(lexrc.getString());
758 error = !readCiteFormat(lexrc);
761 case TC_DEFAULTBIBLIO:
763 vector<string> const dbs =
764 getVectorFromString(rtrim(lexrc.getString()), "|");
765 vector<string>::const_iterator it = dbs.begin();
766 vector<string>::const_iterator end = dbs.end();
767 for (; it != end; ++it) {
768 if (!contains(*it, ':'))
769 cite_default_biblio_style_[opt_enginetype_] = *it;
772 string const db = split(*it, eng, ':');
773 cite_default_biblio_style_[eng] = db;
779 case TC_FULLAUTHORLIST:
781 cite_full_author_list_ &= lexrc.getBool();
786 docstring const cnt = lexrc.getDocString();
787 if (!counters_.remove(cnt))
788 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
797 docstring const name = lexrc.getDocString();
799 string s = "Could not read name for counter: `$$Token' "
800 + lexrc.getString() + " is probably not valid UTF-8!";
801 lexrc.printError(s.c_str());
803 // Since we couldn't read the name, we just scan the rest
807 error = !counters_.read(lexrc, name, !ifcounter);
810 lexrc.printError("No name given for style: `$$Token'.");
815 case TC_TITLELATEXTYPE:
816 readTitleType(lexrc);
819 case TC_TITLELATEXNAME:
821 titlename_ = lexrc.getString();
826 string const nofloat = lexrc.getString();
827 floatlist_.erase(nofloat);
831 case TC_OUTLINERNAME:
832 error = !readOutlinerName(lexrc);
836 // Note that this is triggered the first time through the loop unless
837 // we hit a format tag.
838 if (format != LAYOUT_FORMAT)
839 return FORMAT_MISMATCH;
842 // at present, we abort if we encounter an error,
843 // so there is no point continuing.
850 if (defaultlayout_.empty()) {
851 LYXERR0("Error: Textclass '" << name_
852 << "' is missing a defaultstyle.");
856 // Try to erase "stdinsets" from the provides_ set.
858 // Provides stdinsets 1
859 // declaration simply tells us that the standard insets have been
860 // defined. (It's found in stdinsets.inc but could also be used in
861 // user-defined files.) There isn't really any such package. So we
862 // might as well go ahead and erase it.
863 // If we do not succeed, then it was not there, which means that
864 // the textclass did not provide the definitions of the standard
865 // insets. So we need to try to load them.
866 int erased = provides_.erase("stdinsets");
868 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
871 frontend::Alert::warning(_("Missing File"),
872 _("Could not find stdinsets.inc! This may lead to data loss!"));
874 } else if (!read(tmp, MERGE)) {
875 frontend::Alert::warning(_("Corrupt File"),
876 _("Could not read stdinsets.inc! This may lead to data loss!"));
881 min_toclevel_ = Layout::NOT_IN_TOC;
882 max_toclevel_ = Layout::NOT_IN_TOC;
883 const_iterator lit = begin();
884 const_iterator len = end();
885 for (; lit != len; ++lit) {
886 int const toclevel = lit->toclevel;
887 if (toclevel != Layout::NOT_IN_TOC) {
888 if (min_toclevel_ == Layout::NOT_IN_TOC)
889 min_toclevel_ = toclevel;
891 min_toclevel_ = min(min_toclevel_, toclevel);
892 max_toclevel_ = max(max_toclevel_, toclevel);
895 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
896 << ", maximum is " << max_toclevel_);
898 return (error ? ERROR : OK);
902 void TextClass::readTitleType(Lexer & lexrc)
904 LexerKeyword titleTypeTags[] = {
905 { "commandafter", TITLE_COMMAND_AFTER },
906 { "environment", TITLE_ENVIRONMENT }
909 PushPopHelper pph(lexrc, titleTypeTags);
911 int le = lexrc.lex();
913 case Lexer::LEX_UNDEF:
914 lexrc.printError("Unknown output type `$$Token'");
916 case TITLE_COMMAND_AFTER:
917 case TITLE_ENVIRONMENT:
918 titletype_ = static_cast<TitleLatexType>(le);
921 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
927 void TextClass::readOutputType(Lexer & lexrc)
929 LexerKeyword outputTypeTags[] = {
930 { "docbook", DOCBOOK },
932 { "literate", LITERATE }
935 PushPopHelper pph(lexrc, outputTypeTags);
937 int le = lexrc.lex();
939 case Lexer::LEX_UNDEF:
940 lexrc.printError("Unknown output type `$$Token'");
945 outputType_ = static_cast<OutputType>(le);
948 LYXERR0("Unhandled value " << le);
954 void TextClass::readClassOptions(Lexer & lexrc)
964 LexerKeyword classOptionsTags[] = {
966 {"fontsize", CO_FONTSIZE },
967 {"header", CO_HEADER },
968 {"other", CO_OTHER },
969 {"pagestyle", CO_PAGESTYLE }
972 lexrc.pushTable(classOptionsTags);
974 while (!getout && lexrc.isOK()) {
975 int le = lexrc.lex();
977 case Lexer::LEX_UNDEF:
978 lexrc.printError("Unknown ClassOption tag `$$Token'");
986 opt_fontsize_ = rtrim(lexrc.getString());
990 opt_pagestyle_ = rtrim(lexrc.getString());
994 if (options_.empty())
995 options_ = lexrc.getString();
997 options_ += ',' + lexrc.getString();
1001 class_header_ = subst(lexrc.getString(), """, "\"");
1012 bool TextClass::readCiteEngine(Lexer & lexrc)
1014 int const type = readCiteEngineType(lexrc);
1015 if (type & ENGINE_TYPE_AUTHORYEAR)
1016 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1017 if (type & ENGINE_TYPE_NUMERICAL)
1018 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1019 if (type & ENGINE_TYPE_DEFAULT)
1020 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1022 bool getout = false;
1023 while (!getout && lexrc.isOK()) {
1025 def = lexrc.getString();
1026 def = subst(def, " ", "");
1027 def = subst(def, "\t", "");
1028 if (compare_ascii_no_case(def, "end") == 0) {
1034 char ichar = def[0];
1038 cs.forceUpperCase = true;
1042 size_t const n = def.size();
1043 for (size_t i = 0; i != n; ++i) {
1046 cs.fullAuthorList = true;
1047 else if (ichar == '[' && cs.textAfter)
1048 cs.textBefore = true;
1049 else if (ichar == '[')
1050 cs.textAfter = true;
1051 else if (ichar != ']')
1056 if (type & ENGINE_TYPE_AUTHORYEAR)
1057 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1058 if (type & ENGINE_TYPE_NUMERICAL)
1059 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1060 if (type & ENGINE_TYPE_DEFAULT)
1061 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1067 int TextClass::readCiteEngineType(Lexer & lexrc) const
1069 LATTEST(ENGINE_TYPE_DEFAULT ==
1070 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1071 if (!lexrc.next()) {
1072 lexrc.printError("No cite engine type given for token: `$$Token'.");
1073 return ENGINE_TYPE_DEFAULT;
1075 string const type = rtrim(lexrc.getString());
1076 if (compare_ascii_no_case(type, "authoryear") == 0)
1077 return ENGINE_TYPE_AUTHORYEAR;
1078 else if (compare_ascii_no_case(type, "numerical") == 0)
1079 return ENGINE_TYPE_NUMERICAL;
1080 else if (compare_ascii_no_case(type, "default") != 0) {
1081 string const s = "Unknown cite engine type `" + type
1082 + "' given for token: `$$Token',";
1083 lexrc.printError(s);
1085 return ENGINE_TYPE_DEFAULT;
1089 bool TextClass::readCiteFormat(Lexer & lexrc)
1091 int const type = readCiteEngineType(lexrc);
1094 while (lexrc.isOK()) {
1096 etype = lexrc.getString();
1097 if (compare_ascii_no_case(etype, "end") == 0)
1102 definition = lexrc.getString();
1103 char initchar = etype[0];
1104 if (initchar == '#')
1106 if (initchar == '!' || initchar == '_') {
1107 if (type & ENGINE_TYPE_AUTHORYEAR)
1108 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1109 if (type & ENGINE_TYPE_NUMERICAL)
1110 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1111 if (type & ENGINE_TYPE_DEFAULT)
1112 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1114 if (type & ENGINE_TYPE_AUTHORYEAR)
1115 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1116 if (type & ENGINE_TYPE_NUMERICAL)
1117 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1118 if (type & ENGINE_TYPE_DEFAULT)
1119 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1126 bool TextClass::readFloat(Lexer & lexrc)
1143 FT_ALLOWED_PLACEMENT,
1149 LexerKeyword floatTags[] = {
1150 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1151 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1152 { "allowswide", FT_ALLOWS_WIDE },
1154 { "extension", FT_EXT },
1155 { "guiname", FT_NAME },
1156 { "htmlattr", FT_HTMLATTR },
1157 { "htmlstyle", FT_HTMLSTYLE },
1158 { "htmltag", FT_HTMLTAG },
1159 { "ispredefined", FT_PREDEFINED },
1160 { "listcommand", FT_LISTCOMMAND },
1161 { "listname", FT_LISTNAME },
1162 { "numberwithin", FT_WITHIN },
1163 { "placement", FT_PLACEMENT },
1164 { "refprefix", FT_REFPREFIX },
1165 { "style", FT_STYLE },
1166 { "type", FT_TYPE },
1167 { "usesfloatpkg", FT_USESFLOAT }
1170 lexrc.pushTable(floatTags);
1174 docstring htmlstyle;
1180 string allowed_placement = "!htbpH";
1185 bool usesfloat = true;
1186 bool ispredefined = false;
1187 bool allowswide = true;
1188 bool allowssideways = true;
1190 bool getout = false;
1191 while (!getout && lexrc.isOK()) {
1192 int le = lexrc.lex();
1194 case Lexer::LEX_UNDEF:
1195 lexrc.printError("Unknown float tag `$$Token'");
1203 type = lexrc.getString();
1204 if (floatlist_.typeExist(type)) {
1205 Floating const & fl = floatlist_.getType(type);
1206 placement = fl.placement();
1208 within = fl.within();
1211 listname = fl.listName();
1212 usesfloat = fl.usesFloatPkg();
1213 ispredefined = fl.isPredefined();
1214 listcommand = fl.listCommand();
1215 refprefix = fl.refPrefix();
1220 name = lexrc.getString();
1224 placement = lexrc.getString();
1226 case FT_ALLOWED_PLACEMENT:
1228 allowed_placement = lexrc.getString();
1232 ext = lexrc.getString();
1236 within = lexrc.getString();
1237 if (within == "none")
1242 style = lexrc.getString();
1244 case FT_LISTCOMMAND:
1246 listcommand = lexrc.getString();
1250 refprefix = lexrc.getString();
1254 listname = lexrc.getString();
1258 usesfloat = lexrc.getBool();
1262 ispredefined = lexrc.getBool();
1264 case FT_ALLOWS_SIDEWAYS:
1266 allowssideways = lexrc.getBool();
1268 case FT_ALLOWS_WIDE:
1270 allowswide = lexrc.getBool();
1274 htmlattr = lexrc.getString();
1278 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1282 htmltag = lexrc.getString();
1292 // Here we have a full float if getout == true
1294 if (!usesfloat && listcommand.empty()) {
1295 // if this float uses the same auxfile as an existing one,
1296 // there is no need for it to provide a list command.
1297 FloatList::const_iterator it = floatlist_.begin();
1298 FloatList::const_iterator en = floatlist_.end();
1299 bool found_ext = false;
1300 for (; it != en; ++it) {
1301 if (it->second.ext() == ext) {
1307 LYXERR0("The layout does not provide a list command " <<
1308 "for the float `" << type << "'. LyX will " <<
1309 "not be able to produce a float list.");
1311 Floating fl(type, placement, ext, within, style, name,
1312 listname, listcommand, refprefix, allowed_placement,
1313 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1314 allowswide, allowssideways);
1315 floatlist_.newFloat(fl);
1316 // each float has its own counter
1317 counters_.newCounter(from_ascii(type), from_ascii(within),
1318 docstring(), docstring());
1319 // also define sub-float counters
1320 docstring const subtype = "sub-" + from_ascii(type);
1321 counters_.newCounter(subtype, from_ascii(type),
1322 "\\alph{" + subtype + "}", docstring());
1328 bool TextClass::readOutlinerName(Lexer & lexrc)
1333 type = lexrc.getString();
1335 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1339 name = lexrc.getDocString();
1341 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1344 outliner_names_[type] = name;
1349 docstring TextClass::outlinerName(std::string const & type) const
1351 std::map<std::string,docstring>::const_iterator const it
1352 = outliner_names_.find(type);
1353 if (it == outliner_names_.end()) {
1354 LYXERR0("Missing OutlinerName for " << type << "!");
1355 return from_utf8(type);
1361 string const & TextClass::prerequisites(string const & sep) const
1363 if (contains(prerequisites_, ',')) {
1364 vector<string> const pres = getVectorFromString(prerequisites_);
1365 prerequisites_ = getStringFromVector(pres, sep);
1367 return prerequisites_;
1371 bool TextClass::hasLayout(docstring const & n) const
1373 docstring const name = n.empty() ? defaultLayoutName() : n;
1375 return find_if(layoutlist_.begin(), layoutlist_.end(),
1376 LayoutNamesEqual(name))
1377 != layoutlist_.end();
1381 bool TextClass::hasInsetLayout(docstring const & n) const
1385 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1386 return it != insetlayoutlist_.end();
1390 Layout const & TextClass::operator[](docstring const & name) const
1392 LATTEST(!name.empty());
1395 find_if(begin(), end(), LayoutNamesEqual(name));
1398 LYXERR0("We failed to find the layout '" << name
1399 << "' in the layout list. You MUST investigate!");
1400 for (const_iterator cit = begin(); cit != end(); ++cit)
1401 lyxerr << " " << to_utf8(cit->name()) << endl;
1403 // We require the name to exist
1404 static const Layout dummy;
1405 LASSERT(false, return dummy);
1412 Layout & TextClass::operator[](docstring const & name)
1414 LATTEST(!name.empty());
1415 // Safe to continue, given what we do below.
1417 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1420 LYXERR0("We failed to find the layout '" << to_utf8(name)
1421 << "' in the layout list. You MUST investigate!");
1422 for (const_iterator cit = begin(); cit != end(); ++cit)
1423 LYXERR0(" " << to_utf8(cit->name()));
1425 // we require the name to exist
1427 // we are here only in release mode
1428 layoutlist_.push_back(createBasicLayout(name, true));
1429 it = find_if(begin(), end(), LayoutNamesEqual(name));
1436 bool TextClass::deleteLayout(docstring const & name)
1438 if (name == defaultLayoutName() || name == plainLayoutName())
1441 LayoutList::iterator it =
1442 remove_if(layoutlist_.begin(), layoutlist_.end(),
1443 LayoutNamesEqual(name));
1445 LayoutList::iterator end = layoutlist_.end();
1446 bool const ret = (it != end);
1447 layoutlist_.erase(it, end);
1452 bool TextClass::deleteInsetLayout(docstring const & name)
1454 return insetlayoutlist_.erase(name);
1458 // Load textclass info if not loaded yet
1459 bool TextClass::load(string const & path) const
1464 // Read style-file, provided path is searched before the system ones
1465 // If path is a file, it is loaded directly.
1466 FileName layout_file(path);
1467 if (!path.empty() && !layout_file.isReadableFile())
1468 layout_file = FileName(addName(path, name_ + ".layout"));
1469 if (layout_file.empty() || !layout_file.exists())
1470 layout_file = libFileSearch("layouts", name_, "layout");
1471 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1474 lyxerr << "Error reading `"
1475 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1476 << "'\n(Check `" << name_
1477 << "')\nCheck your installation and "
1478 "try Options/Reconfigure..."
1486 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1491 layoutlist_.push_back(createBasicLayout(n, true));
1496 string DocumentClass::forcedLayouts() const
1500 const_iterator const e = end();
1501 for (const_iterator i = begin(); i != e; ++i) {
1502 if (i->forcelocal > 0) {
1504 os << "Format " << LAYOUT_FORMAT << '\n';
1514 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1516 // FIXME The fix for the InsetLayout part of 4812 would be here:
1517 // Add the InsetLayout to the document class if it is not found.
1519 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1520 while (!n.empty()) {
1521 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1522 if (cit != cen && cit->first == n) {
1523 if (cit->second.obsoleted_by().empty())
1525 n = cit->second.obsoleted_by();
1526 return insetLayout(n);
1528 // If we have a generic prefix (e.g., "Note:"),
1529 // try if this one alone is found.
1530 size_t i = n.find(':');
1531 if (i == string::npos)
1535 // Layout "name" not found.
1536 return plainInsetLayout();
1540 InsetLayout const & DocumentClass::plainInsetLayout() {
1541 static const InsetLayout plain_insetlayout_;
1542 return plain_insetlayout_;
1546 docstring const & TextClass::defaultLayoutName() const
1548 return defaultlayout_;
1552 Layout const & TextClass::defaultLayout() const
1554 return operator[](defaultLayoutName());
1558 bool TextClass::isDefaultLayout(Layout const & layout) const
1560 return layout.name() == defaultLayoutName();
1564 bool TextClass::isPlainLayout(Layout const & layout) const
1566 return layout.name() == plainLayoutName();
1570 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1572 static Layout * defaultLayout = NULL;
1574 if (defaultLayout) {
1575 defaultLayout->setUnknown(unknown);
1576 defaultLayout->setName(name);
1577 return *defaultLayout;
1580 static char const * s = "Margin Static\n"
1581 "LatexType Paragraph\n"
1584 "AlignPossible Left, Right, Center\n"
1585 "LabelType No_Label\n"
1587 istringstream ss(s);
1588 Lexer lex(textClassTags);
1590 defaultLayout = new Layout;
1591 defaultLayout->setUnknown(unknown);
1592 defaultLayout->setName(name);
1593 if (!readStyle(lex, *defaultLayout)) {
1594 // The only way this happens is because the hardcoded layout above
1598 return *defaultLayout;
1602 DocumentClassPtr getDocumentClass(
1603 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1604 LayoutModuleList const & celist,
1607 DocumentClassPtr doc_class =
1608 DocumentClassPtr(new DocumentClass(baseClass));
1609 LayoutModuleList::const_iterator it = modlist.begin();
1610 LayoutModuleList::const_iterator en = modlist.end();
1611 for (; it != en; ++it) {
1612 string const modName = *it;
1613 LyXModule * lm = theModuleList[modName];
1615 docstring const msg =
1616 bformat(_("The module %1$s has been requested by\n"
1617 "this document but has not been found in the list of\n"
1618 "available modules. If you recently installed it, you\n"
1619 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1621 frontend::Alert::warning(_("Module not available"), msg);
1624 if (!lm->isAvailable() && !clone) {
1625 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1626 docstring const msg =
1627 bformat(_("The module %1$s requires a package that is not\n"
1628 "available in your LaTeX installation, or a converter that\n"
1629 "you have not installed. LaTeX output may not be possible.\n"
1630 "Missing prerequisites:\n"
1632 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1633 from_utf8(modName), prereqs);
1634 frontend::Alert::warning(_("Package not available"), msg, true);
1636 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1637 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1638 docstring const msg =
1639 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1640 frontend::Alert::warning(_("Read Error"), msg);
1644 LayoutModuleList::const_iterator cit = celist.begin();
1645 LayoutModuleList::const_iterator cen = celist.end();
1646 for (; cit != cen; ++cit) {
1647 string const ceName = *cit;
1648 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1650 docstring const msg =
1651 bformat(_("The cite engine %1$s has been requested by\n"
1652 "this document but has not been found in the list of\n"
1653 "available engines. If you recently installed it, you\n"
1654 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1656 frontend::Alert::warning(_("Cite Engine not available"), msg);
1659 if (!ce->isAvailable() && !clone) {
1660 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1661 docstring const msg =
1662 bformat(_("The cite engine %1$s requires a package that is not\n"
1663 "available in your LaTeX installation, or a converter that\n"
1664 "you have not installed. LaTeX output may not be possible.\n"
1665 "Missing prerequisites:\n"
1667 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1668 from_utf8(ceName), prereqs);
1669 frontend::Alert::warning(_("Package not available"), msg, true);
1671 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1672 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1673 docstring const msg =
1674 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1675 frontend::Alert::warning(_("Read Error"), msg);
1683 /////////////////////////////////////////////////////////////////////////
1687 /////////////////////////////////////////////////////////////////////////
1689 DocumentClass::DocumentClass(LayoutFile const & tc)
1694 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1696 LayoutList::const_iterator it = layoutlist_.begin();
1697 LayoutList::const_iterator end = layoutlist_.end();
1698 for (; it != end; ++it)
1699 if (it->latexname() == lay)
1705 bool DocumentClass::provides(string const & p) const
1707 return provides_.find(p) != provides_.end();
1711 bool DocumentClass::hasTocLevels() const
1713 return min_toclevel_ != Layout::NOT_IN_TOC;
1717 Layout const & DocumentClass::getTOCLayout() const
1719 // we're going to look for the layout with the minimum toclevel
1720 TextClass::LayoutList::const_iterator lit = begin();
1721 TextClass::LayoutList::const_iterator const len = end();
1722 int minlevel = 1000;
1723 Layout const * lay = NULL;
1724 for (; lit != len; ++lit) {
1725 int const level = lit->toclevel;
1726 // we don't want Part or unnumbered sections
1727 if (level == Layout::NOT_IN_TOC || level < 0
1728 || level >= minlevel || lit->counter.empty())
1735 // hmm. that is very odd, so we'll do our best.
1736 return operator[](defaultLayoutName());
1740 Layout const & DocumentClass::htmlTOCLayout() const
1742 if (html_toc_section_.empty())
1743 html_toc_section_ = getTOCLayout().name();
1744 return operator[](html_toc_section_);
1748 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1749 string const & entry, string const & fallback) const
1751 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1753 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1754 if (itype == cite_formats_.end())
1755 return default_format;
1756 map<string, string>::const_iterator it = itype->second.find(entry);
1757 if (it == itype->second.end() && !fallback.empty())
1758 it = itype->second.find(fallback);
1759 if (it == itype->second.end())
1760 return default_format;
1765 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1766 string const & macro) const
1768 static string empty;
1769 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1770 if (itype == cite_macros_.end())
1772 map<string, string>::const_iterator it = itype->second.find(macro);
1773 if (it == itype->second.end())
1779 vector<string> const DocumentClass::citeCommands(
1780 CiteEngineType const & type) const
1782 vector<CitationStyle> const styles = citeStyles(type);
1783 vector<CitationStyle>::const_iterator it = styles.begin();
1784 vector<CitationStyle>::const_iterator end = styles.end();
1785 vector<string> cmds;
1786 for (; it != end; ++it) {
1787 CitationStyle const cite = *it;
1788 cmds.push_back(cite.cmd);
1794 vector<CitationStyle> const & DocumentClass::citeStyles(
1795 CiteEngineType const & type) const
1797 static vector<CitationStyle> empty;
1798 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1799 if (it == cite_styles_.end())
1805 /////////////////////////////////////////////////////////////////////////
1809 /////////////////////////////////////////////////////////////////////////
1811 ostream & operator<<(ostream & os, PageSides p)