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 = 57; //spitz: New Layout tag ParagraphGroup
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)
1100 FT_ALLOWED_PLACEMENT,
1106 LexerKeyword floatTags[] = {
1107 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1108 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1109 { "allowswide", FT_ALLOWS_WIDE },
1111 { "extension", FT_EXT },
1112 { "guiname", FT_NAME },
1113 { "htmlattr", FT_HTMLATTR },
1114 { "htmlstyle", FT_HTMLSTYLE },
1115 { "htmltag", FT_HTMLTAG },
1116 { "ispredefined", FT_PREDEFINED },
1117 { "listcommand", FT_LISTCOMMAND },
1118 { "listname", FT_LISTNAME },
1119 { "numberwithin", FT_WITHIN },
1120 { "placement", FT_PLACEMENT },
1121 { "refprefix", FT_REFPREFIX },
1122 { "style", FT_STYLE },
1123 { "type", FT_TYPE },
1124 { "usesfloatpkg", FT_USESFLOAT }
1127 lexrc.pushTable(floatTags);
1137 string allowed_placement = "!htbpH";
1142 bool usesfloat = true;
1143 bool ispredefined = false;
1144 bool allowswide = true;
1145 bool allowssideways = true;
1147 bool getout = false;
1148 while (!getout && lexrc.isOK()) {
1149 int le = lexrc.lex();
1151 case Lexer::LEX_UNDEF:
1152 lexrc.printError("Unknown float tag `$$Token'");
1160 type = lexrc.getString();
1161 if (floatlist_.typeExist(type)) {
1162 Floating const & fl = floatlist_.getType(type);
1163 placement = fl.placement();
1165 within = fl.within();
1168 listname = fl.listName();
1169 usesfloat = fl.usesFloatPkg();
1170 ispredefined = fl.isPredefined();
1171 listcommand = fl.listCommand();
1172 refprefix = fl.refPrefix();
1177 name = lexrc.getString();
1181 placement = lexrc.getString();
1183 case FT_ALLOWED_PLACEMENT:
1185 allowed_placement = lexrc.getString();
1189 ext = lexrc.getString();
1193 within = lexrc.getString();
1194 if (within == "none")
1199 style = lexrc.getString();
1201 case FT_LISTCOMMAND:
1203 listcommand = lexrc.getString();
1207 refprefix = lexrc.getString();
1211 listname = lexrc.getString();
1215 usesfloat = lexrc.getBool();
1219 ispredefined = lexrc.getBool();
1221 case FT_ALLOWS_SIDEWAYS:
1223 allowssideways = lexrc.getBool();
1225 case FT_ALLOWS_WIDE:
1227 allowswide = lexrc.getBool();
1231 htmlattr = lexrc.getString();
1235 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1239 htmltag = lexrc.getString();
1249 // Here we have a full float if getout == true
1251 if (!usesfloat && listcommand.empty()) {
1252 // if this float uses the same auxfile as an existing one,
1253 // there is no need for it to provide a list command.
1254 FloatList::const_iterator it = floatlist_.begin();
1255 FloatList::const_iterator en = floatlist_.end();
1256 bool found_ext = false;
1257 for (; it != en; ++it) {
1258 if (it->second.ext() == ext) {
1264 LYXERR0("The layout does not provide a list command " <<
1265 "for the float `" << type << "'. LyX will " <<
1266 "not be able to produce a float list.");
1268 Floating fl(type, placement, ext, within, style, name,
1269 listname, listcommand, refprefix, allowed_placement,
1270 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1271 allowswide, allowssideways);
1272 floatlist_.newFloat(fl);
1273 // each float has its own counter
1274 counters_.newCounter(from_ascii(type), from_ascii(within),
1275 docstring(), docstring());
1276 // also define sub-float counters
1277 docstring const subtype = "sub-" + from_ascii(type);
1278 counters_.newCounter(subtype, from_ascii(type),
1279 "\\alph{" + subtype + "}", docstring());
1285 string const & TextClass::prerequisites(string const & sep) const
1287 if (contains(prerequisites_, ',')) {
1288 vector<string> const pres = getVectorFromString(prerequisites_);
1289 prerequisites_ = getStringFromVector(pres, sep);
1291 return prerequisites_;
1295 bool TextClass::hasLayout(docstring const & n) const
1297 docstring const name = n.empty() ? defaultLayoutName() : n;
1299 return find_if(layoutlist_.begin(), layoutlist_.end(),
1300 LayoutNamesEqual(name))
1301 != layoutlist_.end();
1305 bool TextClass::hasInsetLayout(docstring const & n) const
1309 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1310 return it != insetlayoutlist_.end();
1314 Layout const & TextClass::operator[](docstring const & name) const
1316 LATTEST(!name.empty());
1319 find_if(begin(), end(), LayoutNamesEqual(name));
1322 LYXERR0("We failed to find the layout '" << name
1323 << "' in the layout list. You MUST investigate!");
1324 for (const_iterator cit = begin(); cit != end(); ++cit)
1325 lyxerr << " " << to_utf8(cit->name()) << endl;
1327 // We require the name to exist
1328 static const Layout dummy;
1329 LASSERT(false, return dummy);
1336 Layout & TextClass::operator[](docstring const & name)
1338 LATTEST(!name.empty());
1339 // Safe to continue, given what we do below.
1341 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1344 LYXERR0("We failed to find the layout '" << to_utf8(name)
1345 << "' in the layout list. You MUST investigate!");
1346 for (const_iterator cit = begin(); cit != end(); ++cit)
1347 LYXERR0(" " << to_utf8(cit->name()));
1349 // we require the name to exist
1351 // we are here only in release mode
1352 layoutlist_.push_back(createBasicLayout(name, true));
1353 it = find_if(begin(), end(), LayoutNamesEqual(name));
1360 bool TextClass::deleteLayout(docstring const & name)
1362 if (name == defaultLayoutName() || name == plainLayoutName())
1365 LayoutList::iterator it =
1366 remove_if(layoutlist_.begin(), layoutlist_.end(),
1367 LayoutNamesEqual(name));
1369 LayoutList::iterator end = layoutlist_.end();
1370 bool const ret = (it != end);
1371 layoutlist_.erase(it, end);
1376 bool TextClass::deleteInsetLayout(docstring const & name)
1378 return insetlayoutlist_.erase(name);
1382 // Load textclass info if not loaded yet
1383 bool TextClass::load(string const & path) const
1388 // Read style-file, provided path is searched before the system ones
1389 // If path is a file, it is loaded directly.
1390 FileName layout_file(path);
1391 if (!path.empty() && !layout_file.isReadableFile())
1392 layout_file = FileName(addName(path, name_ + ".layout"));
1393 if (layout_file.empty() || !layout_file.exists())
1394 layout_file = libFileSearch("layouts", name_, "layout");
1395 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1398 lyxerr << "Error reading `"
1399 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1400 << "'\n(Check `" << name_
1401 << "')\nCheck your installation and "
1402 "try Options/Reconfigure..."
1410 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1415 layoutlist_.push_back(createBasicLayout(n, true));
1420 string DocumentClass::forcedLayouts() const
1424 const_iterator const e = end();
1425 for (const_iterator i = begin(); i != e; ++i) {
1426 if (i->forcelocal > 0) {
1428 os << "Format " << LAYOUT_FORMAT << '\n';
1438 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1440 // FIXME The fix for the InsetLayout part of 4812 would be here:
1441 // Add the InsetLayout to the document class if it is not found.
1443 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1444 while (!n.empty()) {
1445 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1446 if (cit != cen && cit->first == n) {
1447 if (cit->second.obsoleted_by().empty())
1449 n = cit->second.obsoleted_by();
1450 return insetLayout(n);
1452 // If we have a generic prefix (e.g., "Note:"),
1453 // try if this one alone is found.
1454 size_t i = n.find(':');
1455 if (i == string::npos)
1459 // Layout "name" not found.
1460 return plain_insetlayout_;
1464 docstring const & TextClass::defaultLayoutName() const
1466 return defaultlayout_;
1470 Layout const & TextClass::defaultLayout() const
1472 return operator[](defaultLayoutName());
1476 bool TextClass::isDefaultLayout(Layout const & layout) const
1478 return layout.name() == defaultLayoutName();
1482 bool TextClass::isPlainLayout(Layout const & layout) const
1484 return layout.name() == plainLayoutName();
1488 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1490 static Layout * defaultLayout = NULL;
1492 if (defaultLayout) {
1493 defaultLayout->setUnknown(unknown);
1494 defaultLayout->setName(name);
1495 return *defaultLayout;
1498 static char const * s = "Margin Static\n"
1499 "LatexType Paragraph\n"
1502 "AlignPossible Left, Right, Center\n"
1503 "LabelType No_Label\n"
1505 istringstream ss(s);
1506 Lexer lex(textClassTags);
1508 defaultLayout = new Layout;
1509 defaultLayout->setUnknown(unknown);
1510 defaultLayout->setName(name);
1511 if (!readStyle(lex, *defaultLayout)) {
1512 // The only way this happens is because the hardcoded layout above
1516 return *defaultLayout;
1520 DocumentClassPtr getDocumentClass(
1521 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1524 DocumentClassPtr doc_class =
1525 DocumentClassPtr(new DocumentClass(baseClass));
1526 LayoutModuleList::const_iterator it = modlist.begin();
1527 LayoutModuleList::const_iterator en = modlist.end();
1528 for (; it != en; ++it) {
1529 string const modName = *it;
1530 LyXModule * lm = theModuleList[modName];
1532 docstring const msg =
1533 bformat(_("The module %1$s has been requested by\n"
1534 "this document but has not been found in the list of\n"
1535 "available modules. If you recently installed it, you\n"
1536 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1538 frontend::Alert::warning(_("Module not available"), msg);
1541 if (!lm->isAvailable() && !clone) {
1542 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1543 docstring const msg =
1544 bformat(_("The module %1$s requires a package that is not\n"
1545 "available in your LaTeX installation, or a converter that\n"
1546 "you have not installed. LaTeX output may not be possible.\n"
1547 "Missing prerequisites:\n"
1549 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1550 from_utf8(modName), prereqs);
1551 frontend::Alert::warning(_("Package not available"), msg, true);
1553 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1554 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1555 docstring const msg =
1556 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1557 frontend::Alert::warning(_("Read Error"), msg);
1564 /////////////////////////////////////////////////////////////////////////
1568 /////////////////////////////////////////////////////////////////////////
1570 DocumentClass::DocumentClass(LayoutFile const & tc)
1575 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1577 LayoutList::const_iterator it = layoutlist_.begin();
1578 LayoutList::const_iterator end = layoutlist_.end();
1579 for (; it != end; ++it)
1580 if (it->latexname() == lay)
1586 bool DocumentClass::provides(string const & p) const
1588 return provides_.find(p) != provides_.end();
1592 bool DocumentClass::hasTocLevels() const
1594 return min_toclevel_ != Layout::NOT_IN_TOC;
1598 Layout const & DocumentClass::getTOCLayout() const
1600 // we're going to look for the layout with the minimum toclevel
1601 TextClass::LayoutList::const_iterator lit = begin();
1602 TextClass::LayoutList::const_iterator const len = end();
1603 int minlevel = 1000;
1604 Layout const * lay = NULL;
1605 for (; lit != len; ++lit) {
1606 int const level = lit->toclevel;
1607 // we don't want Part or unnumbered sections
1608 if (level == Layout::NOT_IN_TOC || level < 0
1609 || level >= minlevel || lit->counter.empty())
1616 // hmm. that is very odd, so we'll do our best.
1617 return operator[](defaultLayoutName());
1621 Layout const & DocumentClass::htmlTOCLayout() const
1623 if (html_toc_section_.empty())
1624 html_toc_section_ = getTOCLayout().name();
1625 return operator[](html_toc_section_);
1629 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1630 string const & entry, string const & fallback) const
1632 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1634 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1635 if (itype == cite_formats_.end())
1636 return default_format;
1637 map<string, string>::const_iterator it = itype->second.find(entry);
1638 if (it == itype->second.end() && !fallback.empty())
1639 it = itype->second.find(fallback);
1640 if (it == itype->second.end())
1641 return default_format;
1646 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1647 string const & macro) const
1649 static string empty;
1650 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1651 if (itype == cite_macros_.end())
1653 map<string, string>::const_iterator it = itype->second.find(macro);
1654 if (it == itype->second.end())
1660 vector<string> const DocumentClass::citeCommands(
1661 CiteEngineType const & type) const
1663 vector<CitationStyle> const styles = citeStyles(type);
1664 vector<CitationStyle>::const_iterator it = styles.begin();
1665 vector<CitationStyle>::const_iterator end = styles.end();
1666 vector<string> cmds;
1667 for (; it != end; ++it) {
1668 CitationStyle const cite = *it;
1669 cmds.push_back(cite.cmd);
1675 vector<CitationStyle> const & DocumentClass::citeStyles(
1676 CiteEngineType const & type) const
1678 static vector<CitationStyle> empty;
1679 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1680 if (it == cite_styles_.end())
1686 /////////////////////////////////////////////////////////////////////////
1690 /////////////////////////////////////////////////////////////////////////
1692 ostream & operator<<(ostream & os, PageSides p)