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 = 62; //spitz PassThru for arguments.
67 // Layout format for the current lyx file format. Controls which format is
68 // targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
69 int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
74 class LayoutNamesEqual : public unary_function<Layout, bool> {
76 LayoutNamesEqual(docstring const & name)
79 bool operator()(Layout const & c) const
81 return c.name() == name_;
88 bool layout2layout(FileName const & filename, FileName const & tempfile,
89 int const format = LAYOUT_FORMAT)
91 FileName const script = libFileSearch("scripts", "layout2layout.py");
93 LYXERR0("Could not find layout conversion "
94 "script layout2layout.py.");
98 ostringstream command;
99 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
101 << ' ' << quoteName(filename.toFilesystemEncoding())
102 << ' ' << quoteName(tempfile.toFilesystemEncoding());
103 string const command_str = command.str();
105 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
107 cmd_ret const ret = runCommand(command_str);
108 if (ret.first != 0) {
109 if (format == LAYOUT_FORMAT)
110 LYXERR0("Conversion of layout with layout2layout.py has failed.");
117 string translateReadType(TextClass::ReadType rt)
120 case TextClass::BASECLASS:
122 case TextClass::MERGE:
124 case TextClass::MODULE:
125 return "module file";
126 case TextClass::VALIDATION:
136 // This string should not be translated here,
137 // because it is a layout identifier.
138 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
141 /////////////////////////////////////////////////////////////////////////
145 /////////////////////////////////////////////////////////////////////////
147 TextClass::TextClass()
148 : loaded_(false), tex_class_avail_(false),
149 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
150 opt_pagestyle_("empty|plain|headings|fancy"), pagestyle_("default"),
151 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3),
152 outputType_(LATEX), outputFormat_("latex"),
153 defaultfont_(sane_font),
154 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
155 min_toclevel_(0), max_toclevel_(0),
156 cite_full_author_list_(true)
161 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
163 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
164 if (!lay.read(lexrc, *this)) {
165 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
169 lay.resfont = lay.font;
170 lay.resfont.realize(defaultfont_);
171 lay.reslabelfont = lay.labelfont;
172 lay.reslabelfont.realize(defaultfont_);
173 return true; // no errors
212 TC_ADDTOHTMLPREAMBLE,
229 LexerKeyword textClassTags[] = {
230 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
231 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
232 { "addtopreamble", TC_ADDTOPREAMBLE },
233 { "citeengine", TC_CITEENGINE },
234 { "citeenginetype", TC_CITEENGINETYPE },
235 { "citeformat", TC_CITEFORMAT },
236 { "classoptions", TC_CLASSOPTIONS },
237 { "columns", TC_COLUMNS },
238 { "counter", TC_COUNTER },
239 { "defaultbiblio", TC_DEFAULTBIBLIO },
240 { "defaultfont", TC_DEFAULTFONT },
241 { "defaultmodule", TC_DEFAULTMODULE },
242 { "defaultstyle", TC_DEFAULTSTYLE },
243 { "excludesmodule", TC_EXCLUDESMODULE },
244 { "float", TC_FLOAT },
245 { "format", TC_FORMAT },
246 { "fullauthorlist", TC_FULLAUTHORLIST },
247 { "htmlpreamble", TC_HTMLPREAMBLE },
248 { "htmlstyles", TC_HTMLSTYLES },
249 { "htmltocsection", TC_HTMLTOCSECTION },
250 { "ifcounter", TC_IFCOUNTER },
251 { "input", TC_INPUT },
252 { "insetlayout", TC_INSETLAYOUT },
253 { "leftmargin", TC_LEFTMARGIN },
254 { "modifystyle", TC_MODIFYSTYLE },
255 { "nocounter", TC_NOCOUNTER },
256 { "nofloat", TC_NOFLOAT },
257 { "noinsetlayout", TC_NOINSETLAYOUT },
258 { "nostyle", TC_NOSTYLE },
259 { "outlinername", TC_OUTLINERNAME },
260 { "outputformat", TC_OUTPUTFORMAT },
261 { "outputtype", TC_OUTPUTTYPE },
262 { "packageoptions", TC_PKGOPTS },
263 { "pagestyle", TC_PAGESTYLE },
264 { "preamble", TC_PREAMBLE },
265 { "provides", TC_PROVIDES },
266 { "providesmodule", TC_PROVIDESMODULE },
267 { "providestyle", TC_PROVIDESTYLE },
268 { "requires", TC_REQUIRES },
269 { "rightmargin", TC_RIGHTMARGIN },
270 { "secnumdepth", TC_SECNUMDEPTH },
271 { "sides", TC_SIDES },
272 { "style", TC_STYLE },
273 { "titlelatexname", TC_TITLELATEXNAME },
274 { "titlelatextype", TC_TITLELATEXTYPE },
275 { "tocdepth", TC_TOCDEPTH }
281 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
283 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
284 TempFile tmp("convertXXXXXX.layout");
285 FileName const tempfile = tmp.name();
286 bool success = layout2layout(filename, tempfile);
288 success = readWithoutConv(tempfile, rt) == OK;
293 std::string TextClass::convert(std::string const & str)
295 TempFile tmp1("localXXXXXX.layout");
296 FileName const fn = tmp1.name();
297 ofstream os(fn.toFilesystemEncoding().c_str());
300 TempFile tmp2("convert_localXXXXXX.layout");
301 FileName const tempfile = tmp2.name();
302 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
305 ifstream is(tempfile.toFilesystemEncoding().c_str());
317 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
319 if (!filename.isReadableFile()) {
320 lyxerr << "Cannot read layout file `" << filename << "'."
325 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
326 to_utf8(makeDisplayPath(filename.absFileName())));
328 // Define the plain layout used in table cells, ert, etc. Note that
329 // we do this before loading any layout file, so that classes can
330 // override features of this layout if they should choose to do so.
331 if (rt == BASECLASS && !hasLayout(plain_layout_))
332 layoutlist_.push_back(createBasicLayout(plain_layout_));
334 Lexer lexrc(textClassTags);
335 lexrc.setFile(filename);
336 ReturnValues retval = read(lexrc, rt);
338 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
339 to_utf8(makeDisplayPath(filename.absFileName())));
345 bool TextClass::read(FileName const & filename, ReadType rt)
347 ReturnValues const retval = readWithoutConv(filename, rt);
348 if (retval != FORMAT_MISMATCH)
351 bool const worx = convertLayoutFormat(filename, rt);
353 LYXERR0 ("Unable to convert " << filename <<
354 " to format " << LAYOUT_FORMAT);
359 TextClass::ReturnValues TextClass::validate(std::string const & str)
362 return tc.read(str, VALIDATION);
366 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
368 Lexer lexrc(textClassTags);
369 istringstream is(str);
371 ReturnValues retval = read(lexrc, rt);
373 if (retval != FORMAT_MISMATCH)
376 // write the layout string to a temporary file
377 TempFile tmp("TextClass_read");
378 FileName const tempfile = tmp.name();
379 ofstream os(tempfile.toFilesystemEncoding().c_str());
381 LYXERR0("Unable to create temporary file");
387 // now try to convert it to LAYOUT_FORMAT
388 if (!convertLayoutFormat(tempfile, rt)) {
389 LYXERR0("Unable to convert internal layout information to format "
398 // Reads a textclass structure from file.
399 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
404 // Format of files before the 'Format' tag was introduced
409 while (lexrc.isOK() && !error) {
410 int le = lexrc.lex();
413 case Lexer::LEX_FEOF:
416 case Lexer::LEX_UNDEF:
417 lexrc.printError("Unknown TextClass tag `$$Token'");
425 // used below to track whether we are in an IfStyle or IfCounter tag.
426 bool modifystyle = false;
427 bool providestyle = false;
428 bool ifcounter = false;
430 switch (static_cast<TextClassTags>(le)) {
434 format = lexrc.getInteger();
437 case TC_OUTPUTFORMAT:
439 outputFormat_ = lexrc.getString();
443 readOutputType(lexrc);
444 switch(outputType_) {
446 outputFormat_ = "latex";
449 outputFormat_ = "docbook";
452 outputFormat_ = "literate";
457 case TC_INPUT: // Include file
459 string const inc = lexrc.getString();
460 FileName tmp = libFileSearch("layouts", inc,
464 lexrc.printError("Could not find input file: " + inc);
466 } else if (!read(tmp, MERGE)) {
467 lexrc.printError("Error reading input file: " + tmp.absFileName());
473 case TC_DEFAULTSTYLE:
475 docstring const name = from_utf8(subst(lexrc.getString(),
477 defaultlayout_ = name;
484 case TC_PROVIDESTYLE:
485 // if modifystyle is true, then we got here by falling through
486 // so we are not in an ProvideStyle block
492 lexrc.printError("No name given for style: `$$Token'.");
496 docstring const name = from_utf8(subst(lexrc.getString(),
499 string s = "Could not read name for style: `$$Token' "
500 + lexrc.getString() + " is probably not valid UTF-8!";
503 // Since we couldn't read the name, we just scan the rest
504 // of the style and discard it.
505 error = !readStyle(lexrc, lay);
509 bool const have_layout = hasLayout(name);
511 // If the layout already exists, then we want to add it to
512 // the existing layout, as long as we are not in an ProvideStyle
514 if (have_layout && !providestyle) {
515 Layout & lay = operator[](name);
516 error = !readStyle(lexrc, lay);
518 // If the layout does not exist, then we want to create a new
519 // one, but not if we are in a ModifyStyle block.
520 else if (!have_layout && !modifystyle) {
522 layout.setName(name);
523 error = !readStyle(lexrc, layout);
525 layoutlist_.push_back(layout);
527 if (defaultlayout_.empty()) {
528 // We do not have a default layout yet, so we choose
529 // the first layout we encounter.
530 defaultlayout_ = name;
533 // There are two ways to get here:
534 // (i) The layout exists but we are in an ProvideStyle block
535 // (ii) The layout doesn't exist, but we are in an ModifyStyle
537 // Either way, we just scan the rest and discard it
540 // false positive from coverity
541 // coverity[CHECKED_RETURN]
542 readStyle(lexrc, lay);
549 docstring const style = from_utf8(subst(lexrc.getString(),
551 if (!deleteLayout(style))
552 lyxerr << "Cannot delete style `"
553 << to_utf8(style) << '\'' << endl;
557 case TC_NOINSETLAYOUT:
559 docstring const style = from_utf8(subst(lexrc.getString(),
561 if (!deleteInsetLayout(style))
562 LYXERR0("Style `" << style << "' cannot be removed\n"
563 "because it was not found!");
569 columns_ = lexrc.getInteger();
574 switch (lexrc.getInteger()) {
575 case 1: sides_ = OneSide; break;
576 case 2: sides_ = TwoSides; break;
578 lyxerr << "Impossible number of page"
579 " sides, setting to one."
589 pagestyle_ = rtrim(lexrc.getString());
593 defaultfont_ = lyxRead(lexrc);
594 if (!defaultfont_.resolved()) {
595 lexrc.printError("Warning: defaultfont should "
596 "be fully instantiated!");
597 defaultfont_.realize(sane_font);
603 secnumdepth_ = lexrc.getInteger();
608 tocdepth_ = lexrc.getInteger();
611 // First step to support options
612 case TC_CLASSOPTIONS:
613 readClassOptions(lexrc);
617 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
620 case TC_HTMLPREAMBLE:
621 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
625 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
628 case TC_HTMLTOCSECTION:
629 html_toc_section_ = from_utf8(trim(lexrc.getString()));
632 case TC_ADDTOPREAMBLE:
633 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
636 case TC_ADDTOHTMLPREAMBLE:
637 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
640 case TC_ADDTOHTMLSTYLES:
641 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
646 string const feature = lexrc.getString();
648 if (lexrc.getInteger())
649 provides_.insert(feature);
651 provides_.erase(feature);
657 vector<string> const req
658 = getVectorFromString(lexrc.getString());
659 requires_.insert(req.begin(), req.end());
665 string const pkg = lexrc.getString();
667 string const options = lexrc.getString();
668 package_options_[pkg] = options;
672 case TC_DEFAULTMODULE: {
674 string const module = lexrc.getString();
675 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
676 default_modules_.push_back(module);
680 case TC_PROVIDESMODULE: {
682 string const module = lexrc.getString();
683 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
684 provided_modules_.push_back(module);
688 case TC_EXCLUDESMODULE: {
690 string const module = lexrc.getString();
691 // modules already have their own way to exclude other modules
693 LYXERR0("ExcludesModule tag cannot be used in a module!");
696 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
697 excluded_modules_.push_back(module);
701 case TC_LEFTMARGIN: // left margin type
703 leftmargin_ = lexrc.getDocString();
706 case TC_RIGHTMARGIN: // right margin type
708 rightmargin_ = lexrc.getDocString();
711 case TC_INSETLAYOUT: {
713 lexrc.printError("No name given for InsetLayout: `$$Token'.");
717 docstring const name = subst(lexrc.getDocString(), '_', ' ');
719 string s = "Could not read name for InsetLayout: `$$Token' "
720 + lexrc.getString() + " is probably not valid UTF-8!";
723 // Since we couldn't read the name, we just scan the rest
724 // of the style and discard it.
725 il.read(lexrc, *this);
726 // Let's try to continue rather than abort.
728 } else if (hasInsetLayout(name)) {
729 InsetLayout & il = insetlayoutlist_[name];
730 error = !il.read(lexrc, *this);
734 error = !il.read(lexrc, *this);
736 insetlayoutlist_[name] = il;
742 error = !readFloat(lexrc);
746 error = !readCiteEngine(lexrc);
749 case TC_CITEENGINETYPE:
751 opt_enginetype_ = rtrim(lexrc.getString());
755 error = !readCiteFormat(lexrc);
758 case TC_DEFAULTBIBLIO:
760 cite_default_biblio_style_ = rtrim(lexrc.getString());
763 case TC_FULLAUTHORLIST:
765 cite_full_author_list_ &= lexrc.getBool();
770 docstring const cnt = lexrc.getDocString();
771 if (!counters_.remove(cnt))
772 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
781 docstring const name = lexrc.getDocString();
783 string s = "Could not read name for counter: `$$Token' "
784 + lexrc.getString() + " is probably not valid UTF-8!";
785 lexrc.printError(s.c_str());
787 // Since we couldn't read the name, we just scan the rest
791 error = !counters_.read(lexrc, name, !ifcounter);
794 lexrc.printError("No name given for style: `$$Token'.");
799 case TC_TITLELATEXTYPE:
800 readTitleType(lexrc);
803 case TC_TITLELATEXNAME:
805 titlename_ = lexrc.getString();
810 string const nofloat = lexrc.getString();
811 floatlist_.erase(nofloat);
815 case TC_OUTLINERNAME:
816 error = !readOutlinerName(lexrc);
820 // Note that this is triggered the first time through the loop unless
821 // we hit a format tag.
822 if (format != LAYOUT_FORMAT)
823 return FORMAT_MISMATCH;
826 // at present, we abort if we encounter an error,
827 // so there is no point continuing.
834 if (defaultlayout_.empty()) {
835 LYXERR0("Error: Textclass '" << name_
836 << "' is missing a defaultstyle.");
840 // Try to erase "stdinsets" from the provides_ set.
842 // Provides stdinsets 1
843 // declaration simply tells us that the standard insets have been
844 // defined. (It's found in stdinsets.inc but could also be used in
845 // user-defined files.) There isn't really any such package. So we
846 // might as well go ahead and erase it.
847 // If we do not succeed, then it was not there, which means that
848 // the textclass did not provide the definitions of the standard
849 // insets. So we need to try to load them.
850 int erased = provides_.erase("stdinsets");
852 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
855 frontend::Alert::warning(_("Missing File"),
856 _("Could not find stdinsets.inc! This may lead to data loss!"));
858 } else if (!read(tmp, MERGE)) {
859 frontend::Alert::warning(_("Corrupt File"),
860 _("Could not read stdinsets.inc! This may lead to data loss!"));
865 min_toclevel_ = Layout::NOT_IN_TOC;
866 max_toclevel_ = Layout::NOT_IN_TOC;
867 const_iterator lit = begin();
868 const_iterator len = end();
869 for (; lit != len; ++lit) {
870 int const toclevel = lit->toclevel;
871 if (toclevel != Layout::NOT_IN_TOC) {
872 if (min_toclevel_ == Layout::NOT_IN_TOC)
873 min_toclevel_ = toclevel;
875 min_toclevel_ = min(min_toclevel_, toclevel);
876 max_toclevel_ = max(max_toclevel_, toclevel);
879 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
880 << ", maximum is " << max_toclevel_);
882 return (error ? ERROR : OK);
886 void TextClass::readTitleType(Lexer & lexrc)
888 LexerKeyword titleTypeTags[] = {
889 { "commandafter", TITLE_COMMAND_AFTER },
890 { "environment", TITLE_ENVIRONMENT }
893 PushPopHelper pph(lexrc, titleTypeTags);
895 int le = lexrc.lex();
897 case Lexer::LEX_UNDEF:
898 lexrc.printError("Unknown output type `$$Token'");
900 case TITLE_COMMAND_AFTER:
901 case TITLE_ENVIRONMENT:
902 titletype_ = static_cast<TitleLatexType>(le);
905 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
911 void TextClass::readOutputType(Lexer & lexrc)
913 LexerKeyword outputTypeTags[] = {
914 { "docbook", DOCBOOK },
916 { "literate", LITERATE }
919 PushPopHelper pph(lexrc, outputTypeTags);
921 int le = lexrc.lex();
923 case Lexer::LEX_UNDEF:
924 lexrc.printError("Unknown output type `$$Token'");
929 outputType_ = static_cast<OutputType>(le);
932 LYXERR0("Unhandled value " << le);
938 void TextClass::readClassOptions(Lexer & lexrc)
948 LexerKeyword classOptionsTags[] = {
950 {"fontsize", CO_FONTSIZE },
951 {"header", CO_HEADER },
952 {"other", CO_OTHER },
953 {"pagestyle", CO_PAGESTYLE }
956 lexrc.pushTable(classOptionsTags);
958 while (!getout && lexrc.isOK()) {
959 int le = lexrc.lex();
961 case Lexer::LEX_UNDEF:
962 lexrc.printError("Unknown ClassOption tag `$$Token'");
970 opt_fontsize_ = rtrim(lexrc.getString());
974 opt_pagestyle_ = rtrim(lexrc.getString());
978 if (options_.empty())
979 options_ = lexrc.getString();
981 options_ += ',' + lexrc.getString();
985 class_header_ = subst(lexrc.getString(), """, "\"");
996 bool TextClass::readCiteEngine(Lexer & lexrc)
998 int const type = readCiteEngineType(lexrc);
999 if (type & ENGINE_TYPE_AUTHORYEAR)
1000 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1001 if (type & ENGINE_TYPE_NUMERICAL)
1002 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1003 if (type & ENGINE_TYPE_DEFAULT)
1004 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1006 bool getout = false;
1007 while (!getout && lexrc.isOK()) {
1009 def = lexrc.getString();
1010 def = subst(def, " ", "");
1011 def = subst(def, "\t", "");
1012 if (compare_ascii_no_case(def, "end") == 0) {
1018 char ichar = def[0];
1022 cs.forceUpperCase = true;
1026 size_t const n = def.size();
1027 for (size_t i = 0; i != n; ++i) {
1030 cs.fullAuthorList = true;
1031 else if (ichar == '[' && cs.textAfter)
1032 cs.textBefore = true;
1033 else if (ichar == '[')
1034 cs.textAfter = true;
1035 else if (ichar != ']')
1040 if (type & ENGINE_TYPE_AUTHORYEAR)
1041 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1042 if (type & ENGINE_TYPE_NUMERICAL)
1043 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1044 if (type & ENGINE_TYPE_DEFAULT)
1045 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1051 int TextClass::readCiteEngineType(Lexer & lexrc) const
1053 LATTEST(ENGINE_TYPE_DEFAULT ==
1054 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1055 if (!lexrc.next()) {
1056 lexrc.printError("No cite engine type given for token: `$$Token'.");
1057 return ENGINE_TYPE_DEFAULT;
1059 string const type = rtrim(lexrc.getString());
1060 if (compare_ascii_no_case(type, "authoryear") == 0)
1061 return ENGINE_TYPE_AUTHORYEAR;
1062 else if (compare_ascii_no_case(type, "numerical") == 0)
1063 return ENGINE_TYPE_NUMERICAL;
1064 else if (compare_ascii_no_case(type, "default") != 0) {
1065 string const s = "Unknown cite engine type `" + type
1066 + "' given for token: `$$Token',";
1067 lexrc.printError(s);
1069 return ENGINE_TYPE_DEFAULT;
1073 bool TextClass::readCiteFormat(Lexer & lexrc)
1075 int const type = readCiteEngineType(lexrc);
1078 while (lexrc.isOK()) {
1080 etype = lexrc.getString();
1081 if (compare_ascii_no_case(etype, "end") == 0)
1086 definition = lexrc.getString();
1087 char initchar = etype[0];
1088 if (initchar == '#')
1090 if (initchar == '!' || initchar == '_') {
1091 if (type & ENGINE_TYPE_AUTHORYEAR)
1092 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1093 if (type & ENGINE_TYPE_NUMERICAL)
1094 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1095 if (type & ENGINE_TYPE_DEFAULT)
1096 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1098 if (type & ENGINE_TYPE_AUTHORYEAR)
1099 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1100 if (type & ENGINE_TYPE_NUMERICAL)
1101 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1102 if (type & ENGINE_TYPE_DEFAULT)
1103 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1110 bool TextClass::readFloat(Lexer & lexrc)
1127 FT_ALLOWED_PLACEMENT,
1133 LexerKeyword floatTags[] = {
1134 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1135 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1136 { "allowswide", FT_ALLOWS_WIDE },
1138 { "extension", FT_EXT },
1139 { "guiname", FT_NAME },
1140 { "htmlattr", FT_HTMLATTR },
1141 { "htmlstyle", FT_HTMLSTYLE },
1142 { "htmltag", FT_HTMLTAG },
1143 { "ispredefined", FT_PREDEFINED },
1144 { "listcommand", FT_LISTCOMMAND },
1145 { "listname", FT_LISTNAME },
1146 { "numberwithin", FT_WITHIN },
1147 { "placement", FT_PLACEMENT },
1148 { "refprefix", FT_REFPREFIX },
1149 { "style", FT_STYLE },
1150 { "type", FT_TYPE },
1151 { "usesfloatpkg", FT_USESFLOAT }
1154 lexrc.pushTable(floatTags);
1158 docstring htmlstyle;
1164 string allowed_placement = "!htbpH";
1169 bool usesfloat = true;
1170 bool ispredefined = false;
1171 bool allowswide = true;
1172 bool allowssideways = true;
1174 bool getout = false;
1175 while (!getout && lexrc.isOK()) {
1176 int le = lexrc.lex();
1178 case Lexer::LEX_UNDEF:
1179 lexrc.printError("Unknown float tag `$$Token'");
1187 type = lexrc.getString();
1188 if (floatlist_.typeExist(type)) {
1189 Floating const & fl = floatlist_.getType(type);
1190 placement = fl.placement();
1192 within = fl.within();
1195 listname = fl.listName();
1196 usesfloat = fl.usesFloatPkg();
1197 ispredefined = fl.isPredefined();
1198 listcommand = fl.listCommand();
1199 refprefix = fl.refPrefix();
1204 name = lexrc.getString();
1208 placement = lexrc.getString();
1210 case FT_ALLOWED_PLACEMENT:
1212 allowed_placement = lexrc.getString();
1216 ext = lexrc.getString();
1220 within = lexrc.getString();
1221 if (within == "none")
1226 style = lexrc.getString();
1228 case FT_LISTCOMMAND:
1230 listcommand = lexrc.getString();
1234 refprefix = lexrc.getString();
1238 listname = lexrc.getString();
1242 usesfloat = lexrc.getBool();
1246 ispredefined = lexrc.getBool();
1248 case FT_ALLOWS_SIDEWAYS:
1250 allowssideways = lexrc.getBool();
1252 case FT_ALLOWS_WIDE:
1254 allowswide = lexrc.getBool();
1258 htmlattr = lexrc.getString();
1262 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1266 htmltag = lexrc.getString();
1276 // Here we have a full float if getout == true
1278 if (!usesfloat && listcommand.empty()) {
1279 // if this float uses the same auxfile as an existing one,
1280 // there is no need for it to provide a list command.
1281 FloatList::const_iterator it = floatlist_.begin();
1282 FloatList::const_iterator en = floatlist_.end();
1283 bool found_ext = false;
1284 for (; it != en; ++it) {
1285 if (it->second.ext() == ext) {
1291 LYXERR0("The layout does not provide a list command " <<
1292 "for the float `" << type << "'. LyX will " <<
1293 "not be able to produce a float list.");
1295 Floating fl(type, placement, ext, within, style, name,
1296 listname, listcommand, refprefix, allowed_placement,
1297 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1298 allowswide, allowssideways);
1299 floatlist_.newFloat(fl);
1300 // each float has its own counter
1301 counters_.newCounter(from_ascii(type), from_ascii(within),
1302 docstring(), docstring());
1303 // also define sub-float counters
1304 docstring const subtype = "sub-" + from_ascii(type);
1305 counters_.newCounter(subtype, from_ascii(type),
1306 "\\alph{" + subtype + "}", docstring());
1312 bool TextClass::readOutlinerName(Lexer & lexrc)
1317 type = lexrc.getString();
1319 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1323 name = lexrc.getDocString();
1325 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1328 outliner_names_[type] = name;
1333 docstring TextClass::outlinerName(std::string const & type) const
1335 std::map<std::string,docstring>::const_iterator const it
1336 = outliner_names_.find(type);
1337 if (it == outliner_names_.end()) {
1338 LYXERR0("Missing OutlinerName for " << type << "!");
1339 return from_utf8(type);
1345 string const & TextClass::prerequisites(string const & sep) const
1347 if (contains(prerequisites_, ',')) {
1348 vector<string> const pres = getVectorFromString(prerequisites_);
1349 prerequisites_ = getStringFromVector(pres, sep);
1351 return prerequisites_;
1355 bool TextClass::hasLayout(docstring const & n) const
1357 docstring const name = n.empty() ? defaultLayoutName() : n;
1359 return find_if(layoutlist_.begin(), layoutlist_.end(),
1360 LayoutNamesEqual(name))
1361 != layoutlist_.end();
1365 bool TextClass::hasInsetLayout(docstring const & n) const
1369 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1370 return it != insetlayoutlist_.end();
1374 Layout const & TextClass::operator[](docstring const & name) const
1376 LATTEST(!name.empty());
1379 find_if(begin(), end(), LayoutNamesEqual(name));
1382 LYXERR0("We failed to find the layout '" << name
1383 << "' in the layout list. You MUST investigate!");
1384 for (const_iterator cit = begin(); cit != end(); ++cit)
1385 lyxerr << " " << to_utf8(cit->name()) << endl;
1387 // We require the name to exist
1388 static const Layout dummy;
1389 LASSERT(false, return dummy);
1396 Layout & TextClass::operator[](docstring const & name)
1398 LATTEST(!name.empty());
1399 // Safe to continue, given what we do below.
1401 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1404 LYXERR0("We failed to find the layout '" << to_utf8(name)
1405 << "' in the layout list. You MUST investigate!");
1406 for (const_iterator cit = begin(); cit != end(); ++cit)
1407 LYXERR0(" " << to_utf8(cit->name()));
1409 // we require the name to exist
1411 // we are here only in release mode
1412 layoutlist_.push_back(createBasicLayout(name, true));
1413 it = find_if(begin(), end(), LayoutNamesEqual(name));
1420 bool TextClass::deleteLayout(docstring const & name)
1422 if (name == defaultLayoutName() || name == plainLayoutName())
1425 LayoutList::iterator it =
1426 remove_if(layoutlist_.begin(), layoutlist_.end(),
1427 LayoutNamesEqual(name));
1429 LayoutList::iterator end = layoutlist_.end();
1430 bool const ret = (it != end);
1431 layoutlist_.erase(it, end);
1436 bool TextClass::deleteInsetLayout(docstring const & name)
1438 return insetlayoutlist_.erase(name);
1442 // Load textclass info if not loaded yet
1443 bool TextClass::load(string const & path) const
1448 // Read style-file, provided path is searched before the system ones
1449 // If path is a file, it is loaded directly.
1450 FileName layout_file(path);
1451 if (!path.empty() && !layout_file.isReadableFile())
1452 layout_file = FileName(addName(path, name_ + ".layout"));
1453 if (layout_file.empty() || !layout_file.exists())
1454 layout_file = libFileSearch("layouts", name_, "layout");
1455 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1458 lyxerr << "Error reading `"
1459 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1460 << "'\n(Check `" << name_
1461 << "')\nCheck your installation and "
1462 "try Options/Reconfigure..."
1470 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1475 layoutlist_.push_back(createBasicLayout(n, true));
1480 string DocumentClass::forcedLayouts() const
1484 const_iterator const e = end();
1485 for (const_iterator i = begin(); i != e; ++i) {
1486 if (i->forcelocal > 0) {
1488 os << "Format " << LAYOUT_FORMAT << '\n';
1498 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1500 // FIXME The fix for the InsetLayout part of 4812 would be here:
1501 // Add the InsetLayout to the document class if it is not found.
1503 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1504 while (!n.empty()) {
1505 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1506 if (cit != cen && cit->first == n) {
1507 if (cit->second.obsoleted_by().empty())
1509 n = cit->second.obsoleted_by();
1510 return insetLayout(n);
1512 // If we have a generic prefix (e.g., "Note:"),
1513 // try if this one alone is found.
1514 size_t i = n.find(':');
1515 if (i == string::npos)
1519 // Layout "name" not found.
1520 return plainInsetLayout();
1524 InsetLayout const & DocumentClass::plainInsetLayout() {
1525 static const InsetLayout plain_insetlayout_;
1526 return plain_insetlayout_;
1530 docstring const & TextClass::defaultLayoutName() const
1532 return defaultlayout_;
1536 Layout const & TextClass::defaultLayout() const
1538 return operator[](defaultLayoutName());
1542 bool TextClass::isDefaultLayout(Layout const & layout) const
1544 return layout.name() == defaultLayoutName();
1548 bool TextClass::isPlainLayout(Layout const & layout) const
1550 return layout.name() == plainLayoutName();
1554 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1556 static Layout * defaultLayout = NULL;
1558 if (defaultLayout) {
1559 defaultLayout->setUnknown(unknown);
1560 defaultLayout->setName(name);
1561 return *defaultLayout;
1564 static char const * s = "Margin Static\n"
1565 "LatexType Paragraph\n"
1568 "AlignPossible Left, Right, Center\n"
1569 "LabelType No_Label\n"
1571 istringstream ss(s);
1572 Lexer lex(textClassTags);
1574 defaultLayout = new Layout;
1575 defaultLayout->setUnknown(unknown);
1576 defaultLayout->setName(name);
1577 if (!readStyle(lex, *defaultLayout)) {
1578 // The only way this happens is because the hardcoded layout above
1582 return *defaultLayout;
1586 DocumentClassPtr getDocumentClass(
1587 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1590 DocumentClassPtr doc_class =
1591 DocumentClassPtr(new DocumentClass(baseClass));
1592 LayoutModuleList::const_iterator it = modlist.begin();
1593 LayoutModuleList::const_iterator en = modlist.end();
1594 for (; it != en; ++it) {
1595 string const modName = *it;
1596 LyXModule * lm = theModuleList[modName];
1598 docstring const msg =
1599 bformat(_("The module %1$s has been requested by\n"
1600 "this document but has not been found in the list of\n"
1601 "available modules. If you recently installed it, you\n"
1602 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1604 frontend::Alert::warning(_("Module not available"), msg);
1607 if (!lm->isAvailable() && !clone) {
1608 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1609 docstring const msg =
1610 bformat(_("The module %1$s requires a package that is not\n"
1611 "available in your LaTeX installation, or a converter that\n"
1612 "you have not installed. LaTeX output may not be possible.\n"
1613 "Missing prerequisites:\n"
1615 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1616 from_utf8(modName), prereqs);
1617 frontend::Alert::warning(_("Package not available"), msg, true);
1619 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1620 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1621 docstring const msg =
1622 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1623 frontend::Alert::warning(_("Read Error"), msg);
1630 /////////////////////////////////////////////////////////////////////////
1634 /////////////////////////////////////////////////////////////////////////
1636 DocumentClass::DocumentClass(LayoutFile const & tc)
1641 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1643 LayoutList::const_iterator it = layoutlist_.begin();
1644 LayoutList::const_iterator end = layoutlist_.end();
1645 for (; it != end; ++it)
1646 if (it->latexname() == lay)
1652 bool DocumentClass::provides(string const & p) const
1654 return provides_.find(p) != provides_.end();
1658 bool DocumentClass::hasTocLevels() const
1660 return min_toclevel_ != Layout::NOT_IN_TOC;
1664 Layout const & DocumentClass::getTOCLayout() const
1666 // we're going to look for the layout with the minimum toclevel
1667 TextClass::LayoutList::const_iterator lit = begin();
1668 TextClass::LayoutList::const_iterator const len = end();
1669 int minlevel = 1000;
1670 Layout const * lay = NULL;
1671 for (; lit != len; ++lit) {
1672 int const level = lit->toclevel;
1673 // we don't want Part or unnumbered sections
1674 if (level == Layout::NOT_IN_TOC || level < 0
1675 || level >= minlevel || lit->counter.empty())
1682 // hmm. that is very odd, so we'll do our best.
1683 return operator[](defaultLayoutName());
1687 Layout const & DocumentClass::htmlTOCLayout() const
1689 if (html_toc_section_.empty())
1690 html_toc_section_ = getTOCLayout().name();
1691 return operator[](html_toc_section_);
1695 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1696 string const & entry, string const & fallback) const
1698 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1700 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1701 if (itype == cite_formats_.end())
1702 return default_format;
1703 map<string, string>::const_iterator it = itype->second.find(entry);
1704 if (it == itype->second.end() && !fallback.empty())
1705 it = itype->second.find(fallback);
1706 if (it == itype->second.end())
1707 return default_format;
1712 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1713 string const & macro) const
1715 static string empty;
1716 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1717 if (itype == cite_macros_.end())
1719 map<string, string>::const_iterator it = itype->second.find(macro);
1720 if (it == itype->second.end())
1726 vector<string> const DocumentClass::citeCommands(
1727 CiteEngineType const & type) const
1729 vector<CitationStyle> const styles = citeStyles(type);
1730 vector<CitationStyle>::const_iterator it = styles.begin();
1731 vector<CitationStyle>::const_iterator end = styles.end();
1732 vector<string> cmds;
1733 for (; it != end; ++it) {
1734 CitationStyle const cite = *it;
1735 cmds.push_back(cite.cmd);
1741 vector<CitationStyle> const & DocumentClass::citeStyles(
1742 CiteEngineType const & type) const
1744 static vector<CitationStyle> empty;
1745 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1746 if (it == cite_styles_.end())
1752 /////////////////////////////////////////////////////////////////////////
1756 /////////////////////////////////////////////////////////////////////////
1758 ostream & operator<<(ostream & os, PageSides p)