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 cite_default_biblio_style_ = rtrim(lexrc.getString());
766 case TC_FULLAUTHORLIST:
768 cite_full_author_list_ &= lexrc.getBool();
773 docstring const cnt = lexrc.getDocString();
774 if (!counters_.remove(cnt))
775 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
784 docstring const name = lexrc.getDocString();
786 string s = "Could not read name for counter: `$$Token' "
787 + lexrc.getString() + " is probably not valid UTF-8!";
788 lexrc.printError(s.c_str());
790 // Since we couldn't read the name, we just scan the rest
794 error = !counters_.read(lexrc, name, !ifcounter);
797 lexrc.printError("No name given for style: `$$Token'.");
802 case TC_TITLELATEXTYPE:
803 readTitleType(lexrc);
806 case TC_TITLELATEXNAME:
808 titlename_ = lexrc.getString();
813 string const nofloat = lexrc.getString();
814 floatlist_.erase(nofloat);
818 case TC_OUTLINERNAME:
819 error = !readOutlinerName(lexrc);
823 // Note that this is triggered the first time through the loop unless
824 // we hit a format tag.
825 if (format != LAYOUT_FORMAT)
826 return FORMAT_MISMATCH;
829 // at present, we abort if we encounter an error,
830 // so there is no point continuing.
837 if (defaultlayout_.empty()) {
838 LYXERR0("Error: Textclass '" << name_
839 << "' is missing a defaultstyle.");
843 // Try to erase "stdinsets" from the provides_ set.
845 // Provides stdinsets 1
846 // declaration simply tells us that the standard insets have been
847 // defined. (It's found in stdinsets.inc but could also be used in
848 // user-defined files.) There isn't really any such package. So we
849 // might as well go ahead and erase it.
850 // If we do not succeed, then it was not there, which means that
851 // the textclass did not provide the definitions of the standard
852 // insets. So we need to try to load them.
853 int erased = provides_.erase("stdinsets");
855 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
858 frontend::Alert::warning(_("Missing File"),
859 _("Could not find stdinsets.inc! This may lead to data loss!"));
861 } else if (!read(tmp, MERGE)) {
862 frontend::Alert::warning(_("Corrupt File"),
863 _("Could not read stdinsets.inc! This may lead to data loss!"));
868 min_toclevel_ = Layout::NOT_IN_TOC;
869 max_toclevel_ = Layout::NOT_IN_TOC;
870 const_iterator lit = begin();
871 const_iterator len = end();
872 for (; lit != len; ++lit) {
873 int const toclevel = lit->toclevel;
874 if (toclevel != Layout::NOT_IN_TOC) {
875 if (min_toclevel_ == Layout::NOT_IN_TOC)
876 min_toclevel_ = toclevel;
878 min_toclevel_ = min(min_toclevel_, toclevel);
879 max_toclevel_ = max(max_toclevel_, toclevel);
882 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
883 << ", maximum is " << max_toclevel_);
885 return (error ? ERROR : OK);
889 void TextClass::readTitleType(Lexer & lexrc)
891 LexerKeyword titleTypeTags[] = {
892 { "commandafter", TITLE_COMMAND_AFTER },
893 { "environment", TITLE_ENVIRONMENT }
896 PushPopHelper pph(lexrc, titleTypeTags);
898 int le = lexrc.lex();
900 case Lexer::LEX_UNDEF:
901 lexrc.printError("Unknown output type `$$Token'");
903 case TITLE_COMMAND_AFTER:
904 case TITLE_ENVIRONMENT:
905 titletype_ = static_cast<TitleLatexType>(le);
908 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
914 void TextClass::readOutputType(Lexer & lexrc)
916 LexerKeyword outputTypeTags[] = {
917 { "docbook", DOCBOOK },
919 { "literate", LITERATE }
922 PushPopHelper pph(lexrc, outputTypeTags);
924 int le = lexrc.lex();
926 case Lexer::LEX_UNDEF:
927 lexrc.printError("Unknown output type `$$Token'");
932 outputType_ = static_cast<OutputType>(le);
935 LYXERR0("Unhandled value " << le);
941 void TextClass::readClassOptions(Lexer & lexrc)
951 LexerKeyword classOptionsTags[] = {
953 {"fontsize", CO_FONTSIZE },
954 {"header", CO_HEADER },
955 {"other", CO_OTHER },
956 {"pagestyle", CO_PAGESTYLE }
959 lexrc.pushTable(classOptionsTags);
961 while (!getout && lexrc.isOK()) {
962 int le = lexrc.lex();
964 case Lexer::LEX_UNDEF:
965 lexrc.printError("Unknown ClassOption tag `$$Token'");
973 opt_fontsize_ = rtrim(lexrc.getString());
977 opt_pagestyle_ = rtrim(lexrc.getString());
981 if (options_.empty())
982 options_ = lexrc.getString();
984 options_ += ',' + lexrc.getString();
988 class_header_ = subst(lexrc.getString(), """, "\"");
999 bool TextClass::readCiteEngine(Lexer & lexrc)
1001 int const type = readCiteEngineType(lexrc);
1002 if (type & ENGINE_TYPE_AUTHORYEAR)
1003 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1004 if (type & ENGINE_TYPE_NUMERICAL)
1005 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1006 if (type & ENGINE_TYPE_DEFAULT)
1007 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1009 bool getout = false;
1010 while (!getout && lexrc.isOK()) {
1012 def = lexrc.getString();
1013 def = subst(def, " ", "");
1014 def = subst(def, "\t", "");
1015 if (compare_ascii_no_case(def, "end") == 0) {
1021 char ichar = def[0];
1025 cs.forceUpperCase = true;
1029 size_t const n = def.size();
1030 for (size_t i = 0; i != n; ++i) {
1033 cs.fullAuthorList = true;
1034 else if (ichar == '[' && cs.textAfter)
1035 cs.textBefore = true;
1036 else if (ichar == '[')
1037 cs.textAfter = true;
1038 else if (ichar != ']')
1043 if (type & ENGINE_TYPE_AUTHORYEAR)
1044 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1045 if (type & ENGINE_TYPE_NUMERICAL)
1046 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1047 if (type & ENGINE_TYPE_DEFAULT)
1048 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1054 int TextClass::readCiteEngineType(Lexer & lexrc) const
1056 LATTEST(ENGINE_TYPE_DEFAULT ==
1057 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1058 if (!lexrc.next()) {
1059 lexrc.printError("No cite engine type given for token: `$$Token'.");
1060 return ENGINE_TYPE_DEFAULT;
1062 string const type = rtrim(lexrc.getString());
1063 if (compare_ascii_no_case(type, "authoryear") == 0)
1064 return ENGINE_TYPE_AUTHORYEAR;
1065 else if (compare_ascii_no_case(type, "numerical") == 0)
1066 return ENGINE_TYPE_NUMERICAL;
1067 else if (compare_ascii_no_case(type, "default") != 0) {
1068 string const s = "Unknown cite engine type `" + type
1069 + "' given for token: `$$Token',";
1070 lexrc.printError(s);
1072 return ENGINE_TYPE_DEFAULT;
1076 bool TextClass::readCiteFormat(Lexer & lexrc)
1078 int const type = readCiteEngineType(lexrc);
1081 while (lexrc.isOK()) {
1083 etype = lexrc.getString();
1084 if (compare_ascii_no_case(etype, "end") == 0)
1089 definition = lexrc.getString();
1090 char initchar = etype[0];
1091 if (initchar == '#')
1093 if (initchar == '!' || initchar == '_') {
1094 if (type & ENGINE_TYPE_AUTHORYEAR)
1095 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1096 if (type & ENGINE_TYPE_NUMERICAL)
1097 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1098 if (type & ENGINE_TYPE_DEFAULT)
1099 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1101 if (type & ENGINE_TYPE_AUTHORYEAR)
1102 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1103 if (type & ENGINE_TYPE_NUMERICAL)
1104 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1105 if (type & ENGINE_TYPE_DEFAULT)
1106 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1113 bool TextClass::readFloat(Lexer & lexrc)
1130 FT_ALLOWED_PLACEMENT,
1136 LexerKeyword floatTags[] = {
1137 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1138 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1139 { "allowswide", FT_ALLOWS_WIDE },
1141 { "extension", FT_EXT },
1142 { "guiname", FT_NAME },
1143 { "htmlattr", FT_HTMLATTR },
1144 { "htmlstyle", FT_HTMLSTYLE },
1145 { "htmltag", FT_HTMLTAG },
1146 { "ispredefined", FT_PREDEFINED },
1147 { "listcommand", FT_LISTCOMMAND },
1148 { "listname", FT_LISTNAME },
1149 { "numberwithin", FT_WITHIN },
1150 { "placement", FT_PLACEMENT },
1151 { "refprefix", FT_REFPREFIX },
1152 { "style", FT_STYLE },
1153 { "type", FT_TYPE },
1154 { "usesfloatpkg", FT_USESFLOAT }
1157 lexrc.pushTable(floatTags);
1161 docstring htmlstyle;
1167 string allowed_placement = "!htbpH";
1172 bool usesfloat = true;
1173 bool ispredefined = false;
1174 bool allowswide = true;
1175 bool allowssideways = true;
1177 bool getout = false;
1178 while (!getout && lexrc.isOK()) {
1179 int le = lexrc.lex();
1181 case Lexer::LEX_UNDEF:
1182 lexrc.printError("Unknown float tag `$$Token'");
1190 type = lexrc.getString();
1191 if (floatlist_.typeExist(type)) {
1192 Floating const & fl = floatlist_.getType(type);
1193 placement = fl.placement();
1195 within = fl.within();
1198 listname = fl.listName();
1199 usesfloat = fl.usesFloatPkg();
1200 ispredefined = fl.isPredefined();
1201 listcommand = fl.listCommand();
1202 refprefix = fl.refPrefix();
1207 name = lexrc.getString();
1211 placement = lexrc.getString();
1213 case FT_ALLOWED_PLACEMENT:
1215 allowed_placement = lexrc.getString();
1219 ext = lexrc.getString();
1223 within = lexrc.getString();
1224 if (within == "none")
1229 style = lexrc.getString();
1231 case FT_LISTCOMMAND:
1233 listcommand = lexrc.getString();
1237 refprefix = lexrc.getString();
1241 listname = lexrc.getString();
1245 usesfloat = lexrc.getBool();
1249 ispredefined = lexrc.getBool();
1251 case FT_ALLOWS_SIDEWAYS:
1253 allowssideways = lexrc.getBool();
1255 case FT_ALLOWS_WIDE:
1257 allowswide = lexrc.getBool();
1261 htmlattr = lexrc.getString();
1265 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1269 htmltag = lexrc.getString();
1279 // Here we have a full float if getout == true
1281 if (!usesfloat && listcommand.empty()) {
1282 // if this float uses the same auxfile as an existing one,
1283 // there is no need for it to provide a list command.
1284 FloatList::const_iterator it = floatlist_.begin();
1285 FloatList::const_iterator en = floatlist_.end();
1286 bool found_ext = false;
1287 for (; it != en; ++it) {
1288 if (it->second.ext() == ext) {
1294 LYXERR0("The layout does not provide a list command " <<
1295 "for the float `" << type << "'. LyX will " <<
1296 "not be able to produce a float list.");
1298 Floating fl(type, placement, ext, within, style, name,
1299 listname, listcommand, refprefix, allowed_placement,
1300 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1301 allowswide, allowssideways);
1302 floatlist_.newFloat(fl);
1303 // each float has its own counter
1304 counters_.newCounter(from_ascii(type), from_ascii(within),
1305 docstring(), docstring());
1306 // also define sub-float counters
1307 docstring const subtype = "sub-" + from_ascii(type);
1308 counters_.newCounter(subtype, from_ascii(type),
1309 "\\alph{" + subtype + "}", docstring());
1315 bool TextClass::readOutlinerName(Lexer & lexrc)
1320 type = lexrc.getString();
1322 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1326 name = lexrc.getDocString();
1328 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1331 outliner_names_[type] = name;
1336 docstring TextClass::outlinerName(std::string const & type) const
1338 std::map<std::string,docstring>::const_iterator const it
1339 = outliner_names_.find(type);
1340 if (it == outliner_names_.end()) {
1341 LYXERR0("Missing OutlinerName for " << type << "!");
1342 return from_utf8(type);
1348 string const & TextClass::prerequisites(string const & sep) const
1350 if (contains(prerequisites_, ',')) {
1351 vector<string> const pres = getVectorFromString(prerequisites_);
1352 prerequisites_ = getStringFromVector(pres, sep);
1354 return prerequisites_;
1358 bool TextClass::hasLayout(docstring const & n) const
1360 docstring const name = n.empty() ? defaultLayoutName() : n;
1362 return find_if(layoutlist_.begin(), layoutlist_.end(),
1363 LayoutNamesEqual(name))
1364 != layoutlist_.end();
1368 bool TextClass::hasInsetLayout(docstring const & n) const
1372 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1373 return it != insetlayoutlist_.end();
1377 Layout const & TextClass::operator[](docstring const & name) const
1379 LATTEST(!name.empty());
1382 find_if(begin(), end(), LayoutNamesEqual(name));
1385 LYXERR0("We failed to find the layout '" << name
1386 << "' in the layout list. You MUST investigate!");
1387 for (const_iterator cit = begin(); cit != end(); ++cit)
1388 lyxerr << " " << to_utf8(cit->name()) << endl;
1390 // We require the name to exist
1391 static const Layout dummy;
1392 LASSERT(false, return dummy);
1399 Layout & TextClass::operator[](docstring const & name)
1401 LATTEST(!name.empty());
1402 // Safe to continue, given what we do below.
1404 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1407 LYXERR0("We failed to find the layout '" << to_utf8(name)
1408 << "' in the layout list. You MUST investigate!");
1409 for (const_iterator cit = begin(); cit != end(); ++cit)
1410 LYXERR0(" " << to_utf8(cit->name()));
1412 // we require the name to exist
1414 // we are here only in release mode
1415 layoutlist_.push_back(createBasicLayout(name, true));
1416 it = find_if(begin(), end(), LayoutNamesEqual(name));
1423 bool TextClass::deleteLayout(docstring const & name)
1425 if (name == defaultLayoutName() || name == plainLayoutName())
1428 LayoutList::iterator it =
1429 remove_if(layoutlist_.begin(), layoutlist_.end(),
1430 LayoutNamesEqual(name));
1432 LayoutList::iterator end = layoutlist_.end();
1433 bool const ret = (it != end);
1434 layoutlist_.erase(it, end);
1439 bool TextClass::deleteInsetLayout(docstring const & name)
1441 return insetlayoutlist_.erase(name);
1445 // Load textclass info if not loaded yet
1446 bool TextClass::load(string const & path) const
1451 // Read style-file, provided path is searched before the system ones
1452 // If path is a file, it is loaded directly.
1453 FileName layout_file(path);
1454 if (!path.empty() && !layout_file.isReadableFile())
1455 layout_file = FileName(addName(path, name_ + ".layout"));
1456 if (layout_file.empty() || !layout_file.exists())
1457 layout_file = libFileSearch("layouts", name_, "layout");
1458 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1461 lyxerr << "Error reading `"
1462 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1463 << "'\n(Check `" << name_
1464 << "')\nCheck your installation and "
1465 "try Options/Reconfigure..."
1473 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1478 layoutlist_.push_back(createBasicLayout(n, true));
1483 string DocumentClass::forcedLayouts() const
1487 const_iterator const e = end();
1488 for (const_iterator i = begin(); i != e; ++i) {
1489 if (i->forcelocal > 0) {
1491 os << "Format " << LAYOUT_FORMAT << '\n';
1501 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1503 // FIXME The fix for the InsetLayout part of 4812 would be here:
1504 // Add the InsetLayout to the document class if it is not found.
1506 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1507 while (!n.empty()) {
1508 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1509 if (cit != cen && cit->first == n) {
1510 if (cit->second.obsoleted_by().empty())
1512 n = cit->second.obsoleted_by();
1513 return insetLayout(n);
1515 // If we have a generic prefix (e.g., "Note:"),
1516 // try if this one alone is found.
1517 size_t i = n.find(':');
1518 if (i == string::npos)
1522 // Layout "name" not found.
1523 return plainInsetLayout();
1527 InsetLayout const & DocumentClass::plainInsetLayout() {
1528 static const InsetLayout plain_insetlayout_;
1529 return plain_insetlayout_;
1533 docstring const & TextClass::defaultLayoutName() const
1535 return defaultlayout_;
1539 Layout const & TextClass::defaultLayout() const
1541 return operator[](defaultLayoutName());
1545 bool TextClass::isDefaultLayout(Layout const & layout) const
1547 return layout.name() == defaultLayoutName();
1551 bool TextClass::isPlainLayout(Layout const & layout) const
1553 return layout.name() == plainLayoutName();
1557 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1559 static Layout * defaultLayout = NULL;
1561 if (defaultLayout) {
1562 defaultLayout->setUnknown(unknown);
1563 defaultLayout->setName(name);
1564 return *defaultLayout;
1567 static char const * s = "Margin Static\n"
1568 "LatexType Paragraph\n"
1571 "AlignPossible Left, Right, Center\n"
1572 "LabelType No_Label\n"
1574 istringstream ss(s);
1575 Lexer lex(textClassTags);
1577 defaultLayout = new Layout;
1578 defaultLayout->setUnknown(unknown);
1579 defaultLayout->setName(name);
1580 if (!readStyle(lex, *defaultLayout)) {
1581 // The only way this happens is because the hardcoded layout above
1585 return *defaultLayout;
1589 DocumentClassPtr getDocumentClass(
1590 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1591 LayoutModuleList const & celist,
1594 DocumentClassPtr doc_class =
1595 DocumentClassPtr(new DocumentClass(baseClass));
1596 LayoutModuleList::const_iterator it = modlist.begin();
1597 LayoutModuleList::const_iterator en = modlist.end();
1598 for (; it != en; ++it) {
1599 string const modName = *it;
1600 LyXModule * lm = theModuleList[modName];
1602 docstring const msg =
1603 bformat(_("The module %1$s has been requested by\n"
1604 "this document but has not been found in the list of\n"
1605 "available modules. If you recently installed it, you\n"
1606 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1608 frontend::Alert::warning(_("Module not available"), msg);
1611 if (!lm->isAvailable() && !clone) {
1612 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1613 docstring const msg =
1614 bformat(_("The module %1$s requires a package that is not\n"
1615 "available in your LaTeX installation, or a converter that\n"
1616 "you have not installed. LaTeX output may not be possible.\n"
1617 "Missing prerequisites:\n"
1619 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1620 from_utf8(modName), prereqs);
1621 frontend::Alert::warning(_("Package not available"), msg, true);
1623 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1624 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1625 docstring const msg =
1626 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1627 frontend::Alert::warning(_("Read Error"), msg);
1631 LayoutModuleList::const_iterator cit = celist.begin();
1632 LayoutModuleList::const_iterator cen = celist.end();
1633 for (; cit != cen; ++cit) {
1634 string const ceName = *cit;
1635 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1637 docstring const msg =
1638 bformat(_("The cite engine %1$s has been requested by\n"
1639 "this document but has not been found in the list of\n"
1640 "available engines. If you recently installed it, you\n"
1641 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1643 frontend::Alert::warning(_("Cite Engine not available"), msg);
1646 if (!ce->isAvailable() && !clone) {
1647 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1648 docstring const msg =
1649 bformat(_("The cite engine %1$s requires a package that is not\n"
1650 "available in your LaTeX installation, or a converter that\n"
1651 "you have not installed. LaTeX output may not be possible.\n"
1652 "Missing prerequisites:\n"
1654 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1655 from_utf8(ceName), prereqs);
1656 frontend::Alert::warning(_("Package not available"), msg, true);
1658 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1659 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1660 docstring const msg =
1661 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1662 frontend::Alert::warning(_("Read Error"), msg);
1670 /////////////////////////////////////////////////////////////////////////
1674 /////////////////////////////////////////////////////////////////////////
1676 DocumentClass::DocumentClass(LayoutFile const & tc)
1681 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1683 LayoutList::const_iterator it = layoutlist_.begin();
1684 LayoutList::const_iterator end = layoutlist_.end();
1685 for (; it != end; ++it)
1686 if (it->latexname() == lay)
1692 bool DocumentClass::provides(string const & p) const
1694 return provides_.find(p) != provides_.end();
1698 bool DocumentClass::hasTocLevels() const
1700 return min_toclevel_ != Layout::NOT_IN_TOC;
1704 Layout const & DocumentClass::getTOCLayout() const
1706 // we're going to look for the layout with the minimum toclevel
1707 TextClass::LayoutList::const_iterator lit = begin();
1708 TextClass::LayoutList::const_iterator const len = end();
1709 int minlevel = 1000;
1710 Layout const * lay = NULL;
1711 for (; lit != len; ++lit) {
1712 int const level = lit->toclevel;
1713 // we don't want Part or unnumbered sections
1714 if (level == Layout::NOT_IN_TOC || level < 0
1715 || level >= minlevel || lit->counter.empty())
1722 // hmm. that is very odd, so we'll do our best.
1723 return operator[](defaultLayoutName());
1727 Layout const & DocumentClass::htmlTOCLayout() const
1729 if (html_toc_section_.empty())
1730 html_toc_section_ = getTOCLayout().name();
1731 return operator[](html_toc_section_);
1735 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1736 string const & entry, string const & fallback) const
1738 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1740 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1741 if (itype == cite_formats_.end())
1742 return default_format;
1743 map<string, string>::const_iterator it = itype->second.find(entry);
1744 if (it == itype->second.end() && !fallback.empty())
1745 it = itype->second.find(fallback);
1746 if (it == itype->second.end())
1747 return default_format;
1752 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1753 string const & macro) const
1755 static string empty;
1756 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1757 if (itype == cite_macros_.end())
1759 map<string, string>::const_iterator it = itype->second.find(macro);
1760 if (it == itype->second.end())
1766 vector<string> const DocumentClass::citeCommands(
1767 CiteEngineType const & type) const
1769 vector<CitationStyle> const styles = citeStyles(type);
1770 vector<CitationStyle>::const_iterator it = styles.begin();
1771 vector<CitationStyle>::const_iterator end = styles.end();
1772 vector<string> cmds;
1773 for (; it != end; ++it) {
1774 CitationStyle const cite = *it;
1775 cmds.push_back(cite.cmd);
1781 vector<CitationStyle> const & DocumentClass::citeStyles(
1782 CiteEngineType const & type) const
1784 static vector<CitationStyle> empty;
1785 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1786 if (it == cite_styles_.end())
1792 /////////////////////////////////////////////////////////////////////////
1796 /////////////////////////////////////////////////////////////////////////
1798 ostream & operator<<(ostream & os, PageSides p)