3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
17 #include "TextClass.h"
19 #include "LayoutFile.h"
23 #include "FloatList.h"
27 #include "ModuleList.h"
29 #include "frontends/alert.h"
31 #include "support/lassert.h"
32 #include "support/debug.h"
33 #include "support/ExceptionMessage.h"
34 #include "support/FileName.h"
35 #include "support/filetools.h"
36 #include "support/gettext.h"
37 #include "support/lstrings.h"
38 #include "support/os.h"
39 #include "support/TempFile.h"
50 using namespace lyx::support;
54 // Keep the changes documented in the Customization manual.
56 // If you change this format, then you MUST also make sure that
57 // your changes do not invalidate the hardcoded layout file in
58 // LayoutFile.cpp. Additions will never do so, but syntax changes
59 // could. See LayoutFileList::addEmptyClass() and, especially, the
60 // definition of the layoutpost string.
61 // You should also run the development/tools/updatelayouts.py script,
62 // to update the format of all of our layout files.
64 int const LAYOUT_FORMAT = 58; // rgh: ProvideStyle
68 class LayoutNamesEqual : public unary_function<Layout, bool> {
70 LayoutNamesEqual(docstring const & name)
73 bool operator()(Layout const & c) const
75 return c.name() == name_;
82 bool layout2layout(FileName const & filename, FileName const & tempfile)
84 FileName const script = libFileSearch("scripts", "layout2layout.py");
86 LYXERR0("Could not find layout conversion "
87 "script layout2layout.py.");
91 ostringstream command;
92 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
93 << ' ' << quoteName(filename.toFilesystemEncoding())
94 << ' ' << quoteName(tempfile.toFilesystemEncoding());
95 string const command_str = command.str();
97 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
99 cmd_ret const ret = runCommand(command_str);
100 if (ret.first != 0) {
101 LYXERR0("Could not run layout conversion script layout2layout.py.");
108 string translateReadType(TextClass::ReadType rt)
111 case TextClass::BASECLASS:
113 case TextClass::MERGE:
115 case TextClass::MODULE:
116 return "module file";
117 case TextClass::VALIDATION:
127 // This string should not be translated here,
128 // because it is a layout identifier.
129 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
132 InsetLayout DocumentClass::plain_insetlayout_;
135 /////////////////////////////////////////////////////////////////////////
139 /////////////////////////////////////////////////////////////////////////
141 TextClass::TextClass()
142 : loaded_(false), tex_class_avail_(false),
143 opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
144 opt_pagestyle_("empty|plain|headings|fancy"), pagestyle_("default"),
145 columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3),
146 outputType_(LATEX), outputFormat_("latex"),
147 defaultfont_(sane_font),
148 titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
149 min_toclevel_(0), max_toclevel_(0),
150 cite_full_author_list_(true)
155 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
157 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
158 if (!lay.read(lexrc, *this)) {
159 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
163 lay.resfont = lay.font;
164 lay.resfont.realize(defaultfont_);
165 lay.reslabelfont = lay.labelfont;
166 lay.reslabelfont.realize(defaultfont_);
167 return true; // no errors
206 TC_ADDTOHTMLPREAMBLE,
222 LexerKeyword textClassTags[] = {
223 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
224 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
225 { "addtopreamble", TC_ADDTOPREAMBLE },
226 { "citeengine", TC_CITEENGINE },
227 { "citeenginetype", TC_CITEENGINETYPE },
228 { "citeformat", TC_CITEFORMAT },
229 { "classoptions", TC_CLASSOPTIONS },
230 { "columns", TC_COLUMNS },
231 { "counter", TC_COUNTER },
232 { "defaultbiblio", TC_DEFAULTBIBLIO },
233 { "defaultfont", TC_DEFAULTFONT },
234 { "defaultmodule", TC_DEFAULTMODULE },
235 { "defaultstyle", TC_DEFAULTSTYLE },
236 { "excludesmodule", TC_EXCLUDESMODULE },
237 { "float", TC_FLOAT },
238 { "format", TC_FORMAT },
239 { "fullauthorlist", TC_FULLAUTHORLIST },
240 { "htmlpreamble", TC_HTMLPREAMBLE },
241 { "htmlstyles", TC_HTMLSTYLES },
242 { "htmltocsection", TC_HTMLTOCSECTION },
243 { "ifcounter", TC_IFCOUNTER },
244 { "input", TC_INPUT },
245 { "insetlayout", TC_INSETLAYOUT },
246 { "leftmargin", TC_LEFTMARGIN },
247 { "modifystyle", TC_MODIFYSTYLE },
248 { "nocounter", TC_NOCOUNTER },
249 { "nofloat", TC_NOFLOAT },
250 { "noinsetlayout", TC_NOINSETLAYOUT },
251 { "nostyle", TC_NOSTYLE },
252 { "outputformat", TC_OUTPUTFORMAT },
253 { "outputtype", TC_OUTPUTTYPE },
254 { "packageoptions", TC_PKGOPTS },
255 { "pagestyle", TC_PAGESTYLE },
256 { "preamble", TC_PREAMBLE },
257 { "provides", TC_PROVIDES },
258 { "providesmodule", TC_PROVIDESMODULE },
259 { "providestyle", TC_PROVIDESTYLE },
260 { "requires", TC_REQUIRES },
261 { "rightmargin", TC_RIGHTMARGIN },
262 { "secnumdepth", TC_SECNUMDEPTH },
263 { "sides", TC_SIDES },
264 { "style", TC_STYLE },
265 { "titlelatexname", TC_TITLELATEXNAME },
266 { "titlelatextype", TC_TITLELATEXTYPE },
267 { "tocdepth", TC_TOCDEPTH }
273 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
275 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
276 TempFile tmp("convertXXXXXX.layout");
277 FileName const tempfile = tmp.name();
278 bool success = layout2layout(filename, tempfile);
280 success = readWithoutConv(tempfile, rt) == OK;
285 std::string TextClass::convert(std::string const & str)
287 TempFile tmp1("localXXXXXX.layout");
288 FileName const fn = tmp1.name();
289 ofstream os(fn.toFilesystemEncoding().c_str());
292 TempFile tmp2("convert_localXXXXXX.layout");
293 FileName const tempfile = tmp2.name();
294 bool success = layout2layout(fn, tempfile);
297 ifstream is(tempfile.toFilesystemEncoding().c_str());
309 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
311 if (!filename.isReadableFile()) {
312 lyxerr << "Cannot read layout file `" << filename << "'."
317 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
318 to_utf8(makeDisplayPath(filename.absFileName())));
320 // Define the plain layout used in table cells, ert, etc. Note that
321 // we do this before loading any layout file, so that classes can
322 // override features of this layout if they should choose to do so.
323 if (rt == BASECLASS && !hasLayout(plain_layout_))
324 layoutlist_.push_back(createBasicLayout(plain_layout_));
326 Lexer lexrc(textClassTags);
327 lexrc.setFile(filename);
328 ReturnValues retval = read(lexrc, rt);
330 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
331 to_utf8(makeDisplayPath(filename.absFileName())));
337 bool TextClass::read(FileName const & filename, ReadType rt)
339 ReturnValues const retval = readWithoutConv(filename, rt);
340 if (retval != FORMAT_MISMATCH)
343 bool const worx = convertLayoutFormat(filename, rt);
345 LYXERR0 ("Unable to convert " << filename <<
346 " to format " << LAYOUT_FORMAT);
351 TextClass::ReturnValues TextClass::validate(std::string const & str)
354 return tc.read(str, VALIDATION);
358 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
360 Lexer lexrc(textClassTags);
361 istringstream is(str);
363 ReturnValues retval = read(lexrc, rt);
365 if (retval != FORMAT_MISMATCH)
368 // write the layout string to a temporary file
369 TempFile tmp("TextClass_read");
370 FileName const tempfile = tmp.name();
371 ofstream os(tempfile.toFilesystemEncoding().c_str());
373 LYXERR0("Unable to create temporary file");
379 // now try to convert it
380 bool const worx = convertLayoutFormat(tempfile, rt);
382 LYXERR0("Unable to convert internal layout information to format "
390 // Reads a textclass structure from file.
391 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
396 // Format of files before the 'Format' tag was introduced
401 while (lexrc.isOK() && !error) {
402 int le = lexrc.lex();
405 case Lexer::LEX_FEOF:
408 case Lexer::LEX_UNDEF:
409 lexrc.printError("Unknown TextClass tag `$$Token'");
417 // used below to track whether we are in an IfStyle or IfCounter tag.
418 bool modifystyle = false;
419 bool providestyle = false;
420 bool ifcounter = false;
422 switch (static_cast<TextClassTags>(le)) {
426 format = lexrc.getInteger();
429 case TC_OUTPUTFORMAT:
431 outputFormat_ = lexrc.getString();
435 readOutputType(lexrc);
436 switch(outputType_) {
438 outputFormat_ = "latex";
441 outputFormat_ = "docbook";
444 outputFormat_ = "literate";
449 case TC_INPUT: // Include file
451 string const inc = lexrc.getString();
452 FileName tmp = libFileSearch("layouts", inc,
456 lexrc.printError("Could not find input file: " + inc);
458 } else if (!read(tmp, MERGE)) {
459 lexrc.printError("Error reading input file: " + tmp.absFileName());
465 case TC_DEFAULTSTYLE:
467 docstring const name = from_utf8(subst(lexrc.getString(),
469 defaultlayout_ = name;
476 case TC_PROVIDESTYLE:
477 // if modifystyle is true, then we got here by falling through
478 // so we are not in an ProvideStyle block
484 lexrc.printError("No name given for style: `$$Token'.");
488 docstring const name = from_utf8(subst(lexrc.getString(),
491 string s = "Could not read name for style: `$$Token' "
492 + lexrc.getString() + " is probably not valid UTF-8!";
495 // Since we couldn't read the name, we just scan the rest
496 // of the style and discard it.
497 error = !readStyle(lexrc, lay);
501 bool const have_layout = hasLayout(name);
503 // If the layout already exists, then we want to add it to
504 // the existing layout, as long as we are not in an ProvideStyle
506 if (have_layout && !providestyle) {
507 Layout & lay = operator[](name);
508 error = !readStyle(lexrc, lay);
510 // If the layout does not exist, then we want to create a new
511 // one, but not if we are in a ModifyStyle block.
512 else if (!have_layout && !modifystyle) {
514 layout.setName(name);
515 error = !readStyle(lexrc, layout);
517 layoutlist_.push_back(layout);
519 if (defaultlayout_.empty()) {
520 // We do not have a default layout yet, so we choose
521 // the first layout we encounter.
522 defaultlayout_ = name;
525 // There are two ways to get here:
526 // (i) The layout exists but we are in an ProvideStyle block
527 // (ii) The layout doesn't exist, but we are in an ModifyStyle
529 // Either way, we just scan the rest and discard it
532 readStyle(lexrc, lay);
539 docstring const style = from_utf8(subst(lexrc.getString(),
541 if (!deleteLayout(style))
542 lyxerr << "Cannot delete style `"
543 << to_utf8(style) << '\'' << endl;
547 case TC_NOINSETLAYOUT:
549 docstring const style = from_utf8(subst(lexrc.getString(),
551 if (!deleteInsetLayout(style))
552 LYXERR0("Style `" << style << "' cannot be removed\n"
553 "because it was not found!");
559 columns_ = lexrc.getInteger();
564 switch (lexrc.getInteger()) {
565 case 1: sides_ = OneSide; break;
566 case 2: sides_ = TwoSides; break;
568 lyxerr << "Impossible number of page"
569 " sides, setting to one."
579 pagestyle_ = rtrim(lexrc.getString());
583 defaultfont_ = lyxRead(lexrc);
584 if (!defaultfont_.resolved()) {
585 lexrc.printError("Warning: defaultfont should "
586 "be fully instantiated!");
587 defaultfont_.realize(sane_font);
593 secnumdepth_ = lexrc.getInteger();
598 tocdepth_ = lexrc.getInteger();
601 // First step to support options
602 case TC_CLASSOPTIONS:
603 readClassOptions(lexrc);
607 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
610 case TC_HTMLPREAMBLE:
611 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
615 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
618 case TC_HTMLTOCSECTION:
619 html_toc_section_ = from_utf8(trim(lexrc.getString()));
622 case TC_ADDTOPREAMBLE:
623 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
626 case TC_ADDTOHTMLPREAMBLE:
627 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
630 case TC_ADDTOHTMLSTYLES:
631 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
636 string const feature = lexrc.getString();
638 if (lexrc.getInteger())
639 provides_.insert(feature);
641 provides_.erase(feature);
647 vector<string> const req
648 = getVectorFromString(lexrc.getString());
649 requires_.insert(req.begin(), req.end());
655 string const pkg = lexrc.getString();
657 string const options = lexrc.getString();
658 package_options_[pkg] = options;
662 case TC_DEFAULTMODULE: {
664 string const module = lexrc.getString();
665 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
666 default_modules_.push_back(module);
670 case TC_PROVIDESMODULE: {
672 string const module = lexrc.getString();
673 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
674 provided_modules_.push_back(module);
678 case TC_EXCLUDESMODULE: {
680 string const module = lexrc.getString();
681 // modules already have their own way to exclude other modules
683 LYXERR0("ExcludesModule tag cannot be used in a module!");
686 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
687 excluded_modules_.push_back(module);
691 case TC_LEFTMARGIN: // left margin type
693 leftmargin_ = lexrc.getDocString();
696 case TC_RIGHTMARGIN: // right margin type
698 rightmargin_ = lexrc.getDocString();
701 case TC_INSETLAYOUT: {
703 lexrc.printError("No name given for InsetLayout: `$$Token'.");
707 docstring const name = subst(lexrc.getDocString(), '_', ' ');
709 string s = "Could not read name for InsetLayout: `$$Token' "
710 + lexrc.getString() + " is probably not valid UTF-8!";
713 // Since we couldn't read the name, we just scan the rest
714 // of the style and discard it.
715 il.read(lexrc, *this);
716 // Let's try to continue rather than abort.
718 } else if (hasInsetLayout(name)) {
719 InsetLayout & il = insetlayoutlist_[name];
720 error = !il.read(lexrc, *this);
724 error = !il.read(lexrc, *this);
726 insetlayoutlist_[name] = il;
732 error = !readFloat(lexrc);
736 error = !readCiteEngine(lexrc);
739 case TC_CITEENGINETYPE:
741 opt_enginetype_ = rtrim(lexrc.getString());
745 error = !readCiteFormat(lexrc);
748 case TC_DEFAULTBIBLIO:
750 cite_default_biblio_style_ = rtrim(lexrc.getString());
753 case TC_FULLAUTHORLIST:
755 cite_full_author_list_ &= lexrc.getBool();
760 docstring const cnt = lexrc.getDocString();
761 if (!counters_.remove(cnt))
762 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
771 docstring const name = lexrc.getDocString();
773 string s = "Could not read name for counter: `$$Token' "
774 + lexrc.getString() + " is probably not valid UTF-8!";
775 lexrc.printError(s.c_str());
777 // Since we couldn't read the name, we just scan the rest
781 error = !counters_.read(lexrc, name, !ifcounter);
784 lexrc.printError("No name given for style: `$$Token'.");
789 case TC_TITLELATEXTYPE:
790 readTitleType(lexrc);
793 case TC_TITLELATEXNAME:
795 titlename_ = lexrc.getString();
800 string const nofloat = lexrc.getString();
801 floatlist_.erase(nofloat);
806 // Note that this is triggered the first time through the loop unless
807 // we hit a format tag.
808 if (format != LAYOUT_FORMAT)
809 return FORMAT_MISMATCH;
812 // at present, we abort if we encounter an error,
813 // so there is no point continuing.
820 if (defaultlayout_.empty()) {
821 LYXERR0("Error: Textclass '" << name_
822 << "' is missing a defaultstyle.");
826 // Try to erase "stdinsets" from the provides_ set.
828 // Provides stdinsets 1
829 // declaration simply tells us that the standard insets have been
830 // defined. (It's found in stdinsets.inc but could also be used in
831 // user-defined files.) There isn't really any such package. So we
832 // might as well go ahead and erase it.
833 // If we do not succeed, then it was not there, which means that
834 // the textclass did not provide the definitions of the standard
835 // insets. So we need to try to load them.
836 int erased = provides_.erase("stdinsets");
838 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
841 frontend::Alert::warning(_("Missing File"),
842 _("Could not find stdinsets.inc! This may lead to data loss!"));
844 } else if (!read(tmp, MERGE)) {
845 frontend::Alert::warning(_("Corrupt File"),
846 _("Could not read stdinsets.inc! This may lead to data loss!"));
851 min_toclevel_ = Layout::NOT_IN_TOC;
852 max_toclevel_ = Layout::NOT_IN_TOC;
853 const_iterator lit = begin();
854 const_iterator len = end();
855 for (; lit != len; ++lit) {
856 int const toclevel = lit->toclevel;
857 if (toclevel != Layout::NOT_IN_TOC) {
858 if (min_toclevel_ == Layout::NOT_IN_TOC)
859 min_toclevel_ = toclevel;
861 min_toclevel_ = min(min_toclevel_, toclevel);
862 max_toclevel_ = max(max_toclevel_, toclevel);
865 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
866 << ", maximum is " << max_toclevel_);
868 return (error ? ERROR : OK);
872 void TextClass::readTitleType(Lexer & lexrc)
874 LexerKeyword titleTypeTags[] = {
875 { "commandafter", TITLE_COMMAND_AFTER },
876 { "environment", TITLE_ENVIRONMENT }
879 PushPopHelper pph(lexrc, titleTypeTags);
881 int le = lexrc.lex();
883 case Lexer::LEX_UNDEF:
884 lexrc.printError("Unknown output type `$$Token'");
886 case TITLE_COMMAND_AFTER:
887 case TITLE_ENVIRONMENT:
888 titletype_ = static_cast<TitleLatexType>(le);
891 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
897 void TextClass::readOutputType(Lexer & lexrc)
899 LexerKeyword outputTypeTags[] = {
900 { "docbook", DOCBOOK },
902 { "literate", LITERATE }
905 PushPopHelper pph(lexrc, outputTypeTags);
907 int le = lexrc.lex();
909 case Lexer::LEX_UNDEF:
910 lexrc.printError("Unknown output type `$$Token'");
915 outputType_ = static_cast<OutputType>(le);
918 LYXERR0("Unhandled value " << le);
924 void TextClass::readClassOptions(Lexer & lexrc)
934 LexerKeyword classOptionsTags[] = {
936 {"fontsize", CO_FONTSIZE },
937 {"header", CO_HEADER },
938 {"other", CO_OTHER },
939 {"pagestyle", CO_PAGESTYLE }
942 lexrc.pushTable(classOptionsTags);
944 while (!getout && lexrc.isOK()) {
945 int le = lexrc.lex();
947 case Lexer::LEX_UNDEF:
948 lexrc.printError("Unknown ClassOption tag `$$Token'");
956 opt_fontsize_ = rtrim(lexrc.getString());
960 opt_pagestyle_ = rtrim(lexrc.getString());
964 if (options_.empty())
965 options_ = lexrc.getString();
967 options_ += ',' + lexrc.getString();
971 class_header_ = subst(lexrc.getString(), """, "\"");
982 bool TextClass::readCiteEngine(Lexer & lexrc)
984 int const type = readCiteEngineType(lexrc);
985 if (type & ENGINE_TYPE_AUTHORYEAR)
986 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
987 if (type & ENGINE_TYPE_NUMERICAL)
988 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
989 if (type & ENGINE_TYPE_DEFAULT)
990 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
993 while (!getout && lexrc.isOK()) {
995 def = lexrc.getString();
996 def = subst(def, " ", "");
997 def = subst(def, "\t", "");
998 if (compare_ascii_no_case(def, "end") == 0) {
1004 char ichar = def[0];
1008 cs.forceUpperCase = true;
1012 size_t const n = def.size();
1013 for (size_t i = 0; i != n; ++i) {
1016 cs.fullAuthorList = true;
1017 else if (ichar == '[' && cs.textAfter)
1018 cs.textBefore = true;
1019 else if (ichar == '[')
1020 cs.textAfter = true;
1021 else if (ichar != ']')
1026 if (type & ENGINE_TYPE_AUTHORYEAR)
1027 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1028 if (type & ENGINE_TYPE_NUMERICAL)
1029 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1030 if (type & ENGINE_TYPE_DEFAULT)
1031 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1037 int TextClass::readCiteEngineType(Lexer & lexrc) const
1039 LATTEST(ENGINE_TYPE_DEFAULT ==
1040 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1041 if (!lexrc.next()) {
1042 lexrc.printError("No cite engine type given for token: `$$Token'.");
1043 return ENGINE_TYPE_DEFAULT;
1045 string const type = rtrim(lexrc.getString());
1046 if (compare_ascii_no_case(type, "authoryear") == 0)
1047 return ENGINE_TYPE_AUTHORYEAR;
1048 else if (compare_ascii_no_case(type, "numerical") == 0)
1049 return ENGINE_TYPE_NUMERICAL;
1050 else if (compare_ascii_no_case(type, "default") != 0) {
1051 string const s = "Unknown cite engine type `" + type
1052 + "' given for token: `$$Token',";
1053 lexrc.printError(s);
1055 return ENGINE_TYPE_DEFAULT;
1059 bool TextClass::readCiteFormat(Lexer & lexrc)
1061 int const type = readCiteEngineType(lexrc);
1064 while (lexrc.isOK()) {
1066 etype = lexrc.getString();
1067 if (compare_ascii_no_case(etype, "end") == 0)
1072 definition = lexrc.getString();
1073 char initchar = etype[0];
1074 if (initchar == '#')
1076 if (initchar == '!' || initchar == '_') {
1077 if (type & ENGINE_TYPE_AUTHORYEAR)
1078 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1079 if (type & ENGINE_TYPE_NUMERICAL)
1080 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1081 if (type & ENGINE_TYPE_DEFAULT)
1082 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1084 if (type & ENGINE_TYPE_AUTHORYEAR)
1085 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1086 if (type & ENGINE_TYPE_NUMERICAL)
1087 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1088 if (type & ENGINE_TYPE_DEFAULT)
1089 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1096 bool TextClass::readFloat(Lexer & lexrc)
1113 FT_ALLOWED_PLACEMENT,
1119 LexerKeyword floatTags[] = {
1120 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1121 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1122 { "allowswide", FT_ALLOWS_WIDE },
1124 { "extension", FT_EXT },
1125 { "guiname", FT_NAME },
1126 { "htmlattr", FT_HTMLATTR },
1127 { "htmlstyle", FT_HTMLSTYLE },
1128 { "htmltag", FT_HTMLTAG },
1129 { "ispredefined", FT_PREDEFINED },
1130 { "listcommand", FT_LISTCOMMAND },
1131 { "listname", FT_LISTNAME },
1132 { "numberwithin", FT_WITHIN },
1133 { "placement", FT_PLACEMENT },
1134 { "refprefix", FT_REFPREFIX },
1135 { "style", FT_STYLE },
1136 { "type", FT_TYPE },
1137 { "usesfloatpkg", FT_USESFLOAT }
1140 lexrc.pushTable(floatTags);
1150 string allowed_placement = "!htbpH";
1155 bool usesfloat = true;
1156 bool ispredefined = false;
1157 bool allowswide = true;
1158 bool allowssideways = true;
1160 bool getout = false;
1161 while (!getout && lexrc.isOK()) {
1162 int le = lexrc.lex();
1164 case Lexer::LEX_UNDEF:
1165 lexrc.printError("Unknown float tag `$$Token'");
1173 type = lexrc.getString();
1174 if (floatlist_.typeExist(type)) {
1175 Floating const & fl = floatlist_.getType(type);
1176 placement = fl.placement();
1178 within = fl.within();
1181 listname = fl.listName();
1182 usesfloat = fl.usesFloatPkg();
1183 ispredefined = fl.isPredefined();
1184 listcommand = fl.listCommand();
1185 refprefix = fl.refPrefix();
1190 name = lexrc.getString();
1194 placement = lexrc.getString();
1196 case FT_ALLOWED_PLACEMENT:
1198 allowed_placement = lexrc.getString();
1202 ext = lexrc.getString();
1206 within = lexrc.getString();
1207 if (within == "none")
1212 style = lexrc.getString();
1214 case FT_LISTCOMMAND:
1216 listcommand = lexrc.getString();
1220 refprefix = lexrc.getString();
1224 listname = lexrc.getString();
1228 usesfloat = lexrc.getBool();
1232 ispredefined = lexrc.getBool();
1234 case FT_ALLOWS_SIDEWAYS:
1236 allowssideways = lexrc.getBool();
1238 case FT_ALLOWS_WIDE:
1240 allowswide = lexrc.getBool();
1244 htmlattr = lexrc.getString();
1248 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1252 htmltag = lexrc.getString();
1262 // Here we have a full float if getout == true
1264 if (!usesfloat && listcommand.empty()) {
1265 // if this float uses the same auxfile as an existing one,
1266 // there is no need for it to provide a list command.
1267 FloatList::const_iterator it = floatlist_.begin();
1268 FloatList::const_iterator en = floatlist_.end();
1269 bool found_ext = false;
1270 for (; it != en; ++it) {
1271 if (it->second.ext() == ext) {
1277 LYXERR0("The layout does not provide a list command " <<
1278 "for the float `" << type << "'. LyX will " <<
1279 "not be able to produce a float list.");
1281 Floating fl(type, placement, ext, within, style, name,
1282 listname, listcommand, refprefix, allowed_placement,
1283 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1284 allowswide, allowssideways);
1285 floatlist_.newFloat(fl);
1286 // each float has its own counter
1287 counters_.newCounter(from_ascii(type), from_ascii(within),
1288 docstring(), docstring());
1289 // also define sub-float counters
1290 docstring const subtype = "sub-" + from_ascii(type);
1291 counters_.newCounter(subtype, from_ascii(type),
1292 "\\alph{" + subtype + "}", docstring());
1298 string const & TextClass::prerequisites(string const & sep) const
1300 if (contains(prerequisites_, ',')) {
1301 vector<string> const pres = getVectorFromString(prerequisites_);
1302 prerequisites_ = getStringFromVector(pres, sep);
1304 return prerequisites_;
1308 bool TextClass::hasLayout(docstring const & n) const
1310 docstring const name = n.empty() ? defaultLayoutName() : n;
1312 return find_if(layoutlist_.begin(), layoutlist_.end(),
1313 LayoutNamesEqual(name))
1314 != layoutlist_.end();
1318 bool TextClass::hasInsetLayout(docstring const & n) const
1322 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1323 return it != insetlayoutlist_.end();
1327 Layout const & TextClass::operator[](docstring const & name) const
1329 LATTEST(!name.empty());
1332 find_if(begin(), end(), LayoutNamesEqual(name));
1335 LYXERR0("We failed to find the layout '" << name
1336 << "' in the layout list. You MUST investigate!");
1337 for (const_iterator cit = begin(); cit != end(); ++cit)
1338 lyxerr << " " << to_utf8(cit->name()) << endl;
1340 // We require the name to exist
1341 static const Layout dummy;
1342 LASSERT(false, return dummy);
1349 Layout & TextClass::operator[](docstring const & name)
1351 LATTEST(!name.empty());
1352 // Safe to continue, given what we do below.
1354 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1357 LYXERR0("We failed to find the layout '" << to_utf8(name)
1358 << "' in the layout list. You MUST investigate!");
1359 for (const_iterator cit = begin(); cit != end(); ++cit)
1360 LYXERR0(" " << to_utf8(cit->name()));
1362 // we require the name to exist
1364 // we are here only in release mode
1365 layoutlist_.push_back(createBasicLayout(name, true));
1366 it = find_if(begin(), end(), LayoutNamesEqual(name));
1373 bool TextClass::deleteLayout(docstring const & name)
1375 if (name == defaultLayoutName() || name == plainLayoutName())
1378 LayoutList::iterator it =
1379 remove_if(layoutlist_.begin(), layoutlist_.end(),
1380 LayoutNamesEqual(name));
1382 LayoutList::iterator end = layoutlist_.end();
1383 bool const ret = (it != end);
1384 layoutlist_.erase(it, end);
1389 bool TextClass::deleteInsetLayout(docstring const & name)
1391 return insetlayoutlist_.erase(name);
1395 // Load textclass info if not loaded yet
1396 bool TextClass::load(string const & path) const
1401 // Read style-file, provided path is searched before the system ones
1402 // If path is a file, it is loaded directly.
1403 FileName layout_file(path);
1404 if (!path.empty() && !layout_file.isReadableFile())
1405 layout_file = FileName(addName(path, name_ + ".layout"));
1406 if (layout_file.empty() || !layout_file.exists())
1407 layout_file = libFileSearch("layouts", name_, "layout");
1408 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1411 lyxerr << "Error reading `"
1412 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1413 << "'\n(Check `" << name_
1414 << "')\nCheck your installation and "
1415 "try Options/Reconfigure..."
1423 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1428 layoutlist_.push_back(createBasicLayout(n, true));
1433 string DocumentClass::forcedLayouts() const
1437 const_iterator const e = end();
1438 for (const_iterator i = begin(); i != e; ++i) {
1439 if (i->forcelocal > 0) {
1441 os << "Format " << LAYOUT_FORMAT << '\n';
1451 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1453 // FIXME The fix for the InsetLayout part of 4812 would be here:
1454 // Add the InsetLayout to the document class if it is not found.
1456 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1457 while (!n.empty()) {
1458 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1459 if (cit != cen && cit->first == n) {
1460 if (cit->second.obsoleted_by().empty())
1462 n = cit->second.obsoleted_by();
1463 return insetLayout(n);
1465 // If we have a generic prefix (e.g., "Note:"),
1466 // try if this one alone is found.
1467 size_t i = n.find(':');
1468 if (i == string::npos)
1472 // Layout "name" not found.
1473 return plain_insetlayout_;
1477 docstring const & TextClass::defaultLayoutName() const
1479 return defaultlayout_;
1483 Layout const & TextClass::defaultLayout() const
1485 return operator[](defaultLayoutName());
1489 bool TextClass::isDefaultLayout(Layout const & layout) const
1491 return layout.name() == defaultLayoutName();
1495 bool TextClass::isPlainLayout(Layout const & layout) const
1497 return layout.name() == plainLayoutName();
1501 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1503 static Layout * defaultLayout = NULL;
1505 if (defaultLayout) {
1506 defaultLayout->setUnknown(unknown);
1507 defaultLayout->setName(name);
1508 return *defaultLayout;
1511 static char const * s = "Margin Static\n"
1512 "LatexType Paragraph\n"
1515 "AlignPossible Left, Right, Center\n"
1516 "LabelType No_Label\n"
1518 istringstream ss(s);
1519 Lexer lex(textClassTags);
1521 defaultLayout = new Layout;
1522 defaultLayout->setUnknown(unknown);
1523 defaultLayout->setName(name);
1524 if (!readStyle(lex, *defaultLayout)) {
1525 // The only way this happens is because the hardcoded layout above
1529 return *defaultLayout;
1533 DocumentClassPtr getDocumentClass(
1534 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1537 DocumentClassPtr doc_class =
1538 DocumentClassPtr(new DocumentClass(baseClass));
1539 LayoutModuleList::const_iterator it = modlist.begin();
1540 LayoutModuleList::const_iterator en = modlist.end();
1541 for (; it != en; ++it) {
1542 string const modName = *it;
1543 LyXModule * lm = theModuleList[modName];
1545 docstring const msg =
1546 bformat(_("The module %1$s has been requested by\n"
1547 "this document but has not been found in the list of\n"
1548 "available modules. If you recently installed it, you\n"
1549 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1551 frontend::Alert::warning(_("Module not available"), msg);
1554 if (!lm->isAvailable() && !clone) {
1555 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1556 docstring const msg =
1557 bformat(_("The module %1$s requires a package that is not\n"
1558 "available in your LaTeX installation, or a converter that\n"
1559 "you have not installed. LaTeX output may not be possible.\n"
1560 "Missing prerequisites:\n"
1562 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1563 from_utf8(modName), prereqs);
1564 frontend::Alert::warning(_("Package not available"), msg, true);
1566 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1567 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1568 docstring const msg =
1569 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1570 frontend::Alert::warning(_("Read Error"), msg);
1577 /////////////////////////////////////////////////////////////////////////
1581 /////////////////////////////////////////////////////////////////////////
1583 DocumentClass::DocumentClass(LayoutFile const & tc)
1588 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1590 LayoutList::const_iterator it = layoutlist_.begin();
1591 LayoutList::const_iterator end = layoutlist_.end();
1592 for (; it != end; ++it)
1593 if (it->latexname() == lay)
1599 bool DocumentClass::provides(string const & p) const
1601 return provides_.find(p) != provides_.end();
1605 bool DocumentClass::hasTocLevels() const
1607 return min_toclevel_ != Layout::NOT_IN_TOC;
1611 Layout const & DocumentClass::getTOCLayout() const
1613 // we're going to look for the layout with the minimum toclevel
1614 TextClass::LayoutList::const_iterator lit = begin();
1615 TextClass::LayoutList::const_iterator const len = end();
1616 int minlevel = 1000;
1617 Layout const * lay = NULL;
1618 for (; lit != len; ++lit) {
1619 int const level = lit->toclevel;
1620 // we don't want Part or unnumbered sections
1621 if (level == Layout::NOT_IN_TOC || level < 0
1622 || level >= minlevel || lit->counter.empty())
1629 // hmm. that is very odd, so we'll do our best.
1630 return operator[](defaultLayoutName());
1634 Layout const & DocumentClass::htmlTOCLayout() const
1636 if (html_toc_section_.empty())
1637 html_toc_section_ = getTOCLayout().name();
1638 return operator[](html_toc_section_);
1642 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1643 string const & entry, string const & fallback) const
1645 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1647 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1648 if (itype == cite_formats_.end())
1649 return default_format;
1650 map<string, string>::const_iterator it = itype->second.find(entry);
1651 if (it == itype->second.end() && !fallback.empty())
1652 it = itype->second.find(fallback);
1653 if (it == itype->second.end())
1654 return default_format;
1659 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1660 string const & macro) const
1662 static string empty;
1663 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1664 if (itype == cite_macros_.end())
1666 map<string, string>::const_iterator it = itype->second.find(macro);
1667 if (it == itype->second.end())
1673 vector<string> const DocumentClass::citeCommands(
1674 CiteEngineType const & type) const
1676 vector<CitationStyle> const styles = citeStyles(type);
1677 vector<CitationStyle>::const_iterator it = styles.begin();
1678 vector<CitationStyle>::const_iterator end = styles.end();
1679 vector<string> cmds;
1680 for (; it != end; ++it) {
1681 CitationStyle const cite = *it;
1682 cmds.push_back(cite.cmd);
1688 vector<CitationStyle> const & DocumentClass::citeStyles(
1689 CiteEngineType const & type) const
1691 static vector<CitationStyle> empty;
1692 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1693 if (it == cite_styles_.end())
1699 /////////////////////////////////////////////////////////////////////////
1703 /////////////////////////////////////////////////////////////////////////
1705 ostream & operator<<(ostream & os, PageSides p)