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 cite_default_biblio_style_[opt_enginetype_] = *it;
779 string const db = split(*it, eng, ':');
780 cite_default_biblio_style_[eng] = db;
786 case TC_FULLAUTHORLIST:
788 cite_full_author_list_ &= lexrc.getBool();
793 docstring const cnt = lexrc.getDocString();
794 if (!counters_.remove(cnt))
795 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
804 docstring const name = lexrc.getDocString();
806 string s = "Could not read name for counter: `$$Token' "
807 + lexrc.getString() + " is probably not valid UTF-8!";
808 lexrc.printError(s.c_str());
810 // Since we couldn't read the name, we just scan the rest
814 error = !counters_.read(lexrc, name, !ifcounter);
817 lexrc.printError("No name given for style: `$$Token'.");
822 case TC_TITLELATEXTYPE:
823 readTitleType(lexrc);
826 case TC_TITLELATEXNAME:
828 titlename_ = lexrc.getString();
833 string const nofloat = lexrc.getString();
834 floatlist_.erase(nofloat);
838 case TC_OUTLINERNAME:
839 error = !readOutlinerName(lexrc);
843 // Note that this is triggered the first time through the loop unless
844 // we hit a format tag.
845 if (format != LAYOUT_FORMAT)
846 return FORMAT_MISMATCH;
849 // at present, we abort if we encounter an error,
850 // so there is no point continuing.
857 if (defaultlayout_.empty()) {
858 LYXERR0("Error: Textclass '" << name_
859 << "' is missing a defaultstyle.");
863 // Try to erase "stdinsets" from the provides_ set.
865 // Provides stdinsets 1
866 // declaration simply tells us that the standard insets have been
867 // defined. (It's found in stdinsets.inc but could also be used in
868 // user-defined files.) There isn't really any such package. So we
869 // might as well go ahead and erase it.
870 // If we do not succeed, then it was not there, which means that
871 // the textclass did not provide the definitions of the standard
872 // insets. So we need to try to load them.
873 int erased = provides_.erase("stdinsets");
875 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
878 frontend::Alert::warning(_("Missing File"),
879 _("Could not find stdinsets.inc! This may lead to data loss!"));
881 } else if (!read(tmp, MERGE)) {
882 frontend::Alert::warning(_("Corrupt File"),
883 _("Could not read stdinsets.inc! This may lead to data loss!"));
888 min_toclevel_ = Layout::NOT_IN_TOC;
889 max_toclevel_ = Layout::NOT_IN_TOC;
890 const_iterator lit = begin();
891 const_iterator len = end();
892 for (; lit != len; ++lit) {
893 int const toclevel = lit->toclevel;
894 if (toclevel != Layout::NOT_IN_TOC) {
895 if (min_toclevel_ == Layout::NOT_IN_TOC)
896 min_toclevel_ = toclevel;
898 min_toclevel_ = min(min_toclevel_, toclevel);
899 max_toclevel_ = max(max_toclevel_, toclevel);
902 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
903 << ", maximum is " << max_toclevel_);
905 return (error ? ERROR : OK);
909 void TextClass::readTitleType(Lexer & lexrc)
911 LexerKeyword titleTypeTags[] = {
912 { "commandafter", TITLE_COMMAND_AFTER },
913 { "environment", TITLE_ENVIRONMENT }
916 PushPopHelper pph(lexrc, titleTypeTags);
918 int le = lexrc.lex();
920 case Lexer::LEX_UNDEF:
921 lexrc.printError("Unknown output type `$$Token'");
923 case TITLE_COMMAND_AFTER:
924 case TITLE_ENVIRONMENT:
925 titletype_ = static_cast<TitleLatexType>(le);
928 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
934 void TextClass::readOutputType(Lexer & lexrc)
936 LexerKeyword outputTypeTags[] = {
937 { "docbook", DOCBOOK },
939 { "literate", LITERATE }
942 PushPopHelper pph(lexrc, outputTypeTags);
944 int le = lexrc.lex();
946 case Lexer::LEX_UNDEF:
947 lexrc.printError("Unknown output type `$$Token'");
952 outputType_ = static_cast<OutputType>(le);
955 LYXERR0("Unhandled value " << le);
961 void TextClass::readClassOptions(Lexer & lexrc)
971 LexerKeyword classOptionsTags[] = {
973 {"fontsize", CO_FONTSIZE },
974 {"header", CO_HEADER },
975 {"other", CO_OTHER },
976 {"pagestyle", CO_PAGESTYLE }
979 lexrc.pushTable(classOptionsTags);
981 while (!getout && lexrc.isOK()) {
982 int le = lexrc.lex();
984 case Lexer::LEX_UNDEF:
985 lexrc.printError("Unknown ClassOption tag `$$Token'");
993 opt_fontsize_ = rtrim(lexrc.getString());
997 opt_pagestyle_ = rtrim(lexrc.getString());
1001 if (options_.empty())
1002 options_ = lexrc.getString();
1004 options_ += ',' + lexrc.getString();
1008 class_header_ = subst(lexrc.getString(), """, "\"");
1019 bool TextClass::readCiteEngine(Lexer & lexrc)
1021 int const type = readCiteEngineType(lexrc);
1022 if (type & ENGINE_TYPE_AUTHORYEAR)
1023 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1024 if (type & ENGINE_TYPE_NUMERICAL)
1025 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1026 if (type & ENGINE_TYPE_DEFAULT)
1027 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1029 bool getout = false;
1030 while (!getout && lexrc.isOK()) {
1032 def = lexrc.getString();
1033 def = subst(def, " ", "");
1034 def = subst(def, "\t", "");
1035 if (compare_ascii_no_case(def, "end") == 0) {
1041 char ichar = def[0];
1045 cs.forceUpperCase = true;
1049 size_t const n = def.size();
1050 for (size_t i = 0; i != n; ++i) {
1053 cs.fullAuthorList = true;
1054 else if (ichar == '[' && cs.textAfter)
1055 cs.textBefore = true;
1056 else if (ichar == '[')
1057 cs.textAfter = true;
1058 else if (ichar != ']')
1063 if (type & ENGINE_TYPE_AUTHORYEAR)
1064 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1065 if (type & ENGINE_TYPE_NUMERICAL)
1066 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1067 if (type & ENGINE_TYPE_DEFAULT)
1068 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1074 int TextClass::readCiteEngineType(Lexer & lexrc) const
1076 LATTEST(ENGINE_TYPE_DEFAULT ==
1077 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1078 if (!lexrc.next()) {
1079 lexrc.printError("No cite engine type given for token: `$$Token'.");
1080 return ENGINE_TYPE_DEFAULT;
1082 string const type = rtrim(lexrc.getString());
1083 if (compare_ascii_no_case(type, "authoryear") == 0)
1084 return ENGINE_TYPE_AUTHORYEAR;
1085 else if (compare_ascii_no_case(type, "numerical") == 0)
1086 return ENGINE_TYPE_NUMERICAL;
1087 else if (compare_ascii_no_case(type, "default") != 0) {
1088 string const s = "Unknown cite engine type `" + type
1089 + "' given for token: `$$Token',";
1090 lexrc.printError(s);
1092 return ENGINE_TYPE_DEFAULT;
1096 bool TextClass::readCiteFormat(Lexer & lexrc)
1098 int const type = readCiteEngineType(lexrc);
1101 while (lexrc.isOK()) {
1103 etype = lexrc.getString();
1104 if (compare_ascii_no_case(etype, "end") == 0)
1109 definition = lexrc.getString();
1110 char initchar = etype[0];
1111 if (initchar == '#')
1113 if (initchar == '!' || initchar == '_') {
1114 if (type & ENGINE_TYPE_AUTHORYEAR)
1115 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1116 if (type & ENGINE_TYPE_NUMERICAL)
1117 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1118 if (type & ENGINE_TYPE_DEFAULT)
1119 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1121 if (type & ENGINE_TYPE_AUTHORYEAR)
1122 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1123 if (type & ENGINE_TYPE_NUMERICAL)
1124 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1125 if (type & ENGINE_TYPE_DEFAULT)
1126 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1133 bool TextClass::readFloat(Lexer & lexrc)
1150 FT_ALLOWED_PLACEMENT,
1156 LexerKeyword floatTags[] = {
1157 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1158 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1159 { "allowswide", FT_ALLOWS_WIDE },
1161 { "extension", FT_EXT },
1162 { "guiname", FT_NAME },
1163 { "htmlattr", FT_HTMLATTR },
1164 { "htmlstyle", FT_HTMLSTYLE },
1165 { "htmltag", FT_HTMLTAG },
1166 { "ispredefined", FT_PREDEFINED },
1167 { "listcommand", FT_LISTCOMMAND },
1168 { "listname", FT_LISTNAME },
1169 { "numberwithin", FT_WITHIN },
1170 { "placement", FT_PLACEMENT },
1171 { "refprefix", FT_REFPREFIX },
1172 { "style", FT_STYLE },
1173 { "type", FT_TYPE },
1174 { "usesfloatpkg", FT_USESFLOAT }
1177 lexrc.pushTable(floatTags);
1181 docstring htmlstyle;
1187 string allowed_placement = "!htbpH";
1192 bool usesfloat = true;
1193 bool ispredefined = false;
1194 bool allowswide = true;
1195 bool allowssideways = true;
1197 bool getout = false;
1198 while (!getout && lexrc.isOK()) {
1199 int le = lexrc.lex();
1201 case Lexer::LEX_UNDEF:
1202 lexrc.printError("Unknown float tag `$$Token'");
1210 type = lexrc.getString();
1211 if (floatlist_.typeExist(type)) {
1212 Floating const & fl = floatlist_.getType(type);
1213 placement = fl.placement();
1215 within = fl.within();
1218 listname = fl.listName();
1219 usesfloat = fl.usesFloatPkg();
1220 ispredefined = fl.isPredefined();
1221 listcommand = fl.listCommand();
1222 refprefix = fl.refPrefix();
1227 name = lexrc.getString();
1231 placement = lexrc.getString();
1233 case FT_ALLOWED_PLACEMENT:
1235 allowed_placement = lexrc.getString();
1239 ext = lexrc.getString();
1243 within = lexrc.getString();
1244 if (within == "none")
1249 style = lexrc.getString();
1251 case FT_LISTCOMMAND:
1253 listcommand = lexrc.getString();
1257 refprefix = lexrc.getString();
1261 listname = lexrc.getString();
1265 usesfloat = lexrc.getBool();
1269 ispredefined = lexrc.getBool();
1271 case FT_ALLOWS_SIDEWAYS:
1273 allowssideways = lexrc.getBool();
1275 case FT_ALLOWS_WIDE:
1277 allowswide = lexrc.getBool();
1281 htmlattr = lexrc.getString();
1285 htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1289 htmltag = lexrc.getString();
1299 // Here we have a full float if getout == true
1301 if (!usesfloat && listcommand.empty()) {
1302 // if this float uses the same auxfile as an existing one,
1303 // there is no need for it to provide a list command.
1304 FloatList::const_iterator it = floatlist_.begin();
1305 FloatList::const_iterator en = floatlist_.end();
1306 bool found_ext = false;
1307 for (; it != en; ++it) {
1308 if (it->second.ext() == ext) {
1314 LYXERR0("The layout does not provide a list command " <<
1315 "for the float `" << type << "'. LyX will " <<
1316 "not be able to produce a float list.");
1318 Floating fl(type, placement, ext, within, style, name,
1319 listname, listcommand, refprefix, allowed_placement,
1320 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1321 allowswide, allowssideways);
1322 floatlist_.newFloat(fl);
1323 // each float has its own counter
1324 counters_.newCounter(from_ascii(type), from_ascii(within),
1325 docstring(), docstring());
1326 // also define sub-float counters
1327 docstring const subtype = "sub-" + from_ascii(type);
1328 counters_.newCounter(subtype, from_ascii(type),
1329 "\\alph{" + subtype + "}", docstring());
1335 bool TextClass::readOutlinerName(Lexer & lexrc)
1340 type = lexrc.getString();
1342 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1346 name = lexrc.getDocString();
1348 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1351 outliner_names_[type] = name;
1356 docstring TextClass::outlinerName(std::string const & type) const
1358 std::map<std::string,docstring>::const_iterator const it
1359 = outliner_names_.find(type);
1360 if (it == outliner_names_.end()) {
1361 LYXERR0("Missing OutlinerName for " << type << "!");
1362 return from_utf8(type);
1368 string const & TextClass::prerequisites(string const & sep) const
1370 if (contains(prerequisites_, ',')) {
1371 vector<string> const pres = getVectorFromString(prerequisites_);
1372 prerequisites_ = getStringFromVector(pres, sep);
1374 return prerequisites_;
1378 bool TextClass::hasLayout(docstring const & n) const
1380 docstring const name = n.empty() ? defaultLayoutName() : n;
1382 return find_if(layoutlist_.begin(), layoutlist_.end(),
1383 LayoutNamesEqual(name))
1384 != layoutlist_.end();
1388 bool TextClass::hasInsetLayout(docstring const & n) const
1392 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1393 return it != insetlayoutlist_.end();
1397 Layout const & TextClass::operator[](docstring const & name) const
1399 LATTEST(!name.empty());
1402 find_if(begin(), end(), LayoutNamesEqual(name));
1405 LYXERR0("We failed to find the layout '" << name
1406 << "' in the layout list. You MUST investigate!");
1407 for (const_iterator cit = begin(); cit != end(); ++cit)
1408 lyxerr << " " << to_utf8(cit->name()) << endl;
1410 // We require the name to exist
1411 static const Layout dummy;
1412 LASSERT(false, return dummy);
1419 Layout & TextClass::operator[](docstring const & name)
1421 LATTEST(!name.empty());
1422 // Safe to continue, given what we do below.
1424 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1427 LYXERR0("We failed to find the layout '" << to_utf8(name)
1428 << "' in the layout list. You MUST investigate!");
1429 for (const_iterator cit = begin(); cit != end(); ++cit)
1430 LYXERR0(" " << to_utf8(cit->name()));
1432 // we require the name to exist
1434 // we are here only in release mode
1435 layoutlist_.push_back(createBasicLayout(name, true));
1436 it = find_if(begin(), end(), LayoutNamesEqual(name));
1443 bool TextClass::deleteLayout(docstring const & name)
1445 if (name == defaultLayoutName() || name == plainLayoutName())
1448 LayoutList::iterator it =
1449 remove_if(layoutlist_.begin(), layoutlist_.end(),
1450 LayoutNamesEqual(name));
1452 LayoutList::iterator end = layoutlist_.end();
1453 bool const ret = (it != end);
1454 layoutlist_.erase(it, end);
1459 bool TextClass::deleteInsetLayout(docstring const & name)
1461 return insetlayoutlist_.erase(name);
1465 // Load textclass info if not loaded yet
1466 bool TextClass::load(string const & path) const
1471 // Read style-file, provided path is searched before the system ones
1472 // If path is a file, it is loaded directly.
1473 FileName layout_file(path);
1474 if (!path.empty() && !layout_file.isReadableFile())
1475 layout_file = FileName(addName(path, name_ + ".layout"));
1476 if (layout_file.empty() || !layout_file.exists())
1477 layout_file = libFileSearch("layouts", name_, "layout");
1478 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1481 lyxerr << "Error reading `"
1482 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1483 << "'\n(Check `" << name_
1484 << "')\nCheck your installation and "
1485 "try Options/Reconfigure..."
1493 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1498 layoutlist_.push_back(createBasicLayout(n, true));
1503 string DocumentClass::forcedLayouts() const
1507 const_iterator const e = end();
1508 for (const_iterator i = begin(); i != e; ++i) {
1509 if (i->forcelocal > 0) {
1511 os << "Format " << LAYOUT_FORMAT << '\n';
1521 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1523 // FIXME The fix for the InsetLayout part of 4812 would be here:
1524 // Add the InsetLayout to the document class if it is not found.
1526 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1527 while (!n.empty()) {
1528 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1529 if (cit != cen && cit->first == n) {
1530 if (cit->second.obsoleted_by().empty())
1532 n = cit->second.obsoleted_by();
1533 return insetLayout(n);
1535 // If we have a generic prefix (e.g., "Note:"),
1536 // try if this one alone is found.
1537 size_t i = n.find(':');
1538 if (i == string::npos)
1542 // Layout "name" not found.
1543 return plainInsetLayout();
1547 InsetLayout const & DocumentClass::plainInsetLayout() {
1548 static const InsetLayout plain_insetlayout_;
1549 return plain_insetlayout_;
1553 docstring const & TextClass::defaultLayoutName() const
1555 return defaultlayout_;
1559 Layout const & TextClass::defaultLayout() const
1561 return operator[](defaultLayoutName());
1565 bool TextClass::isDefaultLayout(Layout const & layout) const
1567 return layout.name() == defaultLayoutName();
1571 bool TextClass::isPlainLayout(Layout const & layout) const
1573 return layout.name() == plainLayoutName();
1577 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1579 static Layout * defaultLayout = NULL;
1581 if (defaultLayout) {
1582 defaultLayout->setUnknown(unknown);
1583 defaultLayout->setName(name);
1584 return *defaultLayout;
1587 static char const * s = "Margin Static\n"
1588 "LatexType Paragraph\n"
1591 "AlignPossible Left, Right, Center\n"
1592 "LabelType No_Label\n"
1594 istringstream ss(s);
1595 Lexer lex(textClassTags);
1597 defaultLayout = new Layout;
1598 defaultLayout->setUnknown(unknown);
1599 defaultLayout->setName(name);
1600 if (!readStyle(lex, *defaultLayout)) {
1601 // The only way this happens is because the hardcoded layout above
1605 return *defaultLayout;
1609 DocumentClassPtr getDocumentClass(
1610 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1611 LayoutModuleList const & celist,
1614 DocumentClassPtr doc_class =
1615 DocumentClassPtr(new DocumentClass(baseClass));
1616 LayoutModuleList::const_iterator it = modlist.begin();
1617 LayoutModuleList::const_iterator en = modlist.end();
1618 for (; it != en; ++it) {
1619 string const modName = *it;
1620 LyXModule * lm = theModuleList[modName];
1622 docstring const msg =
1623 bformat(_("The module %1$s has been requested by\n"
1624 "this document but has not been found in the list of\n"
1625 "available modules. If you recently installed it, you\n"
1626 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1628 frontend::Alert::warning(_("Module not available"), msg);
1631 if (!lm->isAvailable() && !clone) {
1632 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1633 docstring const msg =
1634 bformat(_("The module %1$s requires a package that is not\n"
1635 "available in your LaTeX installation, or a converter that\n"
1636 "you have not installed. LaTeX output may not be possible.\n"
1637 "Missing prerequisites:\n"
1639 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1640 from_utf8(modName), prereqs);
1641 frontend::Alert::warning(_("Package not available"), msg, true);
1643 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1644 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1645 docstring const msg =
1646 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1647 frontend::Alert::warning(_("Read Error"), msg);
1651 LayoutModuleList::const_iterator cit = celist.begin();
1652 LayoutModuleList::const_iterator cen = celist.end();
1653 for (; cit != cen; ++cit) {
1654 string const ceName = *cit;
1655 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1657 docstring const msg =
1658 bformat(_("The cite engine %1$s has been requested by\n"
1659 "this document but has not been found in the list of\n"
1660 "available engines. If you recently installed it, you\n"
1661 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1663 frontend::Alert::warning(_("Cite Engine not available"), msg);
1666 if (!ce->isAvailable() && !clone) {
1667 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1668 docstring const msg =
1669 bformat(_("The cite engine %1$s requires a package that is not\n"
1670 "available in your LaTeX installation, or a converter that\n"
1671 "you have not installed. LaTeX output may not be possible.\n"
1672 "Missing prerequisites:\n"
1674 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1675 from_utf8(ceName), prereqs);
1676 frontend::Alert::warning(_("Package not available"), msg, true);
1678 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1679 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1680 docstring const msg =
1681 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1682 frontend::Alert::warning(_("Read Error"), msg);
1690 /////////////////////////////////////////////////////////////////////////
1694 /////////////////////////////////////////////////////////////////////////
1696 DocumentClass::DocumentClass(LayoutFile const & tc)
1701 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1703 LayoutList::const_iterator it = layoutlist_.begin();
1704 LayoutList::const_iterator end = layoutlist_.end();
1705 for (; it != end; ++it)
1706 if (it->latexname() == lay)
1712 bool DocumentClass::provides(string const & p) const
1714 return provides_.find(p) != provides_.end();
1718 bool DocumentClass::hasTocLevels() const
1720 return min_toclevel_ != Layout::NOT_IN_TOC;
1724 Layout const & DocumentClass::getTOCLayout() const
1726 // we're going to look for the layout with the minimum toclevel
1727 TextClass::LayoutList::const_iterator lit = begin();
1728 TextClass::LayoutList::const_iterator const len = end();
1729 int minlevel = 1000;
1730 Layout const * lay = NULL;
1731 for (; lit != len; ++lit) {
1732 int const level = lit->toclevel;
1733 // we don't want Part or unnumbered sections
1734 if (level == Layout::NOT_IN_TOC || level < 0
1735 || level >= minlevel || lit->counter.empty())
1742 // hmm. that is very odd, so we'll do our best.
1743 return operator[](defaultLayoutName());
1747 Layout const & DocumentClass::htmlTOCLayout() const
1749 if (html_toc_section_.empty())
1750 html_toc_section_ = getTOCLayout().name();
1751 return operator[](html_toc_section_);
1755 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1756 string const & entry, string const & fallback) const
1758 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1760 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1761 if (itype == cite_formats_.end())
1762 return default_format;
1763 map<string, string>::const_iterator it = itype->second.find(entry);
1764 if (it == itype->second.end() && !fallback.empty())
1765 it = itype->second.find(fallback);
1766 if (it == itype->second.end())
1767 return default_format;
1772 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1773 string const & macro) const
1775 static string empty;
1776 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1777 if (itype == cite_macros_.end())
1779 map<string, string>::const_iterator it = itype->second.find(macro);
1780 if (it == itype->second.end())
1786 vector<string> const DocumentClass::citeCommands(
1787 CiteEngineType const & type) const
1789 vector<CitationStyle> const styles = citeStyles(type);
1790 vector<CitationStyle>::const_iterator it = styles.begin();
1791 vector<CitationStyle>::const_iterator end = styles.end();
1792 vector<string> cmds;
1793 for (; it != end; ++it) {
1794 CitationStyle const cite = *it;
1795 cmds.push_back(cite.cmd);
1801 vector<CitationStyle> const & DocumentClass::citeStyles(
1802 CiteEngineType const & type) const
1804 static vector<CitationStyle> empty;
1805 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1806 if (it == cite_styles_.end())
1812 /////////////////////////////////////////////////////////////////////////
1816 /////////////////////////////////////////////////////////////////////////
1818 ostream & operator<<(ostream & os, PageSides p)