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,
233 LexerKeyword textClassTags[] = {
234 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
235 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
236 { "addtopreamble", TC_ADDTOPREAMBLE },
237 { "citeengine", TC_CITEENGINE },
238 { "citeenginetype", TC_CITEENGINETYPE },
239 { "citeformat", TC_CITEFORMAT },
240 { "citeframework", TC_CITEFRAMEWORK },
241 { "classoptions", TC_CLASSOPTIONS },
242 { "columns", TC_COLUMNS },
243 { "counter", TC_COUNTER },
244 { "defaultbiblio", TC_DEFAULTBIBLIO },
245 { "defaultfont", TC_DEFAULTFONT },
246 { "defaultmodule", TC_DEFAULTMODULE },
247 { "defaultstyle", TC_DEFAULTSTYLE },
248 { "excludesmodule", TC_EXCLUDESMODULE },
249 { "float", TC_FLOAT },
250 { "format", TC_FORMAT },
251 { "fullauthorlist", TC_FULLAUTHORLIST },
252 { "htmlpreamble", TC_HTMLPREAMBLE },
253 { "htmlstyles", TC_HTMLSTYLES },
254 { "htmltocsection", TC_HTMLTOCSECTION },
255 { "ifcounter", TC_IFCOUNTER },
256 { "input", TC_INPUT },
257 { "insetlayout", TC_INSETLAYOUT },
258 { "leftmargin", TC_LEFTMARGIN },
259 { "modifystyle", TC_MODIFYSTYLE },
260 { "nocounter", TC_NOCOUNTER },
261 { "nofloat", TC_NOFLOAT },
262 { "noinsetlayout", TC_NOINSETLAYOUT },
263 { "nostyle", TC_NOSTYLE },
264 { "outlinername", TC_OUTLINERNAME },
265 { "outputformat", TC_OUTPUTFORMAT },
266 { "outputtype", TC_OUTPUTTYPE },
267 { "packageoptions", TC_PKGOPTS },
268 { "pagestyle", TC_PAGESTYLE },
269 { "preamble", TC_PREAMBLE },
270 { "provides", TC_PROVIDES },
271 { "providesmodule", TC_PROVIDESMODULE },
272 { "providestyle", TC_PROVIDESTYLE },
273 { "requires", TC_REQUIRES },
274 { "rightmargin", TC_RIGHTMARGIN },
275 { "secnumdepth", TC_SECNUMDEPTH },
276 { "sides", TC_SIDES },
277 { "style", TC_STYLE },
278 { "titlelatexname", TC_TITLELATEXNAME },
279 { "titlelatextype", TC_TITLELATEXTYPE },
280 { "tocdepth", TC_TOCDEPTH }
286 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
288 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
289 TempFile tmp("convertXXXXXX.layout");
290 FileName const tempfile = tmp.name();
291 bool success = layout2layout(filename, tempfile);
293 success = readWithoutConv(tempfile, rt) == OK;
298 std::string TextClass::convert(std::string const & str)
300 TempFile tmp1("localXXXXXX.layout");
301 FileName const fn = tmp1.name();
302 ofstream os(fn.toFilesystemEncoding().c_str());
305 TempFile tmp2("convert_localXXXXXX.layout");
306 FileName const tempfile = tmp2.name();
307 bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
310 ifstream is(tempfile.toFilesystemEncoding().c_str());
322 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
324 if (!filename.isReadableFile()) {
325 lyxerr << "Cannot read layout file `" << filename << "'."
330 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
331 to_utf8(makeDisplayPath(filename.absFileName())));
333 // Define the plain layout used in table cells, ert, etc. Note that
334 // we do this before loading any layout file, so that classes can
335 // override features of this layout if they should choose to do so.
336 if (rt == BASECLASS && !hasLayout(plain_layout_))
337 layoutlist_.push_back(createBasicLayout(plain_layout_));
339 Lexer lexrc(textClassTags);
340 lexrc.setFile(filename);
341 ReturnValues retval = read(lexrc, rt);
343 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
344 to_utf8(makeDisplayPath(filename.absFileName())));
350 bool TextClass::read(FileName const & filename, ReadType rt)
352 ReturnValues const retval = readWithoutConv(filename, rt);
353 if (retval != FORMAT_MISMATCH)
356 bool const worx = convertLayoutFormat(filename, rt);
358 LYXERR0 ("Unable to convert " << filename <<
359 " to format " << LAYOUT_FORMAT);
364 TextClass::ReturnValues TextClass::validate(std::string const & str)
367 return tc.read(str, VALIDATION);
371 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
373 Lexer lexrc(textClassTags);
374 istringstream is(str);
376 ReturnValues retval = read(lexrc, rt);
378 if (retval != FORMAT_MISMATCH)
381 // write the layout string to a temporary file
382 TempFile tmp("TextClass_read");
383 FileName const tempfile = tmp.name();
384 ofstream os(tempfile.toFilesystemEncoding().c_str());
386 LYXERR0("Unable to create temporary file");
392 // now try to convert it to LAYOUT_FORMAT
393 if (!convertLayoutFormat(tempfile, rt)) {
394 LYXERR0("Unable to convert internal layout information to format "
403 // Reads a textclass structure from file.
404 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
409 // Format of files before the 'Format' tag was introduced
414 while (lexrc.isOK() && !error) {
415 int le = lexrc.lex();
418 case Lexer::LEX_FEOF:
421 case Lexer::LEX_UNDEF:
422 lexrc.printError("Unknown TextClass tag `$$Token'");
430 // used below to track whether we are in an IfStyle or IfCounter tag.
431 bool modifystyle = false;
432 bool providestyle = false;
433 bool ifcounter = false;
435 switch (static_cast<TextClassTags>(le)) {
439 format = lexrc.getInteger();
442 case TC_OUTPUTFORMAT:
444 outputFormat_ = lexrc.getString();
448 readOutputType(lexrc);
449 switch(outputType_) {
451 outputFormat_ = "latex";
454 outputFormat_ = "docbook";
457 outputFormat_ = "literate";
462 case TC_INPUT: // Include file
464 string const inc = lexrc.getString();
465 FileName tmp = libFileSearch("layouts", inc,
469 lexrc.printError("Could not find input file: " + inc);
471 } else if (!read(tmp, MERGE)) {
472 lexrc.printError("Error reading input file: " + tmp.absFileName());
478 case TC_DEFAULTSTYLE:
480 docstring const name = from_utf8(subst(lexrc.getString(),
482 defaultlayout_ = name;
489 case TC_PROVIDESTYLE:
490 // if modifystyle is true, then we got here by falling through
491 // so we are not in an ProvideStyle block
497 lexrc.printError("No name given for style: `$$Token'.");
501 docstring const name = from_utf8(subst(lexrc.getString(),
504 string s = "Could not read name for style: `$$Token' "
505 + lexrc.getString() + " is probably not valid UTF-8!";
508 // Since we couldn't read the name, we just scan the rest
509 // of the style and discard it.
510 error = !readStyle(lexrc, lay);
514 bool const have_layout = hasLayout(name);
516 // If the layout already exists, then we want to add it to
517 // the existing layout, as long as we are not in an ProvideStyle
519 if (have_layout && !providestyle) {
520 Layout & lay = operator[](name);
521 error = !readStyle(lexrc, lay);
523 // If the layout does not exist, then we want to create a new
524 // one, but not if we are in a ModifyStyle block.
525 else if (!have_layout && !modifystyle) {
527 layout.setName(name);
528 error = !readStyle(lexrc, layout);
530 layoutlist_.push_back(layout);
532 if (defaultlayout_.empty()) {
533 // We do not have a default layout yet, so we choose
534 // the first layout we encounter.
535 defaultlayout_ = name;
538 // There are two ways to get here:
539 // (i) The layout exists but we are in an ProvideStyle block
540 // (ii) The layout doesn't exist, but we are in an ModifyStyle
542 // Either way, we just scan the rest and discard it
545 // false positive from coverity
546 // coverity[CHECKED_RETURN]
547 readStyle(lexrc, lay);
554 docstring const style = from_utf8(subst(lexrc.getString(),
556 if (!deleteLayout(style))
557 lyxerr << "Cannot delete style `"
558 << to_utf8(style) << '\'' << endl;
562 case TC_NOINSETLAYOUT:
564 docstring const style = from_utf8(subst(lexrc.getString(),
566 if (!deleteInsetLayout(style))
567 LYXERR0("Style `" << style << "' cannot be removed\n"
568 "because it was not found!");
574 columns_ = lexrc.getInteger();
579 switch (lexrc.getInteger()) {
580 case 1: sides_ = OneSide; break;
581 case 2: sides_ = TwoSides; break;
583 lyxerr << "Impossible number of page"
584 " sides, setting to one."
594 pagestyle_ = rtrim(lexrc.getString());
598 defaultfont_ = lyxRead(lexrc);
599 if (!defaultfont_.resolved()) {
600 lexrc.printError("Warning: defaultfont should "
601 "be fully instantiated!");
602 defaultfont_.realize(sane_font);
608 secnumdepth_ = lexrc.getInteger();
613 tocdepth_ = lexrc.getInteger();
616 // First step to support options
617 case TC_CLASSOPTIONS:
618 readClassOptions(lexrc);
622 preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
625 case TC_HTMLPREAMBLE:
626 htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
630 htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
633 case TC_HTMLTOCSECTION:
634 html_toc_section_ = from_utf8(trim(lexrc.getString()));
637 case TC_ADDTOPREAMBLE:
638 preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
641 case TC_ADDTOHTMLPREAMBLE:
642 htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
645 case TC_ADDTOHTMLSTYLES:
646 htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
651 string const feature = lexrc.getString();
653 if (lexrc.getInteger())
654 provides_.insert(feature);
656 provides_.erase(feature);
662 vector<string> const req
663 = getVectorFromString(lexrc.getString());
664 requires_.insert(req.begin(), req.end());
670 string const pkg = lexrc.getString();
672 string const options = lexrc.getString();
673 package_options_[pkg] = options;
677 case TC_DEFAULTMODULE: {
679 string const module = lexrc.getString();
680 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
681 default_modules_.push_back(module);
685 case TC_PROVIDESMODULE: {
687 string const module = lexrc.getString();
688 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
689 provided_modules_.push_back(module);
693 case TC_EXCLUDESMODULE: {
695 string const module = lexrc.getString();
696 // modules already have their own way to exclude other modules
698 LYXERR0("ExcludesModule tag cannot be used in a module!");
701 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
702 excluded_modules_.push_back(module);
706 case TC_LEFTMARGIN: // left margin type
708 leftmargin_ = lexrc.getDocString();
711 case TC_RIGHTMARGIN: // right margin type
713 rightmargin_ = lexrc.getDocString();
716 case TC_INSETLAYOUT: {
718 lexrc.printError("No name given for InsetLayout: `$$Token'.");
722 docstring const name = subst(lexrc.getDocString(), '_', ' ');
724 string s = "Could not read name for InsetLayout: `$$Token' "
725 + lexrc.getString() + " is probably not valid UTF-8!";
728 // Since we couldn't read the name, we just scan the rest
729 // of the style and discard it.
730 il.read(lexrc, *this);
731 // Let's try to continue rather than abort.
733 } else if (hasInsetLayout(name)) {
734 InsetLayout & il = insetlayoutlist_[name];
735 error = !il.read(lexrc, *this);
739 error = !il.read(lexrc, *this);
741 insetlayoutlist_[name] = il;
747 error = !readFloat(lexrc);
751 error = !readCiteEngine(lexrc);
754 case TC_CITEENGINETYPE:
756 opt_enginetype_ = rtrim(lexrc.getString());
760 error = !readCiteFormat(lexrc);
763 case TC_CITEFRAMEWORK:
765 citeframework_ = rtrim(lexrc.getString());
768 case TC_DEFAULTBIBLIO:
770 vector<string> const dbs =
771 getVectorFromString(rtrim(lexrc.getString()), "|");
772 vector<string>::const_iterator it = dbs.begin();
773 vector<string>::const_iterator end = dbs.end();
774 for (; it != end; ++it) {
775 if (!contains(*it, ':')) {
776 vector<string> const enginetypes =
777 getVectorFromString(opt_enginetype_, "|");
778 for (string const &s: enginetypes)
779 cite_default_biblio_style_[s] = *it;
782 string const db = split(*it, eng, ':');
783 cite_default_biblio_style_[eng] = db;
789 case TC_FULLAUTHORLIST:
791 cite_full_author_list_ &= lexrc.getBool();
796 docstring const cnt = lexrc.getDocString();
797 if (!counters_.remove(cnt))
798 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
807 docstring const name = lexrc.getDocString();
809 string s = "Could not read name for counter: `$$Token' "
810 + lexrc.getString() + " is probably not valid UTF-8!";
811 lexrc.printError(s.c_str());
813 // Since we couldn't read the name, we just scan the rest
817 error = !counters_.read(lexrc, name, !ifcounter);
820 lexrc.printError("No name given for style: `$$Token'.");
825 case TC_TITLELATEXTYPE:
826 readTitleType(lexrc);
829 case TC_TITLELATEXNAME:
831 titlename_ = lexrc.getString();
836 string const nofloat = lexrc.getString();
837 floatlist_.erase(nofloat);
841 case TC_OUTLINERNAME:
842 error = !readOutlinerName(lexrc);
846 // Note that this is triggered the first time through the loop unless
847 // we hit a format tag.
848 if (format != LAYOUT_FORMAT)
849 return FORMAT_MISMATCH;
852 // at present, we abort if we encounter an error,
853 // so there is no point continuing.
860 if (defaultlayout_.empty()) {
861 LYXERR0("Error: Textclass '" << name_
862 << "' is missing a defaultstyle.");
866 // Try to erase "stdinsets" from the provides_ set.
868 // Provides stdinsets 1
869 // declaration simply tells us that the standard insets have been
870 // defined. (It's found in stdinsets.inc but could also be used in
871 // user-defined files.) There isn't really any such package. So we
872 // might as well go ahead and erase it.
873 // If we do not succeed, then it was not there, which means that
874 // the textclass did not provide the definitions of the standard
875 // insets. So we need to try to load them.
876 int erased = provides_.erase("stdinsets");
878 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
881 frontend::Alert::warning(_("Missing File"),
882 _("Could not find stdinsets.inc! This may lead to data loss!"));
884 } else if (!read(tmp, MERGE)) {
885 frontend::Alert::warning(_("Corrupt File"),
886 _("Could not read stdinsets.inc! This may lead to data loss!"));
891 min_toclevel_ = Layout::NOT_IN_TOC;
892 max_toclevel_ = Layout::NOT_IN_TOC;
893 const_iterator lit = begin();
894 const_iterator len = end();
895 for (; lit != len; ++lit) {
896 int const toclevel = lit->toclevel;
897 if (toclevel != Layout::NOT_IN_TOC) {
898 if (min_toclevel_ == Layout::NOT_IN_TOC)
899 min_toclevel_ = toclevel;
901 min_toclevel_ = min(min_toclevel_, toclevel);
902 max_toclevel_ = max(max_toclevel_, toclevel);
905 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
906 << ", maximum is " << max_toclevel_);
908 return (error ? ERROR : OK);
912 void TextClass::readTitleType(Lexer & lexrc)
914 LexerKeyword titleTypeTags[] = {
915 { "commandafter", TITLE_COMMAND_AFTER },
916 { "environment", TITLE_ENVIRONMENT }
919 PushPopHelper pph(lexrc, titleTypeTags);
921 int le = lexrc.lex();
923 case Lexer::LEX_UNDEF:
924 lexrc.printError("Unknown output type `$$Token'");
926 case TITLE_COMMAND_AFTER:
927 case TITLE_ENVIRONMENT:
928 titletype_ = static_cast<TitleLatexType>(le);
931 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
937 void TextClass::readOutputType(Lexer & lexrc)
939 LexerKeyword outputTypeTags[] = {
940 { "docbook", DOCBOOK },
942 { "literate", LITERATE }
945 PushPopHelper pph(lexrc, outputTypeTags);
947 int le = lexrc.lex();
949 case Lexer::LEX_UNDEF:
950 lexrc.printError("Unknown output type `$$Token'");
955 outputType_ = static_cast<OutputType>(le);
958 LYXERR0("Unhandled value " << le);
964 void TextClass::readClassOptions(Lexer & lexrc)
974 LexerKeyword classOptionsTags[] = {
976 {"fontsize", CO_FONTSIZE },
977 {"header", CO_HEADER },
978 {"other", CO_OTHER },
979 {"pagestyle", CO_PAGESTYLE }
982 lexrc.pushTable(classOptionsTags);
984 while (!getout && lexrc.isOK()) {
985 int le = lexrc.lex();
987 case Lexer::LEX_UNDEF:
988 lexrc.printError("Unknown ClassOption tag `$$Token'");
996 opt_fontsize_ = rtrim(lexrc.getString());
1000 opt_pagestyle_ = rtrim(lexrc.getString());
1004 if (options_.empty())
1005 options_ = lexrc.getString();
1007 options_ += ',' + lexrc.getString();
1011 class_header_ = subst(lexrc.getString(), """, "\"");
1022 bool TextClass::readCiteEngine(Lexer & lexrc)
1024 int const type = readCiteEngineType(lexrc);
1025 if (type & ENGINE_TYPE_AUTHORYEAR)
1026 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1027 if (type & ENGINE_TYPE_NUMERICAL)
1028 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1029 if (type & ENGINE_TYPE_DEFAULT)
1030 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1032 bool getout = false;
1033 while (!getout && lexrc.isOK()) {
1035 def = lexrc.getString();
1036 def = subst(def, " ", "");
1037 def = subst(def, "\t", "");
1038 if (compare_ascii_no_case(def, "end") == 0) {
1043 char ichar = def[0];
1046 if (isUpperCase(ichar)) {
1047 cs.forceUpperCase = true;
1048 def[0] = lowercase(ichar);
1051 /** For portability reasons (between different
1052 * cite engines such as natbib and biblatex),
1053 * we distinguish between:
1054 * 1. The LyX name as output in the LyX file
1055 * 2. Possible aliases that might fall back to
1056 * the given LyX name in the current engine
1057 * 3. The actual LaTeX command that is output
1058 * (2) and (3) are optional.
1059 * Also, the GUI string for the starred version can
1062 * LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1071 ScanMode mode = LyXName;
1072 ScanMode oldmode = LyXName;
1077 size_t const n = def.size();
1078 for (size_t i = 0; i != n; ++i) {
1082 else if (ichar == '=')
1084 else if (ichar == '<') {
1087 } else if (ichar == '>')
1089 else if (mode == LaTeXCmd)
1091 else if (mode == StarDesc)
1093 else if (ichar == '*')
1094 cs.hasStarredVersion = true;
1095 else if (ichar == '[' && cs.textAfter)
1096 cs.textBefore = true;
1097 else if (ichar == '[')
1098 cs.textAfter = true;
1099 else if (ichar != ']') {
1107 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1108 if (!alias.empty()) {
1109 vector<string> const aliases = getVectorFromString(alias);
1110 for (string const &s: aliases)
1111 cite_command_aliases_[s] = lyx_cmd;
1113 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1114 int size = stardesc.size();
1116 cs.stardesc = stardescs[0];
1118 cs.startooltip = stardescs[1];
1119 if (type & ENGINE_TYPE_AUTHORYEAR)
1120 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1121 if (type & ENGINE_TYPE_NUMERICAL)
1122 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1123 if (type & ENGINE_TYPE_DEFAULT)
1124 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1130 int TextClass::readCiteEngineType(Lexer & lexrc) const
1132 LATTEST(ENGINE_TYPE_DEFAULT ==
1133 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1134 if (!lexrc.next()) {
1135 lexrc.printError("No cite engine type given for token: `$$Token'.");
1136 return ENGINE_TYPE_DEFAULT;
1138 string const type = rtrim(lexrc.getString());
1139 if (compare_ascii_no_case(type, "authoryear") == 0)
1140 return ENGINE_TYPE_AUTHORYEAR;
1141 else if (compare_ascii_no_case(type, "numerical") == 0)
1142 return ENGINE_TYPE_NUMERICAL;
1143 else if (compare_ascii_no_case(type, "default") != 0) {
1144 string const s = "Unknown cite engine type `" + type
1145 + "' given for token: `$$Token',";
1146 lexrc.printError(s);
1148 return ENGINE_TYPE_DEFAULT;
1152 bool TextClass::readCiteFormat(Lexer & lexrc)
1154 int const type = readCiteEngineType(lexrc);
1157 while (lexrc.isOK()) {
1159 etype = lexrc.getString();
1160 if (compare_ascii_no_case(etype, "end") == 0)
1165 definition = lexrc.getString();
1166 char initchar = etype[0];
1167 if (initchar == '#')
1169 if (initchar == '!' || initchar == '_') {
1170 if (type & ENGINE_TYPE_AUTHORYEAR)
1171 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1172 if (type & ENGINE_TYPE_NUMERICAL)
1173 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1174 if (type & ENGINE_TYPE_DEFAULT)
1175 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1177 if (type & ENGINE_TYPE_AUTHORYEAR)
1178 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1179 if (type & ENGINE_TYPE_NUMERICAL)
1180 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1181 if (type & ENGINE_TYPE_DEFAULT)
1182 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1189 bool TextClass::readFloat(Lexer & lexrc)
1206 FT_ALLOWED_PLACEMENT,
1212 LexerKeyword floatTags[] = {
1213 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1214 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1215 { "allowswide", FT_ALLOWS_WIDE },
1217 { "extension", FT_EXT },
1218 { "guiname", FT_NAME },
1219 { "htmlattr", FT_HTMLATTR },
1220 { "htmlstyle", FT_HTMLSTYLE },
1221 { "htmltag", FT_HTMLTAG },
1222 { "ispredefined", FT_PREDEFINED },
1223 { "listcommand", FT_LISTCOMMAND },
1224 { "listname", FT_LISTNAME },
1225 { "numberwithin", FT_WITHIN },
1226 { "placement", FT_PLACEMENT },
1227 { "refprefix", FT_REFPREFIX },
1228 { "style", FT_STYLE },
1229 { "type", FT_TYPE },
1230 { "usesfloatpkg", FT_USESFLOAT }
1233 lexrc.pushTable(floatTags);
1237 docstring htmlstyle;
1243 string allowed_placement = "!htbpH";
1248 bool usesfloat = true;
1249 bool ispredefined = false;
1250 bool allowswide = true;
1251 bool allowssideways = true;
1253 bool getout = false;
1254 while (!getout && lexrc.isOK()) {
1255 int le = lexrc.lex();
1257 case Lexer::LEX_UNDEF:
1258 lexrc.printError("Unknown float tag `$$Token'");
1266 type = lexrc.getString();
1267 if (floatlist_.typeExist(type)) {
1268 Floating const & fl = floatlist_.getType(type);
1269 placement = fl.placement();
1271 within = fl.within();
1274 listname = fl.listName();
1275 usesfloat = fl.usesFloatPkg();
1276 ispredefined = fl.isPredefined();
1277 listcommand = fl.listCommand();
1278 refprefix = fl.refPrefix();
1283 name = lexrc.getString();
1287 placement = lexrc.getString();
1289 case FT_ALLOWED_PLACEMENT:
1291 allowed_placement = lexrc.getString();
1295 ext = lexrc.getString();
1299 within = lexrc.getString();
1300 if (within == "none")
1305 style = lexrc.getString();
1307 case FT_LISTCOMMAND:
1309 listcommand = lexrc.getString();
1313 refprefix = lexrc.getString();
1317 listname = lexrc.getString();
1321 usesfloat = lexrc.getBool();
1325 ispredefined = lexrc.getBool();
1327 case FT_ALLOWS_SIDEWAYS:
1329 allowssideways = lexrc.getBool();
1331 case FT_ALLOWS_WIDE:
1333 allowswide = lexrc.getBool();
1337 htmlattr = lexrc.getString();
1341 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1345 htmltag = lexrc.getString();
1355 // Here we have a full float if getout == true
1357 if (!usesfloat && listcommand.empty()) {
1358 // if this float uses the same auxfile as an existing one,
1359 // there is no need for it to provide a list command.
1360 FloatList::const_iterator it = floatlist_.begin();
1361 FloatList::const_iterator en = floatlist_.end();
1362 bool found_ext = false;
1363 for (; it != en; ++it) {
1364 if (it->second.ext() == ext) {
1370 LYXERR0("The layout does not provide a list command " <<
1371 "for the float `" << type << "'. LyX will " <<
1372 "not be able to produce a float list.");
1374 Floating fl(type, placement, ext, within, style, name,
1375 listname, listcommand, refprefix, allowed_placement,
1376 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1377 allowswide, allowssideways);
1378 floatlist_.newFloat(fl);
1379 // each float has its own counter
1380 counters_.newCounter(from_ascii(type), from_ascii(within),
1381 docstring(), docstring());
1382 // also define sub-float counters
1383 docstring const subtype = "sub-" + from_ascii(type);
1384 counters_.newCounter(subtype, from_ascii(type),
1385 "\\alph{" + subtype + "}", docstring());
1391 bool TextClass::readOutlinerName(Lexer & lexrc)
1396 type = lexrc.getString();
1398 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1402 name = lexrc.getDocString();
1404 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1407 outliner_names_[type] = name;
1412 docstring TextClass::outlinerName(std::string const & type) const
1414 std::map<std::string,docstring>::const_iterator const it
1415 = outliner_names_.find(type);
1416 if (it == outliner_names_.end()) {
1417 LYXERR0("Missing OutlinerName for " << type << "!");
1418 return from_utf8(type);
1424 string const & TextClass::prerequisites(string const & sep) const
1426 if (contains(prerequisites_, ',')) {
1427 vector<string> const pres = getVectorFromString(prerequisites_);
1428 prerequisites_ = getStringFromVector(pres, sep);
1430 return prerequisites_;
1434 bool TextClass::hasLayout(docstring const & n) const
1436 docstring const name = n.empty() ? defaultLayoutName() : n;
1438 return find_if(layoutlist_.begin(), layoutlist_.end(),
1439 LayoutNamesEqual(name))
1440 != layoutlist_.end();
1444 bool TextClass::hasInsetLayout(docstring const & n) const
1448 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1449 return it != insetlayoutlist_.end();
1453 Layout const & TextClass::operator[](docstring const & name) const
1455 LATTEST(!name.empty());
1458 find_if(begin(), end(), LayoutNamesEqual(name));
1461 LYXERR0("We failed to find the layout '" << name
1462 << "' in the layout list. You MUST investigate!");
1463 for (const_iterator cit = begin(); cit != end(); ++cit)
1464 lyxerr << " " << to_utf8(cit->name()) << endl;
1466 // We require the name to exist
1467 static const Layout dummy;
1468 LASSERT(false, return dummy);
1475 Layout & TextClass::operator[](docstring const & name)
1477 LATTEST(!name.empty());
1478 // Safe to continue, given what we do below.
1480 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1483 LYXERR0("We failed to find the layout '" << to_utf8(name)
1484 << "' in the layout list. You MUST investigate!");
1485 for (const_iterator cit = begin(); cit != end(); ++cit)
1486 LYXERR0(" " << to_utf8(cit->name()));
1488 // we require the name to exist
1490 // we are here only in release mode
1491 layoutlist_.push_back(createBasicLayout(name, true));
1492 it = find_if(begin(), end(), LayoutNamesEqual(name));
1499 bool TextClass::deleteLayout(docstring const & name)
1501 if (name == defaultLayoutName() || name == plainLayoutName())
1504 LayoutList::iterator it =
1505 remove_if(layoutlist_.begin(), layoutlist_.end(),
1506 LayoutNamesEqual(name));
1508 LayoutList::iterator end = layoutlist_.end();
1509 bool const ret = (it != end);
1510 layoutlist_.erase(it, end);
1515 bool TextClass::deleteInsetLayout(docstring const & name)
1517 return insetlayoutlist_.erase(name);
1521 // Load textclass info if not loaded yet
1522 bool TextClass::load(string const & path) const
1527 // Read style-file, provided path is searched before the system ones
1528 // If path is a file, it is loaded directly.
1529 FileName layout_file(path);
1530 if (!path.empty() && !layout_file.isReadableFile())
1531 layout_file = FileName(addName(path, name_ + ".layout"));
1532 if (layout_file.empty() || !layout_file.exists())
1533 layout_file = libFileSearch("layouts", name_, "layout");
1534 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1537 lyxerr << "Error reading `"
1538 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1539 << "'\n(Check `" << name_
1540 << "')\nCheck your installation and "
1541 "try Options/Reconfigure..."
1549 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1554 layoutlist_.push_back(createBasicLayout(n, true));
1559 string DocumentClass::forcedLayouts() const
1563 const_iterator const e = end();
1564 for (const_iterator i = begin(); i != e; ++i) {
1565 if (i->forcelocal > 0) {
1567 os << "Format " << LAYOUT_FORMAT << '\n';
1577 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1579 // FIXME The fix for the InsetLayout part of 4812 would be here:
1580 // Add the InsetLayout to the document class if it is not found.
1582 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1583 while (!n.empty()) {
1584 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1585 if (cit != cen && cit->first == n) {
1586 if (cit->second.obsoleted_by().empty())
1588 n = cit->second.obsoleted_by();
1589 return insetLayout(n);
1591 // If we have a generic prefix (e.g., "Note:"),
1592 // try if this one alone is found.
1593 size_t i = n.find(':');
1594 if (i == string::npos)
1598 // Layout "name" not found.
1599 return plainInsetLayout();
1603 InsetLayout const & DocumentClass::plainInsetLayout() {
1604 static const InsetLayout plain_insetlayout_;
1605 return plain_insetlayout_;
1609 docstring const & TextClass::defaultLayoutName() const
1611 return defaultlayout_;
1615 Layout const & TextClass::defaultLayout() const
1617 return operator[](defaultLayoutName());
1621 bool TextClass::isDefaultLayout(Layout const & layout) const
1623 return layout.name() == defaultLayoutName();
1627 bool TextClass::isPlainLayout(Layout const & layout) const
1629 return layout.name() == plainLayoutName();
1633 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1635 static Layout * defaultLayout = NULL;
1637 if (defaultLayout) {
1638 defaultLayout->setUnknown(unknown);
1639 defaultLayout->setName(name);
1640 return *defaultLayout;
1643 static char const * s = "Margin Static\n"
1644 "LatexType Paragraph\n"
1647 "AlignPossible Left, Right, Center\n"
1648 "LabelType No_Label\n"
1650 istringstream ss(s);
1651 Lexer lex(textClassTags);
1653 defaultLayout = new Layout;
1654 defaultLayout->setUnknown(unknown);
1655 defaultLayout->setName(name);
1656 if (!readStyle(lex, *defaultLayout)) {
1657 // The only way this happens is because the hardcoded layout above
1661 return *defaultLayout;
1665 DocumentClassPtr getDocumentClass(
1666 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1667 LayoutModuleList const & celist,
1670 DocumentClassPtr doc_class =
1671 DocumentClassPtr(new DocumentClass(baseClass));
1672 LayoutModuleList::const_iterator it = modlist.begin();
1673 LayoutModuleList::const_iterator en = modlist.end();
1674 for (; it != en; ++it) {
1675 string const modName = *it;
1676 LyXModule * lm = theModuleList[modName];
1678 docstring const msg =
1679 bformat(_("The module %1$s has been requested by\n"
1680 "this document but has not been found in the list of\n"
1681 "available modules. If you recently installed it, you\n"
1682 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1684 frontend::Alert::warning(_("Module not available"), msg);
1687 if (!lm->isAvailable() && !clone) {
1688 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1689 docstring const msg =
1690 bformat(_("The module %1$s requires a package that is not\n"
1691 "available in your LaTeX installation, or a converter that\n"
1692 "you have not installed. LaTeX output may not be possible.\n"
1693 "Missing prerequisites:\n"
1695 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1696 from_utf8(modName), prereqs);
1697 frontend::Alert::warning(_("Package not available"), msg, true);
1699 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1700 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1701 docstring const msg =
1702 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1703 frontend::Alert::warning(_("Read Error"), msg);
1707 LayoutModuleList::const_iterator cit = celist.begin();
1708 LayoutModuleList::const_iterator cen = celist.end();
1709 for (; cit != cen; ++cit) {
1710 string const ceName = *cit;
1711 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1713 docstring const msg =
1714 bformat(_("The cite engine %1$s has been requested by\n"
1715 "this document but has not been found in the list of\n"
1716 "available engines. If you recently installed it, you\n"
1717 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1719 frontend::Alert::warning(_("Cite Engine not available"), msg);
1722 if (!ce->isAvailable() && !clone) {
1723 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1724 docstring const msg =
1725 bformat(_("The cite engine %1$s requires a package that is not\n"
1726 "available in your LaTeX installation, or a converter that\n"
1727 "you have not installed. LaTeX output may not be possible.\n"
1728 "Missing prerequisites:\n"
1730 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1731 from_utf8(ceName), prereqs);
1732 frontend::Alert::warning(_("Package not available"), msg, true);
1734 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1735 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1736 docstring const msg =
1737 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1738 frontend::Alert::warning(_("Read Error"), msg);
1746 /////////////////////////////////////////////////////////////////////////
1750 /////////////////////////////////////////////////////////////////////////
1752 DocumentClass::DocumentClass(LayoutFile const & tc)
1757 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1759 LayoutList::const_iterator it = layoutlist_.begin();
1760 LayoutList::const_iterator end = layoutlist_.end();
1761 for (; it != end; ++it)
1762 if (it->latexname() == lay)
1768 bool DocumentClass::provides(string const & p) const
1770 return provides_.find(p) != provides_.end();
1774 bool DocumentClass::hasTocLevels() const
1776 return min_toclevel_ != Layout::NOT_IN_TOC;
1780 Layout const & DocumentClass::getTOCLayout() const
1782 // we're going to look for the layout with the minimum toclevel
1783 TextClass::LayoutList::const_iterator lit = begin();
1784 TextClass::LayoutList::const_iterator const len = end();
1785 int minlevel = 1000;
1786 Layout const * lay = NULL;
1787 for (; lit != len; ++lit) {
1788 int const level = lit->toclevel;
1789 // we don't want Part or unnumbered sections
1790 if (level == Layout::NOT_IN_TOC || level < 0
1791 || level >= minlevel || lit->counter.empty())
1798 // hmm. that is very odd, so we'll do our best.
1799 return operator[](defaultLayoutName());
1803 Layout const & DocumentClass::htmlTOCLayout() const
1805 if (html_toc_section_.empty())
1806 html_toc_section_ = getTOCLayout().name();
1807 return operator[](html_toc_section_);
1811 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1812 string const & entry, string const & fallback) const
1814 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1816 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1817 if (itype == cite_formats_.end())
1818 return default_format;
1819 map<string, string>::const_iterator it = itype->second.find(entry);
1820 if (it == itype->second.end() && !fallback.empty())
1821 it = itype->second.find(fallback);
1822 if (it == itype->second.end())
1823 return default_format;
1828 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1829 string const & macro) const
1831 static string empty;
1832 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1833 if (itype == cite_macros_.end())
1835 map<string, string>::const_iterator it = itype->second.find(macro);
1836 if (it == itype->second.end())
1842 vector<string> const DocumentClass::citeCommands(
1843 CiteEngineType const & type) const
1845 vector<CitationStyle> const styles = citeStyles(type);
1846 vector<CitationStyle>::const_iterator it = styles.begin();
1847 vector<CitationStyle>::const_iterator end = styles.end();
1848 vector<string> cmds;
1849 for (; it != end; ++it) {
1850 CitationStyle const cite = *it;
1851 cmds.push_back(cite.name);
1857 vector<CitationStyle> const & DocumentClass::citeStyles(
1858 CiteEngineType const & type) const
1860 static vector<CitationStyle> empty;
1861 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1862 if (it == cite_styles_.end())
1868 /////////////////////////////////////////////////////////////////////////
1872 /////////////////////////////////////////////////////////////////////////
1874 ostream & operator<<(ostream & os, PageSides p)