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 = 55; //spitz: InsetLayout and Layout tags PassThruChars
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()
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";
161 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
163 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
164 if (!lay.read(lexrc, *this)) {
165 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
169 lay.resfont = lay.font;
170 lay.resfont.realize(defaultfont_);
171 lay.reslabelfont = lay.labelfont;
172 lay.reslabelfont.realize(defaultfont_);
173 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 { "packageoptions", TC_PKGOPTS },
260 { "pagestyle", TC_PAGESTYLE },
261 { "preamble", TC_PREAMBLE },
262 { "provides", TC_PROVIDES },
263 { "providesmodule", TC_PROVIDESMODULE },
264 { "requires", TC_REQUIRES },
265 { "rightmargin", TC_RIGHTMARGIN },
266 { "secnumdepth", TC_SECNUMDEPTH },
267 { "sides", TC_SIDES },
268 { "style", TC_STYLE },
269 { "titlelatexname", TC_TITLELATEXNAME },
270 { "titlelatextype", TC_TITLELATEXTYPE },
271 { "tocdepth", TC_TOCDEPTH }
277 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
279 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
280 TempFile tmp("convertXXXXXX.layout");
281 FileName const tempfile = tmp.name();
282 bool success = layout2layout(filename, tempfile);
284 success = readWithoutConv(tempfile, rt) == OK;
289 std::string TextClass::convert(std::string const & str)
291 TempFile tmp1("localXXXXXX.layout");
292 FileName const fn = tmp1.name();
293 ofstream os(fn.toFilesystemEncoding().c_str());
296 TempFile tmp2("convert_localXXXXXX.layout");
297 FileName const tempfile = tmp2.name();
298 bool success = layout2layout(fn, tempfile);
301 ifstream is(tempfile.toFilesystemEncoding().c_str());
313 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
315 if (!filename.isReadableFile()) {
316 lyxerr << "Cannot read layout file `" << filename << "'."
321 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
322 to_utf8(makeDisplayPath(filename.absFileName())));
324 // Define the plain layout used in table cells, ert, etc. Note that
325 // we do this before loading any layout file, so that classes can
326 // override features of this layout if they should choose to do so.
327 if (rt == BASECLASS && !hasLayout(plain_layout_))
328 layoutlist_.push_back(createBasicLayout(plain_layout_));
330 Lexer lexrc(textClassTags);
331 lexrc.setFile(filename);
332 ReturnValues retval = read(lexrc, rt);
334 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
335 to_utf8(makeDisplayPath(filename.absFileName())));
341 bool TextClass::read(FileName const & filename, ReadType rt)
343 ReturnValues const retval = readWithoutConv(filename, rt);
344 if (retval != FORMAT_MISMATCH)
347 bool const worx = convertLayoutFormat(filename, rt);
349 LYXERR0 ("Unable to convert " << filename <<
350 " to format " << LAYOUT_FORMAT);
355 TextClass::ReturnValues TextClass::validate(std::string const & str)
358 return tc.read(str, VALIDATION);
362 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
364 Lexer lexrc(textClassTags);
365 istringstream is(str);
367 ReturnValues retval = read(lexrc, rt);
369 if (retval != FORMAT_MISMATCH)
372 // write the layout string to a temporary file
373 TempFile tmp("TextClass_read");
374 FileName const tempfile = tmp.name();
375 ofstream os(tempfile.toFilesystemEncoding().c_str());
377 LYXERR0("Unable to create temporary file");
383 // now try to convert it
384 bool const worx = convertLayoutFormat(tempfile, rt);
386 LYXERR0("Unable to convert internal layout information to format "
394 // Reads a textclass structure from file.
395 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
400 // Format of files before the 'Format' tag was introduced
405 while (lexrc.isOK() && !error) {
406 int le = lexrc.lex();
409 case Lexer::LEX_FEOF:
412 case Lexer::LEX_UNDEF:
413 lexrc.printError("Unknown TextClass tag `$$Token'");
421 // used below to track whether we are in an IfStyle or IfCounter tag.
422 bool ifstyle = false;
423 bool ifcounter = false;
425 switch (static_cast<TextClassTags>(le)) {
429 format = lexrc.getInteger();
432 case TC_OUTPUTFORMAT:
434 outputFormat_ = lexrc.getString();
438 readOutputType(lexrc);
439 switch(outputType_) {
441 outputFormat_ = "latex";
444 outputFormat_ = "docbook";
447 outputFormat_ = "literate";
452 case TC_INPUT: // Include file
454 string const inc = lexrc.getString();
455 FileName tmp = libFileSearch("layouts", inc,
459 lexrc.printError("Could not find input file: " + inc);
461 } else if (!read(tmp, MERGE)) {
462 lexrc.printError("Error reading input file: " + tmp.absFileName());
468 case TC_DEFAULTSTYLE:
470 docstring const name = from_utf8(subst(lexrc.getString(),
472 defaultlayout_ = name;
481 lexrc.printError("No name given for style: `$$Token'.");
485 docstring const name = from_utf8(subst(lexrc.getString(),
488 string s = "Could not read name for style: `$$Token' "
489 + lexrc.getString() + " is probably not valid UTF-8!";
492 // Since we couldn't read the name, we just scan the rest
493 // of the style and discard it.
494 error = !readStyle(lexrc, lay);
495 } else if (hasLayout(name)) {
496 Layout & lay = operator[](name);
497 error = !readStyle(lexrc, lay);
498 } else if (!ifstyle) {
500 layout.setName(name);
501 error = !readStyle(lexrc, layout);
503 layoutlist_.push_back(layout);
505 if (defaultlayout_.empty()) {
506 // We do not have a default layout yet, so we choose
507 // the first layout we encounter.
508 defaultlayout_ = name;
512 // this was an ifstyle where we didn't have the style
513 // scan the rest and discard it
515 readStyle(lexrc, lay);
525 docstring const style = from_utf8(subst(lexrc.getString(),
527 if (!deleteLayout(style))
528 lyxerr << "Cannot delete style `"
529 << to_utf8(style) << '\'' << endl;
533 case TC_NOINSETLAYOUT:
535 docstring const style = from_utf8(subst(lexrc.getString(),
537 if (!deleteInsetLayout(style))
538 LYXERR0("Style `" << style << "' cannot be removed\n"
539 "because it was not found!");
545 columns_ = lexrc.getInteger();
550 switch (lexrc.getInteger()) {
551 case 1: sides_ = OneSide; break;
552 case 2: sides_ = TwoSides; break;
554 lyxerr << "Impossible number of page"
555 " sides, setting to one."
565 pagestyle_ = rtrim(lexrc.getString());
569 defaultfont_ = lyxRead(lexrc);
570 if (!defaultfont_.resolved()) {
571 lexrc.printError("Warning: defaultfont should "
572 "be fully instantiated!");
573 defaultfont_.realize(sane_font);
579 secnumdepth_ = lexrc.getInteger();
584 tocdepth_ = lexrc.getInteger();
587 // First step to support options
588 case TC_CLASSOPTIONS:
589 readClassOptions(lexrc);
593 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
596 case TC_HTMLPREAMBLE:
597 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
601 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
604 case TC_HTMLTOCSECTION:
605 html_toc_section_ = from_utf8(trim(lexrc.getString()));
608 case TC_ADDTOPREAMBLE:
609 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
612 case TC_ADDTOHTMLPREAMBLE:
613 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
616 case TC_ADDTOHTMLSTYLES:
617 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
622 string const feature = lexrc.getString();
624 if (lexrc.getInteger())
625 provides_.insert(feature);
627 provides_.erase(feature);
633 vector<string> const req
634 = getVectorFromString(lexrc.getString());
635 requires_.insert(req.begin(), req.end());
641 string const pkg = lexrc.getString();
643 string const options = lexrc.getString();
644 package_options_[pkg] = options;
648 case TC_DEFAULTMODULE: {
650 string const module = lexrc.getString();
651 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
652 default_modules_.push_back(module);
656 case TC_PROVIDESMODULE: {
658 string const module = lexrc.getString();
659 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
660 provided_modules_.push_back(module);
664 case TC_EXCLUDESMODULE: {
666 string const module = lexrc.getString();
667 // modules already have their own way to exclude other modules
669 LYXERR0("ExcludesModule tag cannot be used in a module!");
672 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
673 excluded_modules_.push_back(module);
677 case TC_LEFTMARGIN: // left margin type
679 leftmargin_ = lexrc.getDocString();
682 case TC_RIGHTMARGIN: // right margin type
684 rightmargin_ = lexrc.getDocString();
687 case TC_INSETLAYOUT: {
689 lexrc.printError("No name given for InsetLayout: `$$Token'.");
693 docstring const name = subst(lexrc.getDocString(), '_', ' ');
695 string s = "Could not read name for InsetLayout: `$$Token' "
696 + lexrc.getString() + " is probably not valid UTF-8!";
699 // Since we couldn't read the name, we just scan the rest
700 // of the style and discard it.
701 il.read(lexrc, *this);
702 // Let's try to continue rather than abort.
704 } else if (hasInsetLayout(name)) {
705 InsetLayout & il = insetlayoutlist_[name];
706 error = !il.read(lexrc, *this);
710 error = !il.read(lexrc, *this);
712 insetlayoutlist_[name] = il;
718 error = !readFloat(lexrc);
722 error = !readCiteEngine(lexrc);
725 case TC_CITEENGINETYPE:
727 opt_enginetype_ = rtrim(lexrc.getString());
731 error = !readCiteFormat(lexrc);
734 case TC_DEFAULTBIBLIO:
736 cite_default_biblio_style_ = rtrim(lexrc.getString());
739 case TC_FULLAUTHORLIST:
741 cite_full_author_list_ &= lexrc.getBool();
746 docstring const cnt = lexrc.getDocString();
747 if (!counters_.remove(cnt))
748 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
756 docstring const name = lexrc.getDocString();
758 string s = "Could not read name for counter: `$$Token' "
759 + lexrc.getString() + " is probably not valid UTF-8!";
760 lexrc.printError(s.c_str());
762 // Since we couldn't read the name, we just scan the rest
766 error = !counters_.read(lexrc, name, !ifcounter);
769 lexrc.printError("No name given for style: `$$Token'.");
776 case TC_TITLELATEXTYPE:
777 readTitleType(lexrc);
780 case TC_TITLELATEXNAME:
782 titlename_ = lexrc.getString();
787 string const nofloat = lexrc.getString();
788 floatlist_.erase(nofloat);
793 // Note that this is triggered the first time through the loop unless
794 // we hit a format tag.
795 if (format != LAYOUT_FORMAT)
796 return FORMAT_MISMATCH;
799 // at present, we abort if we encounter an error,
800 // so there is no point continuing.
805 return (error ? ERROR : OK);
807 if (defaultlayout_.empty()) {
808 LYXERR0("Error: Textclass '" << name_
809 << "' is missing a defaultstyle.");
813 // Try to erase "stdinsets" from the provides_ set.
815 // Provides stdinsets 1
816 // declaration simply tells us that the standard insets have been
817 // defined. (It's found in stdinsets.inc but could also be used in
818 // user-defined files.) There isn't really any such package. So we
819 // might as well go ahead and erase it.
820 // If we do not succeed, then it was not there, which means that
821 // the textclass did not provide the definitions of the standard
822 // insets. So we need to try to load them.
823 int erased = provides_.erase("stdinsets");
825 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
828 frontend::Alert::warning(_("Missing File"),
829 _("Could not find stdinsets.inc! This may lead to data loss!"));
831 } else if (!read(tmp, MERGE)) {
832 frontend::Alert::warning(_("Corrupt File"),
833 _("Could not read stdinsets.inc! This may lead to data loss!"));
838 min_toclevel_ = Layout::NOT_IN_TOC;
839 max_toclevel_ = Layout::NOT_IN_TOC;
840 const_iterator lit = begin();
841 const_iterator len = end();
842 for (; lit != len; ++lit) {
843 int const toclevel = lit->toclevel;
844 if (toclevel != Layout::NOT_IN_TOC) {
845 if (min_toclevel_ == Layout::NOT_IN_TOC)
846 min_toclevel_ = toclevel;
848 min_toclevel_ = min(min_toclevel_, toclevel);
849 max_toclevel_ = max(max_toclevel_, toclevel);
852 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
853 << ", maximum is " << max_toclevel_);
855 return (error ? ERROR : OK);
859 void TextClass::readTitleType(Lexer & lexrc)
861 LexerKeyword titleTypeTags[] = {
862 { "commandafter", TITLE_COMMAND_AFTER },
863 { "environment", TITLE_ENVIRONMENT }
866 PushPopHelper pph(lexrc, titleTypeTags);
868 int le = lexrc.lex();
870 case Lexer::LEX_UNDEF:
871 lexrc.printError("Unknown output type `$$Token'");
873 case TITLE_COMMAND_AFTER:
874 case TITLE_ENVIRONMENT:
875 titletype_ = static_cast<TitleLatexType>(le);
878 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
884 void TextClass::readOutputType(Lexer & lexrc)
886 LexerKeyword outputTypeTags[] = {
887 { "docbook", DOCBOOK },
889 { "literate", LITERATE }
892 PushPopHelper pph(lexrc, outputTypeTags);
894 int le = lexrc.lex();
896 case Lexer::LEX_UNDEF:
897 lexrc.printError("Unknown output type `$$Token'");
902 outputType_ = static_cast<OutputType>(le);
905 LYXERR0("Unhandled value " << le);
911 void TextClass::readClassOptions(Lexer & lexrc)
921 LexerKeyword classOptionsTags[] = {
923 {"fontsize", CO_FONTSIZE },
924 {"header", CO_HEADER },
925 {"other", CO_OTHER },
926 {"pagestyle", CO_PAGESTYLE }
929 lexrc.pushTable(classOptionsTags);
931 while (!getout && lexrc.isOK()) {
932 int le = lexrc.lex();
934 case Lexer::LEX_UNDEF:
935 lexrc.printError("Unknown ClassOption tag `$$Token'");
943 opt_fontsize_ = rtrim(lexrc.getString());
947 opt_pagestyle_ = rtrim(lexrc.getString());
951 if (options_.empty())
952 options_ = lexrc.getString();
954 options_ += ',' + lexrc.getString();
958 class_header_ = subst(lexrc.getString(), """, "\"");
969 bool TextClass::readCiteEngine(Lexer & lexrc)
971 int const type = readCiteEngineType(lexrc);
972 if (type & ENGINE_TYPE_AUTHORYEAR)
973 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
974 if (type & ENGINE_TYPE_NUMERICAL)
975 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
976 if (type & ENGINE_TYPE_DEFAULT)
977 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
980 while (!getout && lexrc.isOK()) {
982 def = lexrc.getString();
983 def = subst(def, " ", "");
984 def = subst(def, "\t", "");
985 if (compare_ascii_no_case(def, "end") == 0) {
995 cs.forceUpperCase = true;
999 size_t const n = def.size();
1000 for (size_t i = 0; i != n; ++i) {
1003 cs.fullAuthorList = true;
1004 else if (ichar == '[' && cs.textAfter)
1005 cs.textBefore = true;
1006 else if (ichar == '[')
1007 cs.textAfter = true;
1008 else if (ichar != ']')
1013 if (type & ENGINE_TYPE_AUTHORYEAR)
1014 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1015 if (type & ENGINE_TYPE_NUMERICAL)
1016 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1017 if (type & ENGINE_TYPE_DEFAULT)
1018 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1024 int TextClass::readCiteEngineType(Lexer & lexrc) const
1026 LATTEST(ENGINE_TYPE_DEFAULT ==
1027 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1028 if (!lexrc.next()) {
1029 lexrc.printError("No cite engine type given for token: `$$Token'.");
1030 return ENGINE_TYPE_DEFAULT;
1032 string const type = rtrim(lexrc.getString());
1033 if (compare_ascii_no_case(type, "authoryear") == 0)
1034 return ENGINE_TYPE_AUTHORYEAR;
1035 else if (compare_ascii_no_case(type, "numerical") == 0)
1036 return ENGINE_TYPE_NUMERICAL;
1037 else if (compare_ascii_no_case(type, "default") != 0) {
1038 string const s = "Unknown cite engine type `" + type
1039 + "' given for token: `$$Token',";
1040 lexrc.printError(s);
1042 return ENGINE_TYPE_DEFAULT;
1046 bool TextClass::readCiteFormat(Lexer & lexrc)
1048 int const type = readCiteEngineType(lexrc);
1051 while (lexrc.isOK()) {
1053 etype = lexrc.getString();
1054 if (compare_ascii_no_case(etype, "end") == 0)
1059 definition = lexrc.getString();
1060 char initchar = etype[0];
1061 if (initchar == '#')
1063 if (initchar == '!' || initchar == '_') {
1064 if (type & ENGINE_TYPE_AUTHORYEAR)
1065 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1066 if (type & ENGINE_TYPE_NUMERICAL)
1067 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1068 if (type & ENGINE_TYPE_DEFAULT)
1069 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1071 if (type & ENGINE_TYPE_AUTHORYEAR)
1072 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1073 if (type & ENGINE_TYPE_NUMERICAL)
1074 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1075 if (type & ENGINE_TYPE_DEFAULT)
1076 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1083 bool TextClass::readFloat(Lexer & lexrc)
1103 LexerKeyword floatTags[] = {
1105 { "extension", FT_EXT },
1106 { "guiname", FT_NAME },
1107 { "htmlattr", FT_HTMLATTR },
1108 { "htmlstyle", FT_HTMLSTYLE },
1109 { "htmltag", FT_HTMLTAG },
1110 { "ispredefined", FT_PREDEFINED },
1111 { "listcommand", FT_LISTCOMMAND },
1112 { "listname", FT_LISTNAME },
1113 { "numberwithin", FT_WITHIN },
1114 { "placement", FT_PLACEMENT },
1115 { "refprefix", FT_REFPREFIX },
1116 { "style", FT_STYLE },
1117 { "type", FT_TYPE },
1118 { "usesfloatpkg", FT_USESFLOAT }
1121 lexrc.pushTable(floatTags);
1135 bool usesfloat = true;
1136 bool ispredefined = false;
1138 bool getout = false;
1139 while (!getout && lexrc.isOK()) {
1140 int le = lexrc.lex();
1142 case Lexer::LEX_UNDEF:
1143 lexrc.printError("Unknown float tag `$$Token'");
1151 type = lexrc.getString();
1152 if (floatlist_.typeExist(type)) {
1153 Floating const & fl = floatlist_.getType(type);
1154 placement = fl.placement();
1156 within = fl.within();
1159 listname = fl.listName();
1160 usesfloat = fl.usesFloatPkg();
1161 ispredefined = fl.isPredefined();
1162 listcommand = fl.listCommand();
1163 refprefix = fl.refPrefix();
1168 name = lexrc.getString();
1172 placement = lexrc.getString();
1176 ext = lexrc.getString();
1180 within = lexrc.getString();
1181 if (within == "none")
1186 style = lexrc.getString();
1188 case FT_LISTCOMMAND:
1190 listcommand = lexrc.getString();
1194 refprefix = lexrc.getString();
1198 listname = lexrc.getString();
1202 usesfloat = lexrc.getBool();
1206 ispredefined = lexrc.getBool();
1210 htmlattr = lexrc.getString();
1214 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1218 htmltag = lexrc.getString();
1228 // Here we have a full float if getout == true
1230 if (!usesfloat && listcommand.empty()) {
1231 // if this float uses the same auxfile as an existing one,
1232 // there is no need for it to provide a list command.
1233 FloatList::const_iterator it = floatlist_.begin();
1234 FloatList::const_iterator en = floatlist_.end();
1235 bool found_ext = false;
1236 for (; it != en; ++it) {
1237 if (it->second.ext() == ext) {
1243 LYXERR0("The layout does not provide a list command " <<
1244 "for the float `" << type << "'. LyX will " <<
1245 "not be able to produce a float list.");
1247 Floating fl(type, placement, ext, within, style, name,
1248 listname, listcommand, refprefix,
1249 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1250 floatlist_.newFloat(fl);
1251 // each float has its own counter
1252 counters_.newCounter(from_ascii(type), from_ascii(within),
1253 docstring(), docstring());
1254 // also define sub-float counters
1255 docstring const subtype = "sub-" + from_ascii(type);
1256 counters_.newCounter(subtype, from_ascii(type),
1257 "\\alph{" + subtype + "}", docstring());
1263 string const & TextClass::prerequisites(string const & sep) const
1265 if (contains(prerequisites_, ',')) {
1266 vector<string> const pres = getVectorFromString(prerequisites_);
1267 prerequisites_ = getStringFromVector(pres, sep);
1269 return prerequisites_;
1273 bool TextClass::hasLayout(docstring const & n) const
1275 docstring const name = n.empty() ? defaultLayoutName() : n;
1277 return find_if(layoutlist_.begin(), layoutlist_.end(),
1278 LayoutNamesEqual(name))
1279 != layoutlist_.end();
1283 bool TextClass::hasInsetLayout(docstring const & n) const
1287 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1288 return it != insetlayoutlist_.end();
1292 Layout const & TextClass::operator[](docstring const & name) const
1294 LATTEST(!name.empty());
1297 find_if(begin(), end(), LayoutNamesEqual(name));
1300 LYXERR0("We failed to find the layout '" << name
1301 << "' in the layout list. You MUST investigate!");
1302 for (const_iterator cit = begin(); cit != end(); ++cit)
1303 lyxerr << " " << to_utf8(cit->name()) << endl;
1305 // We require the name to exist
1306 static const Layout dummy;
1307 LASSERT(false, return dummy);
1314 Layout & TextClass::operator[](docstring const & name)
1316 LATTEST(!name.empty());
1317 // Safe to continue, given what we do below.
1319 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1322 LYXERR0("We failed to find the layout '" << to_utf8(name)
1323 << "' in the layout list. You MUST investigate!");
1324 for (const_iterator cit = begin(); cit != end(); ++cit)
1325 LYXERR0(" " << to_utf8(cit->name()));
1327 // we require the name to exist
1329 // we are here only in release mode
1330 layoutlist_.push_back(createBasicLayout(name, true));
1331 it = find_if(begin(), end(), LayoutNamesEqual(name));
1338 bool TextClass::deleteLayout(docstring const & name)
1340 if (name == defaultLayoutName() || name == plainLayoutName())
1343 LayoutList::iterator it =
1344 remove_if(layoutlist_.begin(), layoutlist_.end(),
1345 LayoutNamesEqual(name));
1347 LayoutList::iterator end = layoutlist_.end();
1348 bool const ret = (it != end);
1349 layoutlist_.erase(it, end);
1354 bool TextClass::deleteInsetLayout(docstring const & name)
1356 return insetlayoutlist_.erase(name);
1360 // Load textclass info if not loaded yet
1361 bool TextClass::load(string const & path) const
1366 // Read style-file, provided path is searched before the system ones
1367 // If path is a file, it is loaded directly.
1368 FileName layout_file(path);
1369 if (!path.empty() && !layout_file.isReadableFile())
1370 layout_file = FileName(addName(path, name_ + ".layout"));
1371 if (layout_file.empty() || !layout_file.exists())
1372 layout_file = libFileSearch("layouts", name_, "layout");
1373 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1376 lyxerr << "Error reading `"
1377 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1378 << "'\n(Check `" << name_
1379 << "')\nCheck your installation and "
1380 "try Options/Reconfigure..."
1388 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1393 layoutlist_.push_back(createBasicLayout(n, true));
1398 string DocumentClass::forcedLayouts() const
1402 const_iterator const e = end();
1403 for (const_iterator i = begin(); i != e; ++i) {
1404 if (i->forcelocal > 0) {
1406 os << "Format " << LAYOUT_FORMAT << '\n';
1416 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1418 // FIXME The fix for the InsetLayout part of 4812 would be here:
1419 // Add the InsetLayout to the document class if it is not found.
1421 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1422 while (!n.empty()) {
1423 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1424 if (cit != cen && cit->first == n) {
1425 if (cit->second.obsoleted_by().empty())
1427 n = cit->second.obsoleted_by();
1428 return insetLayout(n);
1430 // If we have a generic prefix (e.g., "Note:"),
1431 // try if this one alone is found.
1432 size_t i = n.find(':');
1433 if (i == string::npos)
1437 // Layout "name" not found.
1438 return plain_insetlayout_;
1442 docstring const & TextClass::defaultLayoutName() const
1444 return defaultlayout_;
1448 Layout const & TextClass::defaultLayout() const
1450 return operator[](defaultLayoutName());
1454 bool TextClass::isDefaultLayout(Layout const & layout) const
1456 return layout.name() == defaultLayoutName();
1460 bool TextClass::isPlainLayout(Layout const & layout) const
1462 return layout.name() == plainLayoutName();
1466 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1468 static Layout * defaultLayout = NULL;
1470 if (defaultLayout) {
1471 defaultLayout->setUnknown(unknown);
1472 defaultLayout->setName(name);
1473 return *defaultLayout;
1476 static char const * s = "Margin Static\n"
1477 "LatexType Paragraph\n"
1480 "AlignPossible Left, Right, Center\n"
1481 "LabelType No_Label\n"
1483 istringstream ss(s);
1484 Lexer lex(textClassTags);
1486 defaultLayout = new Layout;
1487 defaultLayout->setUnknown(unknown);
1488 defaultLayout->setName(name);
1489 if (!readStyle(lex, *defaultLayout)) {
1490 // The only way this happens is because the hardcoded layout above
1494 return *defaultLayout;
1498 DocumentClassPtr getDocumentClass(
1499 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1502 DocumentClassPtr doc_class =
1503 DocumentClassPtr(new DocumentClass(baseClass));
1504 LayoutModuleList::const_iterator it = modlist.begin();
1505 LayoutModuleList::const_iterator en = modlist.end();
1506 for (; it != en; ++it) {
1507 string const modName = *it;
1508 LyXModule * lm = theModuleList[modName];
1510 docstring const msg =
1511 bformat(_("The module %1$s has been requested by\n"
1512 "this document but has not been found in the list of\n"
1513 "available modules. If you recently installed it, you\n"
1514 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1516 frontend::Alert::warning(_("Module not available"), msg);
1519 if (!lm->isAvailable() && !clone) {
1520 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1521 docstring const msg =
1522 bformat(_("The module %1$s requires a package that is not\n"
1523 "available in your LaTeX installation, or a converter that\n"
1524 "you have not installed. LaTeX output may not be possible.\n"
1525 "Missing prerequisites:\n"
1527 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1528 from_utf8(modName), prereqs);
1529 frontend::Alert::warning(_("Package not available"), msg, true);
1531 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1532 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1533 docstring const msg =
1534 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1535 frontend::Alert::warning(_("Read Error"), msg);
1542 /////////////////////////////////////////////////////////////////////////
1546 /////////////////////////////////////////////////////////////////////////
1548 DocumentClass::DocumentClass(LayoutFile const & tc)
1553 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1555 LayoutList::const_iterator it = layoutlist_.begin();
1556 LayoutList::const_iterator end = layoutlist_.end();
1557 for (; it != end; ++it)
1558 if (it->latexname() == lay)
1564 bool DocumentClass::provides(string const & p) const
1566 return provides_.find(p) != provides_.end();
1570 bool DocumentClass::hasTocLevels() const
1572 return min_toclevel_ != Layout::NOT_IN_TOC;
1576 Layout const & DocumentClass::getTOCLayout() const
1578 // we're going to look for the layout with the minimum toclevel
1579 TextClass::LayoutList::const_iterator lit = begin();
1580 TextClass::LayoutList::const_iterator const len = end();
1581 int minlevel = 1000;
1582 Layout const * lay = NULL;
1583 for (; lit != len; ++lit) {
1584 int const level = lit->toclevel;
1585 // we don't want Part or unnumbered sections
1586 if (level == Layout::NOT_IN_TOC || level < 0
1587 || level >= minlevel || lit->counter.empty())
1594 // hmm. that is very odd, so we'll do our best.
1595 return operator[](defaultLayoutName());
1599 Layout const & DocumentClass::htmlTOCLayout() const
1601 if (html_toc_section_.empty())
1602 html_toc_section_ = getTOCLayout().name();
1603 return operator[](html_toc_section_);
1607 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1608 string const & entry, string const & fallback) const
1610 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1612 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1613 if (itype == cite_formats_.end())
1614 return default_format;
1615 map<string, string>::const_iterator it = itype->second.find(entry);
1616 if (it == itype->second.end() && !fallback.empty())
1617 it = itype->second.find(fallback);
1618 if (it == itype->second.end())
1619 return default_format;
1624 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1625 string const & macro) const
1627 static string empty;
1628 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1629 if (itype == cite_macros_.end())
1631 map<string, string>::const_iterator it = itype->second.find(macro);
1632 if (it == itype->second.end())
1638 vector<string> const DocumentClass::citeCommands(
1639 CiteEngineType const & type) const
1641 vector<CitationStyle> const styles = citeStyles(type);
1642 vector<CitationStyle>::const_iterator it = styles.begin();
1643 vector<CitationStyle>::const_iterator end = styles.end();
1644 vector<string> cmds;
1645 for (; it != end; ++it) {
1646 CitationStyle const cite = *it;
1647 cmds.push_back(cite.cmd);
1653 vector<CitationStyle> const & DocumentClass::citeStyles(
1654 CiteEngineType const & type) const
1656 static vector<CitationStyle> empty;
1657 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1658 if (it == cite_styles_.end())
1664 /////////////////////////////////////////////////////////////////////////
1668 /////////////////////////////////////////////////////////////////////////
1670 ostream & operator<<(ostream & os, PageSides p)