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 = 59; //gm: OutlinerName, AddToToc, IsTocCaption
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,
223 LexerKeyword textClassTags[] = {
224 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
225 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
226 { "addtopreamble", TC_ADDTOPREAMBLE },
227 { "citeengine", TC_CITEENGINE },
228 { "citeenginetype", TC_CITEENGINETYPE },
229 { "citeformat", TC_CITEFORMAT },
230 { "classoptions", TC_CLASSOPTIONS },
231 { "columns", TC_COLUMNS },
232 { "counter", TC_COUNTER },
233 { "defaultbiblio", TC_DEFAULTBIBLIO },
234 { "defaultfont", TC_DEFAULTFONT },
235 { "defaultmodule", TC_DEFAULTMODULE },
236 { "defaultstyle", TC_DEFAULTSTYLE },
237 { "excludesmodule", TC_EXCLUDESMODULE },
238 { "float", TC_FLOAT },
239 { "format", TC_FORMAT },
240 { "fullauthorlist", TC_FULLAUTHORLIST },
241 { "htmlpreamble", TC_HTMLPREAMBLE },
242 { "htmlstyles", TC_HTMLSTYLES },
243 { "htmltocsection", TC_HTMLTOCSECTION },
244 { "ifcounter", TC_IFCOUNTER },
245 { "input", TC_INPUT },
246 { "insetlayout", TC_INSETLAYOUT },
247 { "leftmargin", TC_LEFTMARGIN },
248 { "modifystyle", TC_MODIFYSTYLE },
249 { "nocounter", TC_NOCOUNTER },
250 { "nofloat", TC_NOFLOAT },
251 { "noinsetlayout", TC_NOINSETLAYOUT },
252 { "nostyle", TC_NOSTYLE },
253 { "outlinername", TC_OUTLINERNAME },
254 { "outputformat", TC_OUTPUTFORMAT },
255 { "outputtype", TC_OUTPUTTYPE },
256 { "packageoptions", TC_PKGOPTS },
257 { "pagestyle", TC_PAGESTYLE },
258 { "preamble", TC_PREAMBLE },
259 { "provides", TC_PROVIDES },
260 { "providesmodule", TC_PROVIDESMODULE },
261 { "providestyle", TC_PROVIDESTYLE },
262 { "requires", TC_REQUIRES },
263 { "rightmargin", TC_RIGHTMARGIN },
264 { "secnumdepth", TC_SECNUMDEPTH },
265 { "sides", TC_SIDES },
266 { "style", TC_STYLE },
267 { "titlelatexname", TC_TITLELATEXNAME },
268 { "titlelatextype", TC_TITLELATEXTYPE },
269 { "tocdepth", TC_TOCDEPTH }
275 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
277 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
278 TempFile tmp("convertXXXXXX.layout");
279 FileName const tempfile = tmp.name();
280 bool success = layout2layout(filename, tempfile);
282 success = readWithoutConv(tempfile, rt) == OK;
287 std::string TextClass::convert(std::string const & str)
289 TempFile tmp1("localXXXXXX.layout");
290 FileName const fn = tmp1.name();
291 ofstream os(fn.toFilesystemEncoding().c_str());
294 TempFile tmp2("convert_localXXXXXX.layout");
295 FileName const tempfile = tmp2.name();
296 bool success = layout2layout(fn, tempfile);
299 ifstream is(tempfile.toFilesystemEncoding().c_str());
311 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
313 if (!filename.isReadableFile()) {
314 lyxerr << "Cannot read layout file `" << filename << "'."
319 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
320 to_utf8(makeDisplayPath(filename.absFileName())));
322 // Define the plain layout used in table cells, ert, etc. Note that
323 // we do this before loading any layout file, so that classes can
324 // override features of this layout if they should choose to do so.
325 if (rt == BASECLASS && !hasLayout(plain_layout_))
326 layoutlist_.push_back(createBasicLayout(plain_layout_));
328 Lexer lexrc(textClassTags);
329 lexrc.setFile(filename);
330 ReturnValues retval = read(lexrc, rt);
332 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
333 to_utf8(makeDisplayPath(filename.absFileName())));
339 bool TextClass::read(FileName const & filename, ReadType rt)
341 ReturnValues const retval = readWithoutConv(filename, rt);
342 if (retval != FORMAT_MISMATCH)
345 bool const worx = convertLayoutFormat(filename, rt);
347 LYXERR0 ("Unable to convert " << filename <<
348 " to format " << LAYOUT_FORMAT);
353 TextClass::ReturnValues TextClass::validate(std::string const & str)
356 return tc.read(str, VALIDATION);
360 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
362 Lexer lexrc(textClassTags);
363 istringstream is(str);
365 ReturnValues retval = read(lexrc, rt);
367 if (retval != FORMAT_MISMATCH)
370 // write the layout string to a temporary file
371 TempFile tmp("TextClass_read");
372 FileName const tempfile = tmp.name();
373 ofstream os(tempfile.toFilesystemEncoding().c_str());
375 LYXERR0("Unable to create temporary file");
381 // now try to convert it
382 bool const worx = convertLayoutFormat(tempfile, rt);
384 LYXERR0("Unable to convert internal layout information to format "
392 // Reads a textclass structure from file.
393 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
398 // Format of files before the 'Format' tag was introduced
403 while (lexrc.isOK() && !error) {
404 int le = lexrc.lex();
407 case Lexer::LEX_FEOF:
410 case Lexer::LEX_UNDEF:
411 lexrc.printError("Unknown TextClass tag `$$Token'");
419 // used below to track whether we are in an IfStyle or IfCounter tag.
420 bool modifystyle = false;
421 bool providestyle = false;
422 bool ifcounter = false;
424 switch (static_cast<TextClassTags>(le)) {
428 format = lexrc.getInteger();
431 case TC_OUTPUTFORMAT:
433 outputFormat_ = lexrc.getString();
437 readOutputType(lexrc);
438 switch(outputType_) {
440 outputFormat_ = "latex";
443 outputFormat_ = "docbook";
446 outputFormat_ = "literate";
451 case TC_INPUT: // Include file
453 string const inc = lexrc.getString();
454 FileName tmp = libFileSearch("layouts", inc,
458 lexrc.printError("Could not find input file: " + inc);
460 } else if (!read(tmp, MERGE)) {
461 lexrc.printError("Error reading input file: " + tmp.absFileName());
467 case TC_DEFAULTSTYLE:
469 docstring const name = from_utf8(subst(lexrc.getString(),
471 defaultlayout_ = name;
478 case TC_PROVIDESTYLE:
479 // if modifystyle is true, then we got here by falling through
480 // so we are not in an ProvideStyle block
486 lexrc.printError("No name given for style: `$$Token'.");
490 docstring const name = from_utf8(subst(lexrc.getString(),
493 string s = "Could not read name for style: `$$Token' "
494 + lexrc.getString() + " is probably not valid UTF-8!";
497 // Since we couldn't read the name, we just scan the rest
498 // of the style and discard it.
499 error = !readStyle(lexrc, lay);
503 bool const have_layout = hasLayout(name);
505 // If the layout already exists, then we want to add it to
506 // the existing layout, as long as we are not in an ProvideStyle
508 if (have_layout && !providestyle) {
509 Layout & lay = operator[](name);
510 error = !readStyle(lexrc, lay);
512 // If the layout does not exist, then we want to create a new
513 // one, but not if we are in a ModifyStyle block.
514 else if (!have_layout && !modifystyle) {
516 layout.setName(name);
517 error = !readStyle(lexrc, layout);
519 layoutlist_.push_back(layout);
521 if (defaultlayout_.empty()) {
522 // We do not have a default layout yet, so we choose
523 // the first layout we encounter.
524 defaultlayout_ = name;
527 // There are two ways to get here:
528 // (i) The layout exists but we are in an ProvideStyle block
529 // (ii) The layout doesn't exist, but we are in an ModifyStyle
531 // Either way, we just scan the rest and discard it
534 readStyle(lexrc, lay);
541 docstring const style = from_utf8(subst(lexrc.getString(),
543 if (!deleteLayout(style))
544 lyxerr << "Cannot delete style `"
545 << to_utf8(style) << '\'' << endl;
549 case TC_NOINSETLAYOUT:
551 docstring const style = from_utf8(subst(lexrc.getString(),
553 if (!deleteInsetLayout(style))
554 LYXERR0("Style `" << style << "' cannot be removed\n"
555 "because it was not found!");
561 columns_ = lexrc.getInteger();
566 switch (lexrc.getInteger()) {
567 case 1: sides_ = OneSide; break;
568 case 2: sides_ = TwoSides; break;
570 lyxerr << "Impossible number of page"
571 " sides, setting to one."
581 pagestyle_ = rtrim(lexrc.getString());
585 defaultfont_ = lyxRead(lexrc);
586 if (!defaultfont_.resolved()) {
587 lexrc.printError("Warning: defaultfont should "
588 "be fully instantiated!");
589 defaultfont_.realize(sane_font);
595 secnumdepth_ = lexrc.getInteger();
600 tocdepth_ = lexrc.getInteger();
603 // First step to support options
604 case TC_CLASSOPTIONS:
605 readClassOptions(lexrc);
609 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
612 case TC_HTMLPREAMBLE:
613 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
617 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
620 case TC_HTMLTOCSECTION:
621 html_toc_section_ = from_utf8(trim(lexrc.getString()));
624 case TC_ADDTOPREAMBLE:
625 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
628 case TC_ADDTOHTMLPREAMBLE:
629 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
632 case TC_ADDTOHTMLSTYLES:
633 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
638 string const feature = lexrc.getString();
640 if (lexrc.getInteger())
641 provides_.insert(feature);
643 provides_.erase(feature);
649 vector<string> const req
650 = getVectorFromString(lexrc.getString());
651 requires_.insert(req.begin(), req.end());
657 string const pkg = lexrc.getString();
659 string const options = lexrc.getString();
660 package_options_[pkg] = options;
664 case TC_DEFAULTMODULE: {
666 string const module = lexrc.getString();
667 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
668 default_modules_.push_back(module);
672 case TC_PROVIDESMODULE: {
674 string const module = lexrc.getString();
675 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
676 provided_modules_.push_back(module);
680 case TC_EXCLUDESMODULE: {
682 string const module = lexrc.getString();
683 // modules already have their own way to exclude other modules
685 LYXERR0("ExcludesModule tag cannot be used in a module!");
688 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
689 excluded_modules_.push_back(module);
693 case TC_LEFTMARGIN: // left margin type
695 leftmargin_ = lexrc.getDocString();
698 case TC_RIGHTMARGIN: // right margin type
700 rightmargin_ = lexrc.getDocString();
703 case TC_INSETLAYOUT: {
705 lexrc.printError("No name given for InsetLayout: `$$Token'.");
709 docstring const name = subst(lexrc.getDocString(), '_', ' ');
711 string s = "Could not read name for InsetLayout: `$$Token' "
712 + lexrc.getString() + " is probably not valid UTF-8!";
715 // Since we couldn't read the name, we just scan the rest
716 // of the style and discard it.
717 il.read(lexrc, *this);
718 // Let's try to continue rather than abort.
720 } else if (hasInsetLayout(name)) {
721 InsetLayout & il = insetlayoutlist_[name];
722 error = !il.read(lexrc, *this);
726 error = !il.read(lexrc, *this);
728 insetlayoutlist_[name] = il;
734 error = !readFloat(lexrc);
738 error = !readCiteEngine(lexrc);
741 case TC_CITEENGINETYPE:
743 opt_enginetype_ = rtrim(lexrc.getString());
747 error = !readCiteFormat(lexrc);
750 case TC_DEFAULTBIBLIO:
752 cite_default_biblio_style_ = rtrim(lexrc.getString());
755 case TC_FULLAUTHORLIST:
757 cite_full_author_list_ &= lexrc.getBool();
762 docstring const cnt = lexrc.getDocString();
763 if (!counters_.remove(cnt))
764 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
773 docstring const name = lexrc.getDocString();
775 string s = "Could not read name for counter: `$$Token' "
776 + lexrc.getString() + " is probably not valid UTF-8!";
777 lexrc.printError(s.c_str());
779 // Since we couldn't read the name, we just scan the rest
783 error = !counters_.read(lexrc, name, !ifcounter);
786 lexrc.printError("No name given for style: `$$Token'.");
791 case TC_TITLELATEXTYPE:
792 readTitleType(lexrc);
795 case TC_TITLELATEXNAME:
797 titlename_ = lexrc.getString();
802 string const nofloat = lexrc.getString();
803 floatlist_.erase(nofloat);
807 case TC_OUTLINERNAME:
808 error = !readOutlinerName(lexrc);
812 // Note that this is triggered the first time through the loop unless
813 // we hit a format tag.
814 if (format != LAYOUT_FORMAT)
815 return FORMAT_MISMATCH;
818 // at present, we abort if we encounter an error,
819 // so there is no point continuing.
826 if (defaultlayout_.empty()) {
827 LYXERR0("Error: Textclass '" << name_
828 << "' is missing a defaultstyle.");
832 // Try to erase "stdinsets" from the provides_ set.
834 // Provides stdinsets 1
835 // declaration simply tells us that the standard insets have been
836 // defined. (It's found in stdinsets.inc but could also be used in
837 // user-defined files.) There isn't really any such package. So we
838 // might as well go ahead and erase it.
839 // If we do not succeed, then it was not there, which means that
840 // the textclass did not provide the definitions of the standard
841 // insets. So we need to try to load them.
842 int erased = provides_.erase("stdinsets");
844 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
847 frontend::Alert::warning(_("Missing File"),
848 _("Could not find stdinsets.inc! This may lead to data loss!"));
850 } else if (!read(tmp, MERGE)) {
851 frontend::Alert::warning(_("Corrupt File"),
852 _("Could not read stdinsets.inc! This may lead to data loss!"));
857 min_toclevel_ = Layout::NOT_IN_TOC;
858 max_toclevel_ = Layout::NOT_IN_TOC;
859 const_iterator lit = begin();
860 const_iterator len = end();
861 for (; lit != len; ++lit) {
862 int const toclevel = lit->toclevel;
863 if (toclevel != Layout::NOT_IN_TOC) {
864 if (min_toclevel_ == Layout::NOT_IN_TOC)
865 min_toclevel_ = toclevel;
867 min_toclevel_ = min(min_toclevel_, toclevel);
868 max_toclevel_ = max(max_toclevel_, toclevel);
871 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
872 << ", maximum is " << max_toclevel_);
874 return (error ? ERROR : OK);
878 void TextClass::readTitleType(Lexer & lexrc)
880 LexerKeyword titleTypeTags[] = {
881 { "commandafter", TITLE_COMMAND_AFTER },
882 { "environment", TITLE_ENVIRONMENT }
885 PushPopHelper pph(lexrc, titleTypeTags);
887 int le = lexrc.lex();
889 case Lexer::LEX_UNDEF:
890 lexrc.printError("Unknown output type `$$Token'");
892 case TITLE_COMMAND_AFTER:
893 case TITLE_ENVIRONMENT:
894 titletype_ = static_cast<TitleLatexType>(le);
897 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
903 void TextClass::readOutputType(Lexer & lexrc)
905 LexerKeyword outputTypeTags[] = {
906 { "docbook", DOCBOOK },
908 { "literate", LITERATE }
911 PushPopHelper pph(lexrc, outputTypeTags);
913 int le = lexrc.lex();
915 case Lexer::LEX_UNDEF:
916 lexrc.printError("Unknown output type `$$Token'");
921 outputType_ = static_cast<OutputType>(le);
924 LYXERR0("Unhandled value " << le);
930 void TextClass::readClassOptions(Lexer & lexrc)
940 LexerKeyword classOptionsTags[] = {
942 {"fontsize", CO_FONTSIZE },
943 {"header", CO_HEADER },
944 {"other", CO_OTHER },
945 {"pagestyle", CO_PAGESTYLE }
948 lexrc.pushTable(classOptionsTags);
950 while (!getout && lexrc.isOK()) {
951 int le = lexrc.lex();
953 case Lexer::LEX_UNDEF:
954 lexrc.printError("Unknown ClassOption tag `$$Token'");
962 opt_fontsize_ = rtrim(lexrc.getString());
966 opt_pagestyle_ = rtrim(lexrc.getString());
970 if (options_.empty())
971 options_ = lexrc.getString();
973 options_ += ',' + lexrc.getString();
977 class_header_ = subst(lexrc.getString(), """, "\"");
988 bool TextClass::readCiteEngine(Lexer & lexrc)
990 int const type = readCiteEngineType(lexrc);
991 if (type & ENGINE_TYPE_AUTHORYEAR)
992 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
993 if (type & ENGINE_TYPE_NUMERICAL)
994 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
995 if (type & ENGINE_TYPE_DEFAULT)
996 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
999 while (!getout && lexrc.isOK()) {
1001 def = lexrc.getString();
1002 def = subst(def, " ", "");
1003 def = subst(def, "\t", "");
1004 if (compare_ascii_no_case(def, "end") == 0) {
1010 char ichar = def[0];
1014 cs.forceUpperCase = true;
1018 size_t const n = def.size();
1019 for (size_t i = 0; i != n; ++i) {
1022 cs.fullAuthorList = true;
1023 else if (ichar == '[' && cs.textAfter)
1024 cs.textBefore = true;
1025 else if (ichar == '[')
1026 cs.textAfter = true;
1027 else if (ichar != ']')
1032 if (type & ENGINE_TYPE_AUTHORYEAR)
1033 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1034 if (type & ENGINE_TYPE_NUMERICAL)
1035 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1036 if (type & ENGINE_TYPE_DEFAULT)
1037 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1043 int TextClass::readCiteEngineType(Lexer & lexrc) const
1045 LATTEST(ENGINE_TYPE_DEFAULT ==
1046 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1047 if (!lexrc.next()) {
1048 lexrc.printError("No cite engine type given for token: `$$Token'.");
1049 return ENGINE_TYPE_DEFAULT;
1051 string const type = rtrim(lexrc.getString());
1052 if (compare_ascii_no_case(type, "authoryear") == 0)
1053 return ENGINE_TYPE_AUTHORYEAR;
1054 else if (compare_ascii_no_case(type, "numerical") == 0)
1055 return ENGINE_TYPE_NUMERICAL;
1056 else if (compare_ascii_no_case(type, "default") != 0) {
1057 string const s = "Unknown cite engine type `" + type
1058 + "' given for token: `$$Token',";
1059 lexrc.printError(s);
1061 return ENGINE_TYPE_DEFAULT;
1065 bool TextClass::readCiteFormat(Lexer & lexrc)
1067 int const type = readCiteEngineType(lexrc);
1070 while (lexrc.isOK()) {
1072 etype = lexrc.getString();
1073 if (compare_ascii_no_case(etype, "end") == 0)
1078 definition = lexrc.getString();
1079 char initchar = etype[0];
1080 if (initchar == '#')
1082 if (initchar == '!' || initchar == '_') {
1083 if (type & ENGINE_TYPE_AUTHORYEAR)
1084 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1085 if (type & ENGINE_TYPE_NUMERICAL)
1086 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1087 if (type & ENGINE_TYPE_DEFAULT)
1088 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1090 if (type & ENGINE_TYPE_AUTHORYEAR)
1091 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1092 if (type & ENGINE_TYPE_NUMERICAL)
1093 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1094 if (type & ENGINE_TYPE_DEFAULT)
1095 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1102 bool TextClass::readFloat(Lexer & lexrc)
1119 FT_ALLOWED_PLACEMENT,
1125 LexerKeyword floatTags[] = {
1126 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1127 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1128 { "allowswide", FT_ALLOWS_WIDE },
1130 { "extension", FT_EXT },
1131 { "guiname", FT_NAME },
1132 { "htmlattr", FT_HTMLATTR },
1133 { "htmlstyle", FT_HTMLSTYLE },
1134 { "htmltag", FT_HTMLTAG },
1135 { "ispredefined", FT_PREDEFINED },
1136 { "listcommand", FT_LISTCOMMAND },
1137 { "listname", FT_LISTNAME },
1138 { "numberwithin", FT_WITHIN },
1139 { "placement", FT_PLACEMENT },
1140 { "refprefix", FT_REFPREFIX },
1141 { "style", FT_STYLE },
1142 { "type", FT_TYPE },
1143 { "usesfloatpkg", FT_USESFLOAT }
1146 lexrc.pushTable(floatTags);
1156 string allowed_placement = "!htbpH";
1161 bool usesfloat = true;
1162 bool ispredefined = false;
1163 bool allowswide = true;
1164 bool allowssideways = true;
1166 bool getout = false;
1167 while (!getout && lexrc.isOK()) {
1168 int le = lexrc.lex();
1170 case Lexer::LEX_UNDEF:
1171 lexrc.printError("Unknown float tag `$$Token'");
1179 type = lexrc.getString();
1180 if (floatlist_.typeExist(type)) {
1181 Floating const & fl = floatlist_.getType(type);
1182 placement = fl.placement();
1184 within = fl.within();
1187 listname = fl.listName();
1188 usesfloat = fl.usesFloatPkg();
1189 ispredefined = fl.isPredefined();
1190 listcommand = fl.listCommand();
1191 refprefix = fl.refPrefix();
1196 name = lexrc.getString();
1200 placement = lexrc.getString();
1202 case FT_ALLOWED_PLACEMENT:
1204 allowed_placement = lexrc.getString();
1208 ext = lexrc.getString();
1212 within = lexrc.getString();
1213 if (within == "none")
1218 style = lexrc.getString();
1220 case FT_LISTCOMMAND:
1222 listcommand = lexrc.getString();
1226 refprefix = lexrc.getString();
1230 listname = lexrc.getString();
1234 usesfloat = lexrc.getBool();
1238 ispredefined = lexrc.getBool();
1240 case FT_ALLOWS_SIDEWAYS:
1242 allowssideways = lexrc.getBool();
1244 case FT_ALLOWS_WIDE:
1246 allowswide = lexrc.getBool();
1250 htmlattr = lexrc.getString();
1254 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1258 htmltag = lexrc.getString();
1268 // Here we have a full float if getout == true
1270 if (!usesfloat && listcommand.empty()) {
1271 // if this float uses the same auxfile as an existing one,
1272 // there is no need for it to provide a list command.
1273 FloatList::const_iterator it = floatlist_.begin();
1274 FloatList::const_iterator en = floatlist_.end();
1275 bool found_ext = false;
1276 for (; it != en; ++it) {
1277 if (it->second.ext() == ext) {
1283 LYXERR0("The layout does not provide a list command " <<
1284 "for the float `" << type << "'. LyX will " <<
1285 "not be able to produce a float list.");
1287 Floating fl(type, placement, ext, within, style, name,
1288 listname, listcommand, refprefix, allowed_placement,
1289 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1290 allowswide, allowssideways);
1291 floatlist_.newFloat(fl);
1292 // each float has its own counter
1293 counters_.newCounter(from_ascii(type), from_ascii(within),
1294 docstring(), docstring());
1295 // also define sub-float counters
1296 docstring const subtype = "sub-" + from_ascii(type);
1297 counters_.newCounter(subtype, from_ascii(type),
1298 "\\alph{" + subtype + "}", docstring());
1304 bool TextClass::readOutlinerName(Lexer & lexrc)
1309 type = lexrc.getString();
1311 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1315 name = lexrc.getDocString();
1317 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1320 outliner_names_[type] = name;
1325 docstring TextClass::outlinerName(std::string const & type) const
1327 std::map<std::string,docstring>::const_iterator const it
1328 = outliner_names_.find(type);
1329 if (it == outliner_names_.end()) {
1330 LYXERR0("Missing OutlinerName for " << type << "!");
1331 return from_utf8(type);
1337 string const & TextClass::prerequisites(string const & sep) const
1339 if (contains(prerequisites_, ',')) {
1340 vector<string> const pres = getVectorFromString(prerequisites_);
1341 prerequisites_ = getStringFromVector(pres, sep);
1343 return prerequisites_;
1347 bool TextClass::hasLayout(docstring const & n) const
1349 docstring const name = n.empty() ? defaultLayoutName() : n;
1351 return find_if(layoutlist_.begin(), layoutlist_.end(),
1352 LayoutNamesEqual(name))
1353 != layoutlist_.end();
1357 bool TextClass::hasInsetLayout(docstring const & n) const
1361 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1362 return it != insetlayoutlist_.end();
1366 Layout const & TextClass::operator[](docstring const & name) const
1368 LATTEST(!name.empty());
1371 find_if(begin(), end(), LayoutNamesEqual(name));
1374 LYXERR0("We failed to find the layout '" << name
1375 << "' in the layout list. You MUST investigate!");
1376 for (const_iterator cit = begin(); cit != end(); ++cit)
1377 lyxerr << " " << to_utf8(cit->name()) << endl;
1379 // We require the name to exist
1380 static const Layout dummy;
1381 LASSERT(false, return dummy);
1388 Layout & TextClass::operator[](docstring const & name)
1390 LATTEST(!name.empty());
1391 // Safe to continue, given what we do below.
1393 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1396 LYXERR0("We failed to find the layout '" << to_utf8(name)
1397 << "' in the layout list. You MUST investigate!");
1398 for (const_iterator cit = begin(); cit != end(); ++cit)
1399 LYXERR0(" " << to_utf8(cit->name()));
1401 // we require the name to exist
1403 // we are here only in release mode
1404 layoutlist_.push_back(createBasicLayout(name, true));
1405 it = find_if(begin(), end(), LayoutNamesEqual(name));
1412 bool TextClass::deleteLayout(docstring const & name)
1414 if (name == defaultLayoutName() || name == plainLayoutName())
1417 LayoutList::iterator it =
1418 remove_if(layoutlist_.begin(), layoutlist_.end(),
1419 LayoutNamesEqual(name));
1421 LayoutList::iterator end = layoutlist_.end();
1422 bool const ret = (it != end);
1423 layoutlist_.erase(it, end);
1428 bool TextClass::deleteInsetLayout(docstring const & name)
1430 return insetlayoutlist_.erase(name);
1434 // Load textclass info if not loaded yet
1435 bool TextClass::load(string const & path) const
1440 // Read style-file, provided path is searched before the system ones
1441 // If path is a file, it is loaded directly.
1442 FileName layout_file(path);
1443 if (!path.empty() && !layout_file.isReadableFile())
1444 layout_file = FileName(addName(path, name_ + ".layout"));
1445 if (layout_file.empty() || !layout_file.exists())
1446 layout_file = libFileSearch("layouts", name_, "layout");
1447 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1450 lyxerr << "Error reading `"
1451 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1452 << "'\n(Check `" << name_
1453 << "')\nCheck your installation and "
1454 "try Options/Reconfigure..."
1462 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1467 layoutlist_.push_back(createBasicLayout(n, true));
1472 string DocumentClass::forcedLayouts() const
1476 const_iterator const e = end();
1477 for (const_iterator i = begin(); i != e; ++i) {
1478 if (i->forcelocal > 0) {
1480 os << "Format " << LAYOUT_FORMAT << '\n';
1490 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1492 // FIXME The fix for the InsetLayout part of 4812 would be here:
1493 // Add the InsetLayout to the document class if it is not found.
1495 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1496 while (!n.empty()) {
1497 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1498 if (cit != cen && cit->first == n) {
1499 if (cit->second.obsoleted_by().empty())
1501 n = cit->second.obsoleted_by();
1502 return insetLayout(n);
1504 // If we have a generic prefix (e.g., "Note:"),
1505 // try if this one alone is found.
1506 size_t i = n.find(':');
1507 if (i == string::npos)
1511 // Layout "name" not found.
1512 return plain_insetlayout_;
1516 docstring const & TextClass::defaultLayoutName() const
1518 return defaultlayout_;
1522 Layout const & TextClass::defaultLayout() const
1524 return operator[](defaultLayoutName());
1528 bool TextClass::isDefaultLayout(Layout const & layout) const
1530 return layout.name() == defaultLayoutName();
1534 bool TextClass::isPlainLayout(Layout const & layout) const
1536 return layout.name() == plainLayoutName();
1540 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1542 static Layout * defaultLayout = NULL;
1544 if (defaultLayout) {
1545 defaultLayout->setUnknown(unknown);
1546 defaultLayout->setName(name);
1547 return *defaultLayout;
1550 static char const * s = "Margin Static\n"
1551 "LatexType Paragraph\n"
1554 "AlignPossible Left, Right, Center\n"
1555 "LabelType No_Label\n"
1557 istringstream ss(s);
1558 Lexer lex(textClassTags);
1560 defaultLayout = new Layout;
1561 defaultLayout->setUnknown(unknown);
1562 defaultLayout->setName(name);
1563 if (!readStyle(lex, *defaultLayout)) {
1564 // The only way this happens is because the hardcoded layout above
1568 return *defaultLayout;
1572 DocumentClassPtr getDocumentClass(
1573 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1576 DocumentClassPtr doc_class =
1577 DocumentClassPtr(new DocumentClass(baseClass));
1578 LayoutModuleList::const_iterator it = modlist.begin();
1579 LayoutModuleList::const_iterator en = modlist.end();
1580 for (; it != en; ++it) {
1581 string const modName = *it;
1582 LyXModule * lm = theModuleList[modName];
1584 docstring const msg =
1585 bformat(_("The module %1$s has been requested by\n"
1586 "this document but has not been found in the list of\n"
1587 "available modules. If you recently installed it, you\n"
1588 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1590 frontend::Alert::warning(_("Module not available"), msg);
1593 if (!lm->isAvailable() && !clone) {
1594 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1595 docstring const msg =
1596 bformat(_("The module %1$s requires a package that is not\n"
1597 "available in your LaTeX installation, or a converter that\n"
1598 "you have not installed. LaTeX output may not be possible.\n"
1599 "Missing prerequisites:\n"
1601 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1602 from_utf8(modName), prereqs);
1603 frontend::Alert::warning(_("Package not available"), msg, true);
1605 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1606 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1607 docstring const msg =
1608 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1609 frontend::Alert::warning(_("Read Error"), msg);
1616 /////////////////////////////////////////////////////////////////////////
1620 /////////////////////////////////////////////////////////////////////////
1622 DocumentClass::DocumentClass(LayoutFile const & tc)
1627 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1629 LayoutList::const_iterator it = layoutlist_.begin();
1630 LayoutList::const_iterator end = layoutlist_.end();
1631 for (; it != end; ++it)
1632 if (it->latexname() == lay)
1638 bool DocumentClass::provides(string const & p) const
1640 return provides_.find(p) != provides_.end();
1644 bool DocumentClass::hasTocLevels() const
1646 return min_toclevel_ != Layout::NOT_IN_TOC;
1650 Layout const & DocumentClass::getTOCLayout() const
1652 // we're going to look for the layout with the minimum toclevel
1653 TextClass::LayoutList::const_iterator lit = begin();
1654 TextClass::LayoutList::const_iterator const len = end();
1655 int minlevel = 1000;
1656 Layout const * lay = NULL;
1657 for (; lit != len; ++lit) {
1658 int const level = lit->toclevel;
1659 // we don't want Part or unnumbered sections
1660 if (level == Layout::NOT_IN_TOC || level < 0
1661 || level >= minlevel || lit->counter.empty())
1668 // hmm. that is very odd, so we'll do our best.
1669 return operator[](defaultLayoutName());
1673 Layout const & DocumentClass::htmlTOCLayout() const
1675 if (html_toc_section_.empty())
1676 html_toc_section_ = getTOCLayout().name();
1677 return operator[](html_toc_section_);
1681 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1682 string const & entry, string const & fallback) const
1684 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1686 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1687 if (itype == cite_formats_.end())
1688 return default_format;
1689 map<string, string>::const_iterator it = itype->second.find(entry);
1690 if (it == itype->second.end() && !fallback.empty())
1691 it = itype->second.find(fallback);
1692 if (it == itype->second.end())
1693 return default_format;
1698 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1699 string const & macro) const
1701 static string empty;
1702 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1703 if (itype == cite_macros_.end())
1705 map<string, string>::const_iterator it = itype->second.find(macro);
1706 if (it == itype->second.end())
1712 vector<string> const DocumentClass::citeCommands(
1713 CiteEngineType const & type) const
1715 vector<CitationStyle> const styles = citeStyles(type);
1716 vector<CitationStyle>::const_iterator it = styles.begin();
1717 vector<CitationStyle>::const_iterator end = styles.end();
1718 vector<string> cmds;
1719 for (; it != end; ++it) {
1720 CitationStyle const cite = *it;
1721 cmds.push_back(cite.cmd);
1727 vector<CitationStyle> const & DocumentClass::citeStyles(
1728 CiteEngineType const & type) const
1730 static vector<CitationStyle> empty;
1731 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1732 if (it == cite_styles_.end())
1738 /////////////////////////////////////////////////////////////////////////
1742 /////////////////////////////////////////////////////////////////////////
1744 ostream & operator<<(ostream & os, PageSides p)