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"
49 using namespace lyx::support;
53 // Keep the changes documented in the Customization manual.
55 // If you change this format, then you MUST also make sure that
56 // your changes do not invalidate the hardcoded layout file in
57 // LayoutFile.cpp. Additions will never do so, but syntax changes
58 // could. See LayoutFileList::addEmptyClass() and, especially, the
59 // definition of the layoutpost string.
60 // You should also run (or ask someone who has bash to run) the
61 // development/tools/updatelayouts.sh script, to update the format of
62 // all of our layout files.
64 int const LAYOUT_FORMAT = 46; // gb: New Tag "ForceLocal"
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("Plain Layout");
132 InsetLayout DocumentClass::plain_insetlayout_;
135 /////////////////////////////////////////////////////////////////////////
139 /////////////////////////////////////////////////////////////////////////
141 TextClass::TextClass()
144 outputFormat_ = "latex";
149 pagestyle_ = "default";
150 defaultfont_ = sane_font;
151 opt_enginetype_ = "authoryear|numerical";
152 opt_fontsize_ = "10|11|12";
153 opt_pagestyle_ = "empty|plain|headings|fancy";
154 cite_full_author_list_ = true;
155 titletype_ = TITLE_COMMAND_AFTER;
156 titlename_ = "maketitle";
158 _("Plain Layout"); // a hack to make this translatable
162 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
164 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
165 if (!lay.read(lexrc, *this)) {
166 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
170 lay.resfont = lay.font;
171 lay.resfont.realize(defaultfont_);
172 lay.reslabelfont = lay.labelfont;
173 lay.reslabelfont.realize(defaultfont_);
174 return true; // no errors
211 TC_ADDTOHTMLPREAMBLE,
227 LexerKeyword textClassTags[] = {
228 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
229 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
230 { "addtopreamble", TC_ADDTOPREAMBLE },
231 { "citeengine", TC_CITEENGINE },
232 { "citeenginetype", TC_CITEENGINETYPE },
233 { "citeformat", TC_CITEFORMAT },
234 { "classoptions", TC_CLASSOPTIONS },
235 { "columns", TC_COLUMNS },
236 { "counter", TC_COUNTER },
237 { "defaultbiblio", TC_DEFAULTBIBLIO },
238 { "defaultfont", TC_DEFAULTFONT },
239 { "defaultmodule", TC_DEFAULTMODULE },
240 { "defaultstyle", TC_DEFAULTSTYLE },
241 { "excludesmodule", TC_EXCLUDESMODULE },
242 { "float", TC_FLOAT },
243 { "format", TC_FORMAT },
244 { "fullauthorlist", TC_FULLAUTHORLIST },
245 { "htmlpreamble", TC_HTMLPREAMBLE },
246 { "htmlstyles", TC_HTMLSTYLES },
247 { "htmltocsection", TC_HTMLTOCSECTION },
248 { "ifcounter", TC_IFCOUNTER },
249 { "ifstyle", TC_IFSTYLE },
250 { "input", TC_INPUT },
251 { "insetlayout", TC_INSETLAYOUT },
252 { "leftmargin", TC_LEFTMARGIN },
253 { "nocounter", TC_NOCOUNTER },
254 { "nofloat", TC_NOFLOAT },
255 { "noinsetlayout", TC_NOINSETLAYOUT },
256 { "nostyle", TC_NOSTYLE },
257 { "outputformat", TC_OUTPUTFORMAT },
258 { "outputtype", TC_OUTPUTTYPE },
259 { "pagestyle", TC_PAGESTYLE },
260 { "preamble", TC_PREAMBLE },
261 { "provides", TC_PROVIDES },
262 { "providesmodule", TC_PROVIDESMODULE },
263 { "requires", TC_REQUIRES },
264 { "rightmargin", TC_RIGHTMARGIN },
265 { "secnumdepth", TC_SECNUMDEPTH },
266 { "sides", TC_SIDES },
267 { "style", TC_STYLE },
268 { "titlelatexname", TC_TITLELATEXNAME },
269 { "titlelatextype", TC_TITLELATEXTYPE },
270 { "tocdepth", TC_TOCDEPTH }
276 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
278 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
279 FileName const tempfile = FileName::tempName("convert_layout");
280 bool success = layout2layout(filename, tempfile);
282 success = readWithoutConv(tempfile, rt) == OK;
283 tempfile.removeFile();
288 std::string TextClass::convert(std::string const & str)
290 FileName const fn = FileName::tempName("locallayout");
291 ofstream os(fn.toFilesystemEncoding().c_str());
294 FileName const tempfile = FileName::tempName("convert_locallayout");
295 bool success = layout2layout(fn, tempfile);
298 ifstream is(tempfile.toFilesystemEncoding().c_str());
306 tempfile.removeFile();
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 FileName const tempfile = FileName::tempName("TextClass_read");
372 ofstream os(tempfile.toFilesystemEncoding().c_str());
374 LYXERR0("Unable to create temporary file");
380 // now try to convert it
381 bool const worx = convertLayoutFormat(tempfile, rt);
383 LYXERR0("Unable to convert internal layout information to format "
387 tempfile.removeFile();
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 ifstyle = false;
421 bool ifcounter = false;
423 switch (static_cast<TextClassTags>(le)) {
427 format = lexrc.getInteger();
430 case TC_OUTPUTFORMAT:
432 outputFormat_ = lexrc.getString();
436 readOutputType(lexrc);
437 switch(outputType_) {
439 outputFormat_ = "latex";
442 outputFormat_ = "docbook";
445 outputFormat_ = "literate";
450 case TC_INPUT: // Include file
452 string const inc = lexrc.getString();
453 FileName tmp = libFileSearch("layouts", inc,
457 lexrc.printError("Could not find input file: " + inc);
459 } else if (!read(tmp, MERGE)) {
460 lexrc.printError("Error reading input file: " + tmp.absFileName());
466 case TC_DEFAULTSTYLE:
468 docstring const name = from_utf8(subst(lexrc.getString(),
470 defaultlayout_ = name;
479 lexrc.printError("No name given for style: `$$Token'.");
483 docstring const name = from_utf8(subst(lexrc.getString(),
486 string s = "Could not read name for style: `$$Token' "
487 + lexrc.getString() + " is probably not valid UTF-8!";
490 // Since we couldn't read the name, we just scan the rest
491 // of the style and discard it.
492 error = !readStyle(lexrc, lay);
493 } else if (hasLayout(name)) {
494 Layout & lay = operator[](name);
495 error = !readStyle(lexrc, lay);
496 } else if (!ifstyle) {
498 layout.setName(name);
499 error = !readStyle(lexrc, layout);
501 layoutlist_.push_back(layout);
503 if (defaultlayout_.empty()) {
504 // We do not have a default layout yet, so we choose
505 // the first layout we encounter.
506 defaultlayout_ = name;
510 // this was an ifstyle where we didn't have the style
511 // scan the rest and discard it
513 readStyle(lexrc, lay);
523 docstring const style = from_utf8(subst(lexrc.getString(),
525 if (!deleteLayout(style))
526 lyxerr << "Cannot delete style `"
527 << to_utf8(style) << '\'' << endl;
531 case TC_NOINSETLAYOUT:
533 docstring const style = from_utf8(subst(lexrc.getString(),
535 if (!deleteInsetLayout(style))
536 LYXERR0("Style `" << style << "' cannot be removed\n"
537 "because it was not found!");
543 columns_ = lexrc.getInteger();
548 switch (lexrc.getInteger()) {
549 case 1: sides_ = OneSide; break;
550 case 2: sides_ = TwoSides; break;
552 lyxerr << "Impossible number of page"
553 " sides, setting to one."
563 pagestyle_ = rtrim(lexrc.getString());
567 defaultfont_ = lyxRead(lexrc);
568 if (!defaultfont_.resolved()) {
569 lexrc.printError("Warning: defaultfont should "
570 "be fully instantiated!");
571 defaultfont_.realize(sane_font);
577 secnumdepth_ = lexrc.getInteger();
582 tocdepth_ = lexrc.getInteger();
585 // First step to support options
586 case TC_CLASSOPTIONS:
587 readClassOptions(lexrc);
591 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
594 case TC_HTMLPREAMBLE:
595 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
599 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
602 case TC_HTMLTOCSECTION:
603 html_toc_section_ = from_utf8(trim(lexrc.getString()));
606 case TC_ADDTOPREAMBLE:
607 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
610 case TC_ADDTOHTMLPREAMBLE:
611 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
614 case TC_ADDTOHTMLSTYLES:
615 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
620 string const feature = lexrc.getString();
622 if (lexrc.getInteger())
623 provides_.insert(feature);
625 provides_.erase(feature);
631 vector<string> const req
632 = getVectorFromString(lexrc.getString());
633 requires_.insert(req.begin(), req.end());
637 case TC_DEFAULTMODULE: {
639 string const module = lexrc.getString();
640 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
641 default_modules_.push_back(module);
645 case TC_PROVIDESMODULE: {
647 string const module = lexrc.getString();
648 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
649 provided_modules_.push_back(module);
653 case TC_EXCLUDESMODULE: {
655 string const module = lexrc.getString();
656 // modules already have their own way to exclude other modules
658 LYXERR0("ExcludesModule tag cannot be used in a module!");
661 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
662 excluded_modules_.push_back(module);
666 case TC_LEFTMARGIN: // left margin type
668 leftmargin_ = lexrc.getDocString();
671 case TC_RIGHTMARGIN: // right margin type
673 rightmargin_ = lexrc.getDocString();
676 case TC_INSETLAYOUT: {
678 lexrc.printError("No name given for InsetLayout: `$$Token'.");
682 docstring const name = subst(lexrc.getDocString(), '_', ' ');
684 string s = "Could not read name for InsetLayout: `$$Token' "
685 + lexrc.getString() + " is probably not valid UTF-8!";
688 // Since we couldn't read the name, we just scan the rest
689 // of the style and discard it.
690 il.read(lexrc, *this);
691 // Let's try to continue rather than abort.
693 } else if (hasInsetLayout(name)) {
694 InsetLayout & il = insetlayoutlist_[name];
695 error = !il.read(lexrc, *this);
699 error = !il.read(lexrc, *this);
701 insetlayoutlist_[name] = il;
707 error = !readFloat(lexrc);
711 error = !readCiteEngine(lexrc);
714 case TC_CITEENGINETYPE:
716 opt_enginetype_ = rtrim(lexrc.getString());
720 error = !readCiteFormat(lexrc);
723 case TC_DEFAULTBIBLIO:
725 cite_default_biblio_style_ = rtrim(lexrc.getString());
728 case TC_FULLAUTHORLIST:
730 cite_full_author_list_ &= lexrc.getBool();
735 docstring const cnt = lexrc.getDocString();
736 if (!counters_.remove(cnt))
737 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
745 docstring const name = lexrc.getDocString();
747 string s = "Could not read name for counter: `$$Token' "
748 + lexrc.getString() + " is probably not valid UTF-8!";
749 lexrc.printError(s.c_str());
751 // Since we couldn't read the name, we just scan the rest
755 error = !counters_.read(lexrc, name, !ifcounter);
758 lexrc.printError("No name given for style: `$$Token'.");
765 case TC_TITLELATEXTYPE:
766 readTitleType(lexrc);
769 case TC_TITLELATEXNAME:
771 titlename_ = lexrc.getString();
776 string const nofloat = lexrc.getString();
777 floatlist_.erase(nofloat);
782 // Note that this is triggered the first time through the loop unless
783 // we hit a format tag.
784 if (format != LAYOUT_FORMAT)
785 return FORMAT_MISMATCH;
788 // at present, we abort if we encounter an error,
789 // so there is no point continuing.
794 return (error ? ERROR : OK);
796 if (defaultlayout_.empty()) {
797 LYXERR0("Error: Textclass '" << name_
798 << "' is missing a defaultstyle.");
802 // Try to erase "stdinsets" from the provides_ set.
804 // Provides stdinsets 1
805 // declaration simply tells us that the standard insets have been
806 // defined. (It's found in stdinsets.inc but could also be used in
807 // user-defined files.) There isn't really any such package. So we
808 // might as well go ahead and erase it.
809 // If we do not succeed, then it was not there, which means that
810 // the textclass did not provide the definitions of the standard
811 // insets. So we need to try to load them.
812 int erased = provides_.erase("stdinsets");
814 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
817 frontend::Alert::warning(_("Missing File"),
818 _("Could not find stdinsets.inc! This may lead to data loss!"));
820 } else if (!read(tmp, MERGE)) {
821 frontend::Alert::warning(_("Corrupt File"),
822 _("Could not read stdinsets.inc! This may lead to data loss!"));
827 min_toclevel_ = Layout::NOT_IN_TOC;
828 max_toclevel_ = Layout::NOT_IN_TOC;
829 const_iterator lit = begin();
830 const_iterator len = end();
831 for (; lit != len; ++lit) {
832 int const toclevel = lit->toclevel;
833 if (toclevel != Layout::NOT_IN_TOC) {
834 if (min_toclevel_ == Layout::NOT_IN_TOC)
835 min_toclevel_ = toclevel;
837 min_toclevel_ = min(min_toclevel_, toclevel);
838 max_toclevel_ = max(max_toclevel_, toclevel);
841 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
842 << ", maximum is " << max_toclevel_);
844 return (error ? ERROR : OK);
848 void TextClass::readTitleType(Lexer & lexrc)
850 LexerKeyword titleTypeTags[] = {
851 { "commandafter", TITLE_COMMAND_AFTER },
852 { "environment", TITLE_ENVIRONMENT }
855 PushPopHelper pph(lexrc, titleTypeTags);
857 int le = lexrc.lex();
859 case Lexer::LEX_UNDEF:
860 lexrc.printError("Unknown output type `$$Token'");
862 case TITLE_COMMAND_AFTER:
863 case TITLE_ENVIRONMENT:
864 titletype_ = static_cast<TitleLatexType>(le);
867 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
873 void TextClass::readOutputType(Lexer & lexrc)
875 LexerKeyword outputTypeTags[] = {
876 { "docbook", DOCBOOK },
878 { "literate", LITERATE }
881 PushPopHelper pph(lexrc, outputTypeTags);
883 int le = lexrc.lex();
885 case Lexer::LEX_UNDEF:
886 lexrc.printError("Unknown output type `$$Token'");
891 outputType_ = static_cast<OutputType>(le);
894 LYXERR0("Unhandled value " << le);
900 void TextClass::readClassOptions(Lexer & lexrc)
910 LexerKeyword classOptionsTags[] = {
912 {"fontsize", CO_FONTSIZE },
913 {"header", CO_HEADER },
914 {"other", CO_OTHER },
915 {"pagestyle", CO_PAGESTYLE }
918 lexrc.pushTable(classOptionsTags);
920 while (!getout && lexrc.isOK()) {
921 int le = lexrc.lex();
923 case Lexer::LEX_UNDEF:
924 lexrc.printError("Unknown ClassOption tag `$$Token'");
932 opt_fontsize_ = rtrim(lexrc.getString());
936 opt_pagestyle_ = rtrim(lexrc.getString());
940 if (options_.empty())
941 options_ = lexrc.getString();
943 options_ += ',' + lexrc.getString();
947 class_header_ = subst(lexrc.getString(), """, "\"");
958 bool TextClass::readCiteEngine(Lexer & lexrc)
960 int const type = readCiteEngineType(lexrc);
961 if (type & ENGINE_TYPE_AUTHORYEAR)
962 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
963 if (type & ENGINE_TYPE_NUMERICAL)
964 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
965 if (type & ENGINE_TYPE_DEFAULT)
966 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
969 while (!getout && lexrc.isOK()) {
971 def = lexrc.getString();
972 def = subst(def, " ", "");
973 def = subst(def, "\t", "");
974 if (compare_ascii_no_case(def, "end") == 0) {
984 cs.forceUpperCase = true;
988 size_t const n = def.size();
989 for (size_t i = 0; i != n; ++i) {
992 cs.fullAuthorList = true;
993 else if (ichar == '[' && cs.textAfter)
994 cs.textBefore = true;
995 else if (ichar == '[')
997 else if (ichar != ']')
1002 if (type & ENGINE_TYPE_AUTHORYEAR)
1003 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1004 if (type & ENGINE_TYPE_NUMERICAL)
1005 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1006 if (type & ENGINE_TYPE_DEFAULT)
1007 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1013 int TextClass::readCiteEngineType(Lexer & lexrc) const
1015 LATTEST(ENGINE_TYPE_DEFAULT ==
1016 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1017 if (!lexrc.next()) {
1018 lexrc.printError("No cite engine type given for token: `$$Token'.");
1019 return ENGINE_TYPE_DEFAULT;
1021 string const type = rtrim(lexrc.getString());
1022 if (compare_ascii_no_case(type, "authoryear") == 0)
1023 return ENGINE_TYPE_AUTHORYEAR;
1024 else if (compare_ascii_no_case(type, "numerical") == 0)
1025 return ENGINE_TYPE_NUMERICAL;
1026 else if (compare_ascii_no_case(type, "default") != 0) {
1027 string const s = "Unknown cite engine type `" + type
1028 + "' given for token: `$$Token',";
1029 lexrc.printError(s);
1031 return ENGINE_TYPE_DEFAULT;
1035 bool TextClass::readCiteFormat(Lexer & lexrc)
1037 int const type = readCiteEngineType(lexrc);
1040 while (lexrc.isOK()) {
1042 etype = lexrc.getString();
1043 if (compare_ascii_no_case(etype, "end") == 0)
1048 definition = lexrc.getString();
1049 char initchar = etype[0];
1050 if (initchar == '#')
1052 if (initchar == '!' || initchar == '_') {
1053 if (type & ENGINE_TYPE_AUTHORYEAR)
1054 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1055 if (type & ENGINE_TYPE_NUMERICAL)
1056 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1057 if (type & ENGINE_TYPE_DEFAULT)
1058 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1060 if (type & ENGINE_TYPE_AUTHORYEAR)
1061 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1062 if (type & ENGINE_TYPE_NUMERICAL)
1063 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1064 if (type & ENGINE_TYPE_DEFAULT)
1065 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1072 bool TextClass::readFloat(Lexer & lexrc)
1092 LexerKeyword floatTags[] = {
1094 { "extension", FT_EXT },
1095 { "guiname", FT_NAME },
1096 { "htmlattr", FT_HTMLATTR },
1097 { "htmlstyle", FT_HTMLSTYLE },
1098 { "htmltag", FT_HTMLTAG },
1099 { "ispredefined", FT_PREDEFINED },
1100 { "listcommand", FT_LISTCOMMAND },
1101 { "listname", FT_LISTNAME },
1102 { "numberwithin", FT_WITHIN },
1103 { "placement", FT_PLACEMENT },
1104 { "refprefix", FT_REFPREFIX },
1105 { "style", FT_STYLE },
1106 { "type", FT_TYPE },
1107 { "usesfloatpkg", FT_USESFLOAT }
1110 lexrc.pushTable(floatTags);
1124 bool usesfloat = true;
1125 bool ispredefined = false;
1127 bool getout = false;
1128 while (!getout && lexrc.isOK()) {
1129 int le = lexrc.lex();
1131 case Lexer::LEX_UNDEF:
1132 lexrc.printError("Unknown float tag `$$Token'");
1140 type = lexrc.getString();
1141 if (floatlist_.typeExist(type)) {
1142 Floating const & fl = floatlist_.getType(type);
1143 placement = fl.placement();
1145 within = fl.within();
1148 listname = fl.listName();
1149 usesfloat = fl.usesFloatPkg();
1150 ispredefined = fl.isPredefined();
1151 listcommand = fl.listCommand();
1152 refprefix = fl.refPrefix();
1157 name = lexrc.getString();
1161 placement = lexrc.getString();
1165 ext = lexrc.getString();
1169 within = lexrc.getString();
1170 if (within == "none")
1175 style = lexrc.getString();
1177 case FT_LISTCOMMAND:
1179 listcommand = lexrc.getString();
1183 refprefix = lexrc.getString();
1187 listname = lexrc.getString();
1191 usesfloat = lexrc.getBool();
1195 ispredefined = lexrc.getBool();
1199 htmlattr = lexrc.getString();
1203 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1207 htmltag = lexrc.getString();
1217 // Here we have a full float if getout == true
1219 if (!usesfloat && listcommand.empty()) {
1220 // if this float uses the same auxfile as an existing one,
1221 // there is no need for it to provide a list command.
1222 FloatList::const_iterator it = floatlist_.begin();
1223 FloatList::const_iterator en = floatlist_.end();
1224 bool found_ext = false;
1225 for (; it != en; ++it) {
1226 if (it->second.ext() == ext) {
1232 LYXERR0("The layout does not provide a list command " <<
1233 "for the float `" << type << "'. LyX will " <<
1234 "not be able to produce a float list.");
1236 Floating fl(type, placement, ext, within, style, name,
1237 listname, listcommand, refprefix,
1238 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1239 floatlist_.newFloat(fl);
1240 // each float has its own counter
1241 counters_.newCounter(from_ascii(type), from_ascii(within),
1242 docstring(), docstring());
1243 // also define sub-float counters
1244 docstring const subtype = "sub-" + from_ascii(type);
1245 counters_.newCounter(subtype, from_ascii(type),
1246 "\\alph{" + subtype + "}", docstring());
1252 string const & TextClass::prerequisites(string const & sep) const
1254 if (contains(prerequisites_, ',')) {
1255 vector<string> const pres = getVectorFromString(prerequisites_);
1256 prerequisites_ = getStringFromVector(pres, sep);
1258 return prerequisites_;
1262 bool TextClass::hasLayout(docstring const & n) const
1264 docstring const name = n.empty() ? defaultLayoutName() : n;
1266 return find_if(layoutlist_.begin(), layoutlist_.end(),
1267 LayoutNamesEqual(name))
1268 != layoutlist_.end();
1272 bool TextClass::hasInsetLayout(docstring const & n) const
1276 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1277 return it != insetlayoutlist_.end();
1281 Layout const & TextClass::operator[](docstring const & name) const
1283 LATTEST(!name.empty());
1286 find_if(begin(), end(), LayoutNamesEqual(name));
1289 LYXERR0("We failed to find the layout '" << name
1290 << "' in the layout list. You MUST investigate!");
1291 for (const_iterator cit = begin(); cit != end(); ++cit)
1292 lyxerr << " " << to_utf8(cit->name()) << endl;
1294 // We require the name to exist
1295 static const Layout dummy;
1296 LASSERT(false, return dummy);
1303 Layout & TextClass::operator[](docstring const & name)
1305 LATTEST(!name.empty());
1306 // Safe to continue, given what we do below.
1308 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1311 LYXERR0("We failed to find the layout '" << to_utf8(name)
1312 << "' in the layout list. You MUST investigate!");
1313 for (const_iterator cit = begin(); cit != end(); ++cit)
1314 LYXERR0(" " << to_utf8(cit->name()));
1316 // we require the name to exist
1318 // we are here only in release mode
1319 layoutlist_.push_back(createBasicLayout(name, true));
1320 it = find_if(begin(), end(), LayoutNamesEqual(name));
1327 bool TextClass::deleteLayout(docstring const & name)
1329 if (name == defaultLayoutName() || name == plainLayoutName())
1332 LayoutList::iterator it =
1333 remove_if(layoutlist_.begin(), layoutlist_.end(),
1334 LayoutNamesEqual(name));
1336 LayoutList::iterator end = layoutlist_.end();
1337 bool const ret = (it != end);
1338 layoutlist_.erase(it, end);
1343 bool TextClass::deleteInsetLayout(docstring const & name)
1345 return insetlayoutlist_.erase(name);
1349 // Load textclass info if not loaded yet
1350 bool TextClass::load(string const & path) const
1355 // Read style-file, provided path is searched before the system ones
1356 // If path is a file, it is loaded directly.
1357 FileName layout_file(path);
1358 if (!path.empty() && !layout_file.isReadableFile())
1359 layout_file = FileName(addName(path, name_ + ".layout"));
1360 if (layout_file.empty() || !layout_file.exists())
1361 layout_file = libFileSearch("layouts", name_, "layout");
1362 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1365 lyxerr << "Error reading `"
1366 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1367 << "'\n(Check `" << name_
1368 << "')\nCheck your installation and "
1369 "try Options/Reconfigure..."
1377 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1382 layoutlist_.push_back(createBasicLayout(n, true));
1387 string DocumentClass::forcedLayouts() const
1391 const_iterator const e = end();
1392 for (const_iterator i = begin(); i != e; ++i) {
1393 if (i->forcelocal > 0) {
1395 os << "Format " << LAYOUT_FORMAT << '\n';
1405 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1407 // FIXME The fix for the InsetLayout part of 4812 would be here:
1408 // Add the InsetLayout to the document class if it is not found.
1410 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1411 while (!n.empty()) {
1412 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1413 if (cit != cen && cit->first == n)
1415 size_t i = n.find(':');
1416 if (i == string::npos)
1420 return plain_insetlayout_;
1424 docstring const & TextClass::defaultLayoutName() const
1426 return defaultlayout_;
1430 Layout const & TextClass::defaultLayout() const
1432 return operator[](defaultLayoutName());
1436 bool TextClass::isDefaultLayout(Layout const & layout) const
1438 return layout.name() == defaultLayoutName();
1442 bool TextClass::isPlainLayout(Layout const & layout) const
1444 return layout.name() == plainLayoutName();
1448 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1450 static Layout * defaultLayout = NULL;
1452 if (defaultLayout) {
1453 defaultLayout->setUnknown(unknown);
1454 defaultLayout->setName(name);
1455 return *defaultLayout;
1458 static char const * s = "Margin Static\n"
1459 "LatexType Paragraph\n"
1462 "AlignPossible Left, Right, Center\n"
1463 "LabelType No_Label\n"
1465 istringstream ss(s);
1466 Lexer lex(textClassTags);
1468 defaultLayout = new Layout;
1469 defaultLayout->setUnknown(unknown);
1470 defaultLayout->setName(name);
1471 if (!readStyle(lex, *defaultLayout)) {
1472 // The only way this happens is because the hardcoded layout above
1476 return *defaultLayout;
1480 DocumentClassPtr getDocumentClass(
1481 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1483 DocumentClassPtr doc_class =
1484 DocumentClassPtr(new DocumentClass(baseClass));
1485 LayoutModuleList::const_iterator it = modlist.begin();
1486 LayoutModuleList::const_iterator en = modlist.end();
1487 for (; it != en; ++it) {
1488 string const modName = *it;
1489 LyXModule * lm = theModuleList[modName];
1491 docstring const msg =
1492 bformat(_("The module %1$s has been requested by\n"
1493 "this document but has not been found in the list of\n"
1494 "available modules. If you recently installed it, you\n"
1495 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1496 frontend::Alert::warning(_("Module not available"), msg);
1499 if (!lm->isAvailable()) {
1500 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1501 docstring const msg =
1502 bformat(_("The module %1$s requires a package that is not\n"
1503 "available in your LaTeX installation, or a converter that\n"
1504 "you have not installed. LaTeX output may not be possible.\n"
1505 "Missing prerequisites:\n"
1507 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1508 from_utf8(modName), prereqs);
1509 frontend::Alert::warning(_("Package not available"), msg, true);
1511 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1512 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1513 docstring const msg =
1514 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1515 frontend::Alert::warning(_("Read Error"), msg);
1522 /////////////////////////////////////////////////////////////////////////
1526 /////////////////////////////////////////////////////////////////////////
1528 DocumentClass::DocumentClass(LayoutFile const & tc)
1533 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1535 LayoutList::const_iterator it = layoutlist_.begin();
1536 LayoutList::const_iterator end = layoutlist_.end();
1537 for (; it != end; ++it)
1538 if (it->latexname() == lay)
1544 bool DocumentClass::provides(string const & p) const
1546 return provides_.find(p) != provides_.end();
1550 bool DocumentClass::hasTocLevels() const
1552 return min_toclevel_ != Layout::NOT_IN_TOC;
1556 Layout const & DocumentClass::getTOCLayout() const
1558 // we're going to look for the layout with the minimum toclevel
1559 TextClass::LayoutList::const_iterator lit = begin();
1560 TextClass::LayoutList::const_iterator const len = end();
1561 int minlevel = 1000;
1562 Layout const * lay = NULL;
1563 for (; lit != len; ++lit) {
1564 int const level = lit->toclevel;
1565 // we don't want Part or unnumbered sections
1566 if (level == Layout::NOT_IN_TOC || level < 0
1567 || level >= minlevel || lit->counter.empty())
1574 // hmm. that is very odd, so we'll do our best.
1575 return operator[](defaultLayoutName());
1579 Layout const & DocumentClass::htmlTOCLayout() const
1581 if (html_toc_section_.empty())
1582 html_toc_section_ = getTOCLayout().name();
1583 return operator[](html_toc_section_);
1587 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1588 string const & entry, string const & fallback) const
1590 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1592 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1593 if (itype == cite_formats_.end())
1594 return default_format;
1595 map<string, string>::const_iterator it = itype->second.find(entry);
1596 if (it == itype->second.end() && !fallback.empty())
1597 it = itype->second.find(fallback);
1598 if (it == itype->second.end())
1599 return default_format;
1604 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1605 string const & macro) const
1607 static string empty;
1608 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1609 if (itype == cite_macros_.end())
1611 map<string, string>::const_iterator it = itype->second.find(macro);
1612 if (it == itype->second.end())
1618 vector<string> const DocumentClass::citeCommands(
1619 CiteEngineType const & type) const
1621 vector<CitationStyle> const styles = citeStyles(type);
1622 vector<CitationStyle>::const_iterator it = styles.begin();
1623 vector<CitationStyle>::const_iterator end = styles.end();
1624 vector<string> cmds;
1625 for (; it != end; ++it) {
1626 CitationStyle const cite = *it;
1627 cmds.push_back(cite.cmd);
1633 vector<CitationStyle> const & DocumentClass::citeStyles(
1634 CiteEngineType const & type) const
1636 static vector<CitationStyle> empty;
1637 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1638 if (it == cite_styles_.end())
1644 /////////////////////////////////////////////////////////////////////////
1648 /////////////////////////////////////////////////////////////////////////
1650 ostream & operator<<(ostream & os, PageSides p)