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 = 63; //spitz: new tags CiteFramework, MaxCiteNames, extended InsetCite syntax.
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), maxcitenames_(2),
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,
234 LexerKeyword textClassTags[] = {
235 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
236 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
237 { "addtopreamble", TC_ADDTOPREAMBLE },
238 { "citeengine", TC_CITEENGINE },
239 { "citeenginetype", TC_CITEENGINETYPE },
240 { "citeformat", TC_CITEFORMAT },
241 { "citeframework", TC_CITEFRAMEWORK },
242 { "classoptions", TC_CLASSOPTIONS },
243 { "columns", TC_COLUMNS },
244 { "counter", TC_COUNTER },
245 { "defaultbiblio", TC_DEFAULTBIBLIO },
246 { "defaultfont", TC_DEFAULTFONT },
247 { "defaultmodule", TC_DEFAULTMODULE },
248 { "defaultstyle", TC_DEFAULTSTYLE },
249 { "excludesmodule", TC_EXCLUDESMODULE },
250 { "float", TC_FLOAT },
251 { "format", TC_FORMAT },
252 { "fullauthorlist", TC_FULLAUTHORLIST },
253 { "htmlpreamble", TC_HTMLPREAMBLE },
254 { "htmlstyles", TC_HTMLSTYLES },
255 { "htmltocsection", TC_HTMLTOCSECTION },
256 { "ifcounter", TC_IFCOUNTER },
257 { "input", TC_INPUT },
258 { "insetlayout", TC_INSETLAYOUT },
259 { "leftmargin", TC_LEFTMARGIN },
260 { "maxcitenames", TC_MAXCITENAMES },
261 { "modifystyle", TC_MODIFYSTYLE },
262 { "nocounter", TC_NOCOUNTER },
263 { "nofloat", TC_NOFLOAT },
264 { "noinsetlayout", TC_NOINSETLAYOUT },
265 { "nostyle", TC_NOSTYLE },
266 { "outlinername", TC_OUTLINERNAME },
267 { "outputformat", TC_OUTPUTFORMAT },
268 { "outputtype", TC_OUTPUTTYPE },
269 { "packageoptions", TC_PKGOPTS },
270 { "pagestyle", TC_PAGESTYLE },
271 { "preamble", TC_PREAMBLE },
272 { "provides", TC_PROVIDES },
273 { "providesmodule", TC_PROVIDESMODULE },
274 { "providestyle", TC_PROVIDESTYLE },
275 { "requires", TC_REQUIRES },
276 { "rightmargin", TC_RIGHTMARGIN },
277 { "secnumdepth", TC_SECNUMDEPTH },
278 { "sides", TC_SIDES },
279 { "style", TC_STYLE },
280 { "titlelatexname", TC_TITLELATEXNAME },
281 { "titlelatextype", TC_TITLELATEXTYPE },
282 { "tocdepth", TC_TOCDEPTH }
288 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
290 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
291 TempFile tmp("convertXXXXXX.layout");
292 FileName const tempfile = tmp.name();
293 bool success = layout2layout(filename, tempfile);
295 success = readWithoutConv(tempfile, rt) == OK;
300 std::string TextClass::convert(std::string const & str)
302 TempFile tmp1("localXXXXXX.layout");
303 FileName const fn = tmp1.name();
304 ofstream os(fn.toFilesystemEncoding().c_str());
307 TempFile tmp2("convert_localXXXXXX.layout");
308 FileName const tempfile = tmp2.name();
309 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
312 ifstream is(tempfile.toFilesystemEncoding().c_str());
324 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
326 if (!filename.isReadableFile()) {
327 lyxerr << "Cannot read layout file `" << filename << "'."
332 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
333 to_utf8(makeDisplayPath(filename.absFileName())));
335 // Define the plain layout used in table cells, ert, etc. Note that
336 // we do this before loading any layout file, so that classes can
337 // override features of this layout if they should choose to do so.
338 if (rt == BASECLASS && !hasLayout(plain_layout_))
339 layoutlist_.push_back(createBasicLayout(plain_layout_));
341 Lexer lexrc(textClassTags);
342 lexrc.setFile(filename);
343 ReturnValues retval = read(lexrc, rt);
345 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
346 to_utf8(makeDisplayPath(filename.absFileName())));
352 bool TextClass::read(FileName const & filename, ReadType rt)
354 ReturnValues const retval = readWithoutConv(filename, rt);
355 if (retval != FORMAT_MISMATCH)
358 bool const worx = convertLayoutFormat(filename, rt);
360 LYXERR0 ("Unable to convert " << filename <<
361 " to format " << LAYOUT_FORMAT);
366 TextClass::ReturnValues TextClass::validate(std::string const & str)
369 return tc.read(str, VALIDATION);
373 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
375 Lexer lexrc(textClassTags);
376 istringstream is(str);
378 ReturnValues retval = read(lexrc, rt);
380 if (retval != FORMAT_MISMATCH)
383 // write the layout string to a temporary file
384 TempFile tmp("TextClass_read");
385 FileName const tempfile = tmp.name();
386 ofstream os(tempfile.toFilesystemEncoding().c_str());
388 LYXERR0("Unable to create temporary file");
394 // now try to convert it to LAYOUT_FORMAT
395 if (!convertLayoutFormat(tempfile, rt)) {
396 LYXERR0("Unable to convert internal layout information to format "
405 // Reads a textclass structure from file.
406 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
411 // Format of files before the 'Format' tag was introduced
416 while (lexrc.isOK() && !error) {
417 int le = lexrc.lex();
420 case Lexer::LEX_FEOF:
423 case Lexer::LEX_UNDEF:
424 lexrc.printError("Unknown TextClass tag `$$Token'");
432 // used below to track whether we are in an IfStyle or IfCounter tag.
433 bool modifystyle = false;
434 bool providestyle = false;
435 bool ifcounter = false;
437 switch (static_cast<TextClassTags>(le)) {
441 format = lexrc.getInteger();
444 case TC_OUTPUTFORMAT:
446 outputFormat_ = lexrc.getString();
450 readOutputType(lexrc);
451 switch(outputType_) {
453 outputFormat_ = "latex";
456 outputFormat_ = "docbook";
459 outputFormat_ = "literate";
464 case TC_INPUT: // Include file
467 string const inc = lexrc.getString();
468 if (!path().empty() && (prefixIs(inc, "./") ||
469 prefixIs(inc, "../")))
470 tmp = fileSearch(path(), inc, "layout");
472 tmp = libFileSearch("layouts", inc,
476 lexrc.printError("Could not find input file: " + inc);
478 } else if (!read(tmp, MERGE)) {
479 lexrc.printError("Error reading input file: " + tmp.absFileName());
485 case TC_DEFAULTSTYLE:
487 docstring const name = from_utf8(subst(lexrc.getString(),
489 defaultlayout_ = name;
496 case TC_PROVIDESTYLE:
497 // if modifystyle is true, then we got here by falling through
498 // so we are not in an ProvideStyle block
504 lexrc.printError("No name given for style: `$$Token'.");
508 docstring const name = from_utf8(subst(lexrc.getString(),
511 string s = "Could not read name for style: `$$Token' "
512 + lexrc.getString() + " is probably not valid UTF-8!";
515 // Since we couldn't read the name, we just scan the rest
516 // of the style and discard it.
517 error = !readStyle(lexrc, lay);
521 bool const have_layout = hasLayout(name);
523 // If the layout already exists, then we want to add it to
524 // the existing layout, as long as we are not in an ProvideStyle
526 if (have_layout && !providestyle) {
527 Layout & lay = operator[](name);
528 error = !readStyle(lexrc, lay);
530 // If the layout does not exist, then we want to create a new
531 // one, but not if we are in a ModifyStyle block.
532 else if (!have_layout && !modifystyle) {
534 layout.setName(name);
535 error = !readStyle(lexrc, layout);
537 layoutlist_.push_back(layout);
539 if (defaultlayout_.empty()) {
540 // We do not have a default layout yet, so we choose
541 // the first layout we encounter.
542 defaultlayout_ = name;
545 // There are two ways to get here:
546 // (i) The layout exists but we are in an ProvideStyle block
547 // (ii) The layout doesn't exist, but we are in an ModifyStyle
549 // Either way, we just scan the rest and discard it
552 // false positive from coverity
553 // coverity[checked_return]
554 readStyle(lexrc, lay);
561 docstring const style = from_utf8(subst(lexrc.getString(),
563 if (!deleteLayout(style))
564 lyxerr << "Cannot delete style `"
565 << to_utf8(style) << '\'' << endl;
569 case TC_NOINSETLAYOUT:
571 docstring const style = from_utf8(subst(lexrc.getString(),
573 if (!deleteInsetLayout(style))
574 LYXERR0("Style `" << style << "' cannot be removed\n"
575 "because it was not found!");
581 columns_ = lexrc.getInteger();
586 switch (lexrc.getInteger()) {
587 case 1: sides_ = OneSide; break;
588 case 2: sides_ = TwoSides; break;
590 lyxerr << "Impossible number of page"
591 " sides, setting to one."
601 pagestyle_ = rtrim(lexrc.getString());
605 defaultfont_ = lyxRead(lexrc);
606 if (!defaultfont_.resolved()) {
607 lexrc.printError("Warning: defaultfont should "
608 "be fully instantiated!");
609 defaultfont_.realize(sane_font);
615 secnumdepth_ = lexrc.getInteger();
620 tocdepth_ = lexrc.getInteger();
623 // First step to support options
624 case TC_CLASSOPTIONS:
625 readClassOptions(lexrc);
629 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
632 case TC_HTMLPREAMBLE:
633 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
637 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
640 case TC_HTMLTOCSECTION:
641 html_toc_section_ = from_utf8(trim(lexrc.getString()));
644 case TC_ADDTOPREAMBLE:
645 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
648 case TC_ADDTOHTMLPREAMBLE:
649 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
652 case TC_ADDTOHTMLSTYLES:
653 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
658 string const feature = lexrc.getString();
660 if (lexrc.getInteger())
661 provides_.insert(feature);
663 provides_.erase(feature);
669 vector<string> const req
670 = getVectorFromString(lexrc.getString());
671 requires_.insert(req.begin(), req.end());
677 string const pkg = lexrc.getString();
679 string const options = lexrc.getString();
680 package_options_[pkg] = options;
684 case TC_DEFAULTMODULE: {
686 string const module = lexrc.getString();
687 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
688 default_modules_.push_back(module);
692 case TC_PROVIDESMODULE: {
694 string const module = lexrc.getString();
695 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
696 provided_modules_.push_back(module);
700 case TC_EXCLUDESMODULE: {
702 string const module = lexrc.getString();
703 // modules already have their own way to exclude other modules
705 LYXERR0("ExcludesModule tag cannot be used in a module!");
708 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
709 excluded_modules_.push_back(module);
713 case TC_LEFTMARGIN: // left margin type
715 leftmargin_ = lexrc.getDocString();
718 case TC_RIGHTMARGIN: // right margin type
720 rightmargin_ = lexrc.getDocString();
723 case TC_INSETLAYOUT: {
725 lexrc.printError("No name given for InsetLayout: `$$Token'.");
729 docstring const name = subst(lexrc.getDocString(), '_', ' ');
731 string s = "Could not read name for InsetLayout: `$$Token' "
732 + lexrc.getString() + " is probably not valid UTF-8!";
735 // Since we couldn't read the name, we just scan the rest
736 // of the style and discard it.
737 il.read(lexrc, *this);
738 // Let's try to continue rather than abort.
740 } else if (hasInsetLayout(name)) {
741 InsetLayout & il = insetlayoutlist_[name];
742 error = !il.read(lexrc, *this);
746 error = !il.read(lexrc, *this);
748 insetlayoutlist_[name] = il;
754 error = !readFloat(lexrc);
758 error = !readCiteEngine(lexrc);
761 case TC_CITEENGINETYPE:
763 opt_enginetype_ = rtrim(lexrc.getString());
767 error = !readCiteFormat(lexrc);
770 case TC_CITEFRAMEWORK:
772 citeframework_ = rtrim(lexrc.getString());
775 case TC_MAXCITENAMES:
777 maxcitenames_ = size_t(lexrc.getInteger());
780 case TC_DEFAULTBIBLIO:
782 vector<string> const dbs =
783 getVectorFromString(rtrim(lexrc.getString()), "|");
784 vector<string>::const_iterator it = dbs.begin();
785 vector<string>::const_iterator end = dbs.end();
786 for (; it != end; ++it) {
787 if (!contains(*it, ':')) {
788 vector<string> const enginetypes =
789 getVectorFromString(opt_enginetype_, "|");
790 for (string const &s: enginetypes)
791 cite_default_biblio_style_[s] = *it;
794 string const db = split(*it, eng, ':');
795 cite_default_biblio_style_[eng] = db;
801 case TC_FULLAUTHORLIST:
803 cite_full_author_list_ &= lexrc.getBool();
808 docstring const cnt = lexrc.getDocString();
809 if (!counters_.remove(cnt))
810 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
819 docstring const name = lexrc.getDocString();
821 string s = "Could not read name for counter: `$$Token' "
822 + lexrc.getString() + " is probably not valid UTF-8!";
823 lexrc.printError(s.c_str());
825 // Since we couldn't read the name, we just scan the rest
829 error = !counters_.read(lexrc, name, !ifcounter);
832 lexrc.printError("No name given for style: `$$Token'.");
837 case TC_TITLELATEXTYPE:
838 readTitleType(lexrc);
841 case TC_TITLELATEXNAME:
843 titlename_ = lexrc.getString();
848 string const nofloat = lexrc.getString();
849 floatlist_.erase(nofloat);
853 case TC_OUTLINERNAME:
854 error = !readOutlinerName(lexrc);
858 // Note that this is triggered the first time through the loop unless
859 // we hit a format tag.
860 if (format != LAYOUT_FORMAT)
861 return FORMAT_MISMATCH;
864 // at present, we abort if we encounter an error,
865 // so there is no point continuing.
872 if (defaultlayout_.empty()) {
873 LYXERR0("Error: Textclass '" << name_
874 << "' is missing a defaultstyle.");
878 // Try to erase "stdinsets" from the provides_ set.
880 // Provides stdinsets 1
881 // declaration simply tells us that the standard insets have been
882 // defined. (It's found in stdinsets.inc but could also be used in
883 // user-defined files.) There isn't really any such package. So we
884 // might as well go ahead and erase it.
885 // If we do not succeed, then it was not there, which means that
886 // the textclass did not provide the definitions of the standard
887 // insets. So we need to try to load them.
888 int erased = provides_.erase("stdinsets");
890 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
893 frontend::Alert::warning(_("Missing File"),
894 _("Could not find stdinsets.inc! This may lead to data loss!"));
896 } else if (!read(tmp, MERGE)) {
897 frontend::Alert::warning(_("Corrupt File"),
898 _("Could not read stdinsets.inc! This may lead to data loss!"));
903 min_toclevel_ = Layout::NOT_IN_TOC;
904 max_toclevel_ = Layout::NOT_IN_TOC;
905 const_iterator lit = begin();
906 const_iterator len = end();
907 for (; lit != len; ++lit) {
908 int const toclevel = lit->toclevel;
909 if (toclevel != Layout::NOT_IN_TOC) {
910 if (min_toclevel_ == Layout::NOT_IN_TOC)
911 min_toclevel_ = toclevel;
913 min_toclevel_ = min(min_toclevel_, toclevel);
914 max_toclevel_ = max(max_toclevel_, toclevel);
917 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
918 << ", maximum is " << max_toclevel_);
920 return (error ? ERROR : OK);
924 void TextClass::readTitleType(Lexer & lexrc)
926 LexerKeyword titleTypeTags[] = {
927 { "commandafter", TITLE_COMMAND_AFTER },
928 { "environment", TITLE_ENVIRONMENT }
931 PushPopHelper pph(lexrc, titleTypeTags);
933 int le = lexrc.lex();
935 case Lexer::LEX_UNDEF:
936 lexrc.printError("Unknown output type `$$Token'");
938 case TITLE_COMMAND_AFTER:
939 case TITLE_ENVIRONMENT:
940 titletype_ = static_cast<TitleLatexType>(le);
943 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
949 void TextClass::readOutputType(Lexer & lexrc)
951 LexerKeyword outputTypeTags[] = {
952 { "docbook", DOCBOOK },
954 { "literate", LITERATE }
957 PushPopHelper pph(lexrc, outputTypeTags);
959 int le = lexrc.lex();
961 case Lexer::LEX_UNDEF:
962 lexrc.printError("Unknown output type `$$Token'");
967 outputType_ = static_cast<OutputType>(le);
970 LYXERR0("Unhandled value " << le);
976 void TextClass::readClassOptions(Lexer & lexrc)
986 LexerKeyword classOptionsTags[] = {
988 {"fontsize", CO_FONTSIZE },
989 {"header", CO_HEADER },
990 {"other", CO_OTHER },
991 {"pagestyle", CO_PAGESTYLE }
994 lexrc.pushTable(classOptionsTags);
996 while (!getout && lexrc.isOK()) {
997 int le = lexrc.lex();
999 case Lexer::LEX_UNDEF:
1000 lexrc.printError("Unknown ClassOption tag `$$Token'");
1008 opt_fontsize_ = rtrim(lexrc.getString());
1012 opt_pagestyle_ = rtrim(lexrc.getString());
1016 if (options_.empty())
1017 options_ = lexrc.getString();
1019 options_ += ',' + lexrc.getString();
1023 class_header_ = subst(lexrc.getString(), """, "\"");
1034 bool TextClass::readCiteEngine(Lexer & lexrc)
1036 int const type = readCiteEngineType(lexrc);
1037 if (type & ENGINE_TYPE_AUTHORYEAR)
1038 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1039 if (type & ENGINE_TYPE_NUMERICAL)
1040 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1041 if (type & ENGINE_TYPE_DEFAULT)
1042 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1044 bool getout = false;
1045 while (!getout && lexrc.isOK()) {
1047 def = lexrc.getString();
1048 def = subst(def, " ", "");
1049 def = subst(def, "\t", "");
1050 if (compare_ascii_no_case(def, "end") == 0) {
1055 char ichar = def[0];
1058 if (isUpperCase(ichar)) {
1059 cs.forceUpperCase = true;
1060 def[0] = lowercase(ichar);
1063 /** For portability reasons (between different
1064 * cite engines such as natbib and biblatex),
1065 * we distinguish between:
1066 * 1. The LyX name as output in the LyX file
1067 * 2. Possible aliases that might fall back to
1068 * the given LyX name in the current engine
1069 * 3. The actual LaTeX command that is output
1070 * (2) and (3) are optional.
1071 * Also, the GUI string for the starred version can
1074 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1083 ScanMode mode = LyXName;
1084 ScanMode oldmode = LyXName;
1089 size_t const n = def.size();
1090 for (size_t i = 0; i != n; ++i) {
1094 else if (ichar == '=')
1096 else if (ichar == '<') {
1099 } else if (ichar == '>')
1101 else if (mode == LaTeXCmd)
1103 else if (mode == StarDesc)
1105 else if (ichar == '$')
1106 cs.hasQualifiedList = true;
1107 else if (ichar == '*')
1108 cs.hasStarredVersion = true;
1109 else if (ichar == '[' && cs.textAfter)
1110 cs.textBefore = true;
1111 else if (ichar == '[')
1112 cs.textAfter = true;
1113 else if (ichar != ']') {
1121 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1122 if (!alias.empty()) {
1123 vector<string> const aliases = getVectorFromString(alias);
1124 for (string const &s: aliases)
1125 cite_command_aliases_[s] = lyx_cmd;
1127 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1128 int size = stardesc.size();
1130 cs.stardesc = stardescs[0];
1132 cs.startooltip = stardescs[1];
1133 if (type & ENGINE_TYPE_AUTHORYEAR)
1134 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1135 if (type & ENGINE_TYPE_NUMERICAL)
1136 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1137 if (type & ENGINE_TYPE_DEFAULT)
1138 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1144 int TextClass::readCiteEngineType(Lexer & lexrc) const
1146 LATTEST(ENGINE_TYPE_DEFAULT ==
1147 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1148 if (!lexrc.next()) {
1149 lexrc.printError("No cite engine type given for token: `$$Token'.");
1150 return ENGINE_TYPE_DEFAULT;
1152 string const type = rtrim(lexrc.getString());
1153 if (compare_ascii_no_case(type, "authoryear") == 0)
1154 return ENGINE_TYPE_AUTHORYEAR;
1155 else if (compare_ascii_no_case(type, "numerical") == 0)
1156 return ENGINE_TYPE_NUMERICAL;
1157 else if (compare_ascii_no_case(type, "default") != 0) {
1158 string const s = "Unknown cite engine type `" + type
1159 + "' given for token: `$$Token',";
1160 lexrc.printError(s);
1162 return ENGINE_TYPE_DEFAULT;
1166 bool TextClass::readCiteFormat(Lexer & lexrc)
1168 int const type = readCiteEngineType(lexrc);
1171 while (lexrc.isOK()) {
1173 etype = lexrc.getString();
1174 if (compare_ascii_no_case(etype, "end") == 0)
1179 definition = lexrc.getString();
1180 char initchar = etype[0];
1181 if (initchar == '#')
1183 if (initchar == '!' || initchar == '_') {
1184 if (type & ENGINE_TYPE_AUTHORYEAR)
1185 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1186 if (type & ENGINE_TYPE_NUMERICAL)
1187 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1188 if (type & ENGINE_TYPE_DEFAULT)
1189 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1191 if (type & ENGINE_TYPE_AUTHORYEAR)
1192 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1193 if (type & ENGINE_TYPE_NUMERICAL)
1194 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1195 if (type & ENGINE_TYPE_DEFAULT)
1196 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1203 bool TextClass::readFloat(Lexer & lexrc)
1220 FT_ALLOWED_PLACEMENT,
1226 LexerKeyword floatTags[] = {
1227 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1228 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1229 { "allowswide", FT_ALLOWS_WIDE },
1231 { "extension", FT_EXT },
1232 { "guiname", FT_NAME },
1233 { "htmlattr", FT_HTMLATTR },
1234 { "htmlstyle", FT_HTMLSTYLE },
1235 { "htmltag", FT_HTMLTAG },
1236 { "ispredefined", FT_PREDEFINED },
1237 { "listcommand", FT_LISTCOMMAND },
1238 { "listname", FT_LISTNAME },
1239 { "numberwithin", FT_WITHIN },
1240 { "placement", FT_PLACEMENT },
1241 { "refprefix", FT_REFPREFIX },
1242 { "style", FT_STYLE },
1243 { "type", FT_TYPE },
1244 { "usesfloatpkg", FT_USESFLOAT }
1247 lexrc.pushTable(floatTags);
1251 docstring htmlstyle;
1257 string allowed_placement = "!htbpH";
1262 bool usesfloat = true;
1263 bool ispredefined = false;
1264 bool allowswide = true;
1265 bool allowssideways = true;
1267 bool getout = false;
1268 while (!getout && lexrc.isOK()) {
1269 int le = lexrc.lex();
1271 case Lexer::LEX_UNDEF:
1272 lexrc.printError("Unknown float tag `$$Token'");
1280 type = lexrc.getString();
1281 if (floatlist_.typeExist(type)) {
1282 Floating const & fl = floatlist_.getType(type);
1283 placement = fl.placement();
1285 within = fl.within();
1288 listname = fl.listName();
1289 usesfloat = fl.usesFloatPkg();
1290 ispredefined = fl.isPredefined();
1291 listcommand = fl.listCommand();
1292 refprefix = fl.refPrefix();
1297 name = lexrc.getString();
1301 placement = lexrc.getString();
1303 case FT_ALLOWED_PLACEMENT:
1305 allowed_placement = lexrc.getString();
1309 ext = lexrc.getString();
1313 within = lexrc.getString();
1314 if (within == "none")
1319 style = lexrc.getString();
1321 case FT_LISTCOMMAND:
1323 listcommand = lexrc.getString();
1327 refprefix = lexrc.getString();
1331 listname = lexrc.getString();
1335 usesfloat = lexrc.getBool();
1339 ispredefined = lexrc.getBool();
1341 case FT_ALLOWS_SIDEWAYS:
1343 allowssideways = lexrc.getBool();
1345 case FT_ALLOWS_WIDE:
1347 allowswide = lexrc.getBool();
1351 htmlattr = lexrc.getString();
1355 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1359 htmltag = lexrc.getString();
1369 // Here we have a full float if getout == true
1371 if (!usesfloat && listcommand.empty()) {
1372 // if this float uses the same auxfile as an existing one,
1373 // there is no need for it to provide a list command.
1374 FloatList::const_iterator it = floatlist_.begin();
1375 FloatList::const_iterator en = floatlist_.end();
1376 bool found_ext = false;
1377 for (; it != en; ++it) {
1378 if (it->second.ext() == ext) {
1384 LYXERR0("The layout does not provide a list command " <<
1385 "for the float `" << type << "'. LyX will " <<
1386 "not be able to produce a float list.");
1388 Floating fl(type, placement, ext, within, style, name,
1389 listname, listcommand, refprefix, allowed_placement,
1390 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1391 allowswide, allowssideways);
1392 floatlist_.newFloat(fl);
1393 // each float has its own counter
1394 counters_.newCounter(from_ascii(type), from_ascii(within),
1395 docstring(), docstring());
1396 // also define sub-float counters
1397 docstring const subtype = "sub-" + from_ascii(type);
1398 counters_.newCounter(subtype, from_ascii(type),
1399 "\\alph{" + subtype + "}", docstring());
1405 bool TextClass::readOutlinerName(Lexer & lexrc)
1410 type = lexrc.getString();
1412 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1416 name = lexrc.getDocString();
1418 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1421 outliner_names_[type] = name;
1426 string const & TextClass::prerequisites(string const & sep) const
1428 if (contains(prerequisites_, ',')) {
1429 vector<string> const pres = getVectorFromString(prerequisites_);
1430 prerequisites_ = getStringFromVector(pres, sep);
1432 return prerequisites_;
1436 bool TextClass::hasLayout(docstring const & n) const
1438 docstring const name = n.empty() ? defaultLayoutName() : n;
1440 return find_if(layoutlist_.begin(), layoutlist_.end(),
1441 LayoutNamesEqual(name))
1442 != layoutlist_.end();
1446 bool TextClass::hasInsetLayout(docstring const & n) const
1450 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1451 return it != insetlayoutlist_.end();
1455 Layout const & TextClass::operator[](docstring const & name) const
1457 LATTEST(!name.empty());
1460 find_if(begin(), end(), LayoutNamesEqual(name));
1463 LYXERR0("We failed to find the layout '" << name
1464 << "' in the layout list. You MUST investigate!");
1465 for (const_iterator cit = begin(); cit != end(); ++cit)
1466 lyxerr << " " << to_utf8(cit->name()) << endl;
1468 // We require the name to exist
1469 static const Layout dummy;
1470 LASSERT(false, return dummy);
1477 Layout & TextClass::operator[](docstring const & name)
1479 LATTEST(!name.empty());
1480 // Safe to continue, given what we do below.
1482 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1485 LYXERR0("We failed to find the layout '" << to_utf8(name)
1486 << "' in the layout list. You MUST investigate!");
1487 for (const_iterator cit = begin(); cit != end(); ++cit)
1488 LYXERR0(" " << to_utf8(cit->name()));
1490 // we require the name to exist
1492 // we are here only in release mode
1493 layoutlist_.push_back(createBasicLayout(name, true));
1494 it = find_if(begin(), end(), LayoutNamesEqual(name));
1501 bool TextClass::deleteLayout(docstring const & name)
1503 if (name == defaultLayoutName() || name == plainLayoutName())
1506 LayoutList::iterator it =
1507 remove_if(layoutlist_.begin(), layoutlist_.end(),
1508 LayoutNamesEqual(name));
1510 LayoutList::iterator end = layoutlist_.end();
1511 bool const ret = (it != end);
1512 layoutlist_.erase(it, end);
1517 bool TextClass::deleteInsetLayout(docstring const & name)
1519 return insetlayoutlist_.erase(name);
1523 // Load textclass info if not loaded yet
1524 bool TextClass::load(string const & path) const
1529 // Read style-file, provided path is searched before the system ones
1530 // If path is a file, it is loaded directly.
1531 FileName layout_file(path);
1532 if (!path.empty() && !layout_file.isReadableFile())
1533 layout_file = FileName(addName(path, name_ + ".layout"));
1534 if (layout_file.empty() || !layout_file.exists())
1535 layout_file = libFileSearch("layouts", name_, "layout");
1536 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1539 lyxerr << "Error reading `"
1540 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1541 << "'\n(Check `" << name_
1542 << "')\nCheck your installation and "
1543 "try Options/Reconfigure..."
1551 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1556 layoutlist_.push_back(createBasicLayout(n, true));
1561 string DocumentClass::forcedLayouts() const
1565 const_iterator const e = end();
1566 for (const_iterator i = begin(); i != e; ++i) {
1567 if (i->forcelocal > 0) {
1569 os << "Format " << LAYOUT_FORMAT << '\n';
1579 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1581 // FIXME The fix for the InsetLayout part of 4812 would be here:
1582 // Add the InsetLayout to the document class if it is not found.
1584 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1585 while (!n.empty()) {
1586 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1587 if (cit != cen && cit->first == n) {
1588 if (cit->second.obsoleted_by().empty())
1590 n = cit->second.obsoleted_by();
1591 return insetLayout(n);
1593 // If we have a generic prefix (e.g., "Note:"),
1594 // try if this one alone is found.
1595 size_t i = n.find(':');
1596 if (i == string::npos)
1600 // Layout "name" not found.
1601 return plainInsetLayout();
1605 InsetLayout const & DocumentClass::plainInsetLayout() {
1606 static const InsetLayout plain_insetlayout_;
1607 return plain_insetlayout_;
1611 docstring const & TextClass::defaultLayoutName() const
1613 return defaultlayout_;
1617 Layout const & TextClass::defaultLayout() const
1619 return operator[](defaultLayoutName());
1623 bool TextClass::isDefaultLayout(Layout const & layout) const
1625 return layout.name() == defaultLayoutName();
1629 bool TextClass::isPlainLayout(Layout const & layout) const
1631 return layout.name() == plainLayoutName();
1635 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1637 static Layout * defaultLayout = NULL;
1639 if (defaultLayout) {
1640 defaultLayout->setUnknown(unknown);
1641 defaultLayout->setName(name);
1642 return *defaultLayout;
1645 static char const * s = "Margin Static\n"
1646 "LatexType Paragraph\n"
1649 "AlignPossible Left, Right, Center\n"
1650 "LabelType No_Label\n"
1652 istringstream ss(s);
1653 Lexer lex(textClassTags);
1655 defaultLayout = new Layout;
1656 defaultLayout->setUnknown(unknown);
1657 defaultLayout->setName(name);
1658 if (!readStyle(lex, *defaultLayout)) {
1659 // The only way this happens is because the hardcoded layout above
1663 return *defaultLayout;
1667 DocumentClassPtr getDocumentClass(
1668 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1669 LayoutModuleList const & celist,
1672 DocumentClassPtr doc_class =
1673 DocumentClassPtr(new DocumentClass(baseClass));
1674 LayoutModuleList::const_iterator it = modlist.begin();
1675 LayoutModuleList::const_iterator en = modlist.end();
1676 for (; it != en; ++it) {
1677 string const modName = *it;
1678 LyXModule * lm = theModuleList[modName];
1680 docstring const msg =
1681 bformat(_("The module %1$s has been requested by\n"
1682 "this document but has not been found in the list of\n"
1683 "available modules. If you recently installed it, you\n"
1684 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1686 frontend::Alert::warning(_("Module not available"), msg);
1689 if (!lm->isAvailable() && !clone) {
1690 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1691 docstring const msg =
1692 bformat(_("The module %1$s requires a package that is not\n"
1693 "available in your LaTeX installation, or a converter that\n"
1694 "you have not installed. LaTeX output may not be possible.\n"
1695 "Missing prerequisites:\n"
1697 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1698 from_utf8(modName), prereqs);
1699 frontend::Alert::warning(_("Package not available"), msg, true);
1701 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1702 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1703 docstring const msg =
1704 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1705 frontend::Alert::warning(_("Read Error"), msg);
1709 LayoutModuleList::const_iterator cit = celist.begin();
1710 LayoutModuleList::const_iterator cen = celist.end();
1711 for (; cit != cen; ++cit) {
1712 string const ceName = *cit;
1713 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1715 docstring const msg =
1716 bformat(_("The cite engine %1$s has been requested by\n"
1717 "this document but has not been found in the list of\n"
1718 "available engines. If you recently installed it, you\n"
1719 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1721 frontend::Alert::warning(_("Cite Engine not available"), msg);
1724 if (!ce->isAvailable() && !clone) {
1725 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1726 docstring const msg =
1727 bformat(_("The cite engine %1$s requires a package that is not\n"
1728 "available in your LaTeX installation, or a converter that\n"
1729 "you have not installed. LaTeX output may not be possible.\n"
1730 "Missing prerequisites:\n"
1732 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1733 from_utf8(ceName), prereqs);
1734 frontend::Alert::warning(_("Package not available"), msg, true);
1736 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1737 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1738 docstring const msg =
1739 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1740 frontend::Alert::warning(_("Read Error"), msg);
1748 /////////////////////////////////////////////////////////////////////////
1752 /////////////////////////////////////////////////////////////////////////
1754 DocumentClass::DocumentClass(LayoutFile const & tc)
1759 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1761 LayoutList::const_iterator it = layoutlist_.begin();
1762 LayoutList::const_iterator end = layoutlist_.end();
1763 for (; it != end; ++it)
1764 if (it->latexname() == lay)
1770 bool DocumentClass::provides(string const & p) const
1772 return provides_.find(p) != provides_.end();
1776 bool DocumentClass::hasTocLevels() const
1778 return min_toclevel_ != Layout::NOT_IN_TOC;
1782 Layout const & DocumentClass::getTOCLayout() const
1784 // we're going to look for the layout with the minimum toclevel
1785 TextClass::LayoutList::const_iterator lit = begin();
1786 TextClass::LayoutList::const_iterator const len = end();
1787 int minlevel = 1000;
1788 Layout const * lay = NULL;
1789 for (; lit != len; ++lit) {
1790 int const level = lit->toclevel;
1791 // we don't want Part or unnumbered sections
1792 if (level == Layout::NOT_IN_TOC || level < 0
1793 || level >= minlevel || lit->counter.empty())
1800 // hmm. that is very odd, so we'll do our best.
1801 return operator[](defaultLayoutName());
1805 Layout const & DocumentClass::htmlTOCLayout() const
1807 if (html_toc_section_.empty())
1808 html_toc_section_ = getTOCLayout().name();
1809 return operator[](html_toc_section_);
1813 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1814 string const & entry, bool const punct, string const & fallback) const
1816 string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1818 default_format += ".";
1820 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1821 if (itype == cite_formats_.end())
1822 return default_format;
1823 map<string, string>::const_iterator it = itype->second.find(entry);
1824 if (it == itype->second.end() && !fallback.empty())
1825 it = itype->second.find(fallback);
1826 if (it == itype->second.end())
1827 return default_format;
1829 return it->second + ".";
1834 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1835 string const & macro) const
1837 static string empty;
1838 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1839 if (itype == cite_macros_.end())
1841 map<string, string>::const_iterator it = itype->second.find(macro);
1842 if (it == itype->second.end())
1848 vector<string> const DocumentClass::citeCommands(
1849 CiteEngineType const & type) const
1851 vector<CitationStyle> const styles = citeStyles(type);
1852 vector<CitationStyle>::const_iterator it = styles.begin();
1853 vector<CitationStyle>::const_iterator end = styles.end();
1854 vector<string> cmds;
1855 for (; it != end; ++it) {
1856 CitationStyle const cite = *it;
1857 cmds.push_back(cite.name);
1863 vector<CitationStyle> const & DocumentClass::citeStyles(
1864 CiteEngineType const & type) const
1866 static vector<CitationStyle> empty;
1867 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1868 if (it == cite_styles_.end())
1874 /////////////////////////////////////////////////////////////////////////
1878 /////////////////////////////////////////////////////////////////////////
1880 ostream & operator<<(ostream & os, PageSides p)