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 = 49; //gb: change default of ResetsFont
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
212 TC_ADDTOHTMLPREAMBLE,
228 LexerKeyword textClassTags[] = {
229 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
230 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
231 { "addtopreamble", TC_ADDTOPREAMBLE },
232 { "citeengine", TC_CITEENGINE },
233 { "citeenginetype", TC_CITEENGINETYPE },
234 { "citeformat", TC_CITEFORMAT },
235 { "classoptions", TC_CLASSOPTIONS },
236 { "columns", TC_COLUMNS },
237 { "counter", TC_COUNTER },
238 { "defaultbiblio", TC_DEFAULTBIBLIO },
239 { "defaultfont", TC_DEFAULTFONT },
240 { "defaultmodule", TC_DEFAULTMODULE },
241 { "defaultstyle", TC_DEFAULTSTYLE },
242 { "excludesmodule", TC_EXCLUDESMODULE },
243 { "float", TC_FLOAT },
244 { "format", TC_FORMAT },
245 { "fullauthorlist", TC_FULLAUTHORLIST },
246 { "htmlpreamble", TC_HTMLPREAMBLE },
247 { "htmlstyles", TC_HTMLSTYLES },
248 { "htmltocsection", TC_HTMLTOCSECTION },
249 { "ifcounter", TC_IFCOUNTER },
250 { "ifstyle", TC_IFSTYLE },
251 { "input", TC_INPUT },
252 { "insetlayout", TC_INSETLAYOUT },
253 { "leftmargin", TC_LEFTMARGIN },
254 { "nocounter", TC_NOCOUNTER },
255 { "nofloat", TC_NOFLOAT },
256 { "noinsetlayout", TC_NOINSETLAYOUT },
257 { "nostyle", TC_NOSTYLE },
258 { "outputformat", TC_OUTPUTFORMAT },
259 { "outputtype", TC_OUTPUTTYPE },
260 { "packageoptions", TC_PKGOPTS },
261 { "pagestyle", TC_PAGESTYLE },
262 { "preamble", TC_PREAMBLE },
263 { "provides", TC_PROVIDES },
264 { "providesmodule", TC_PROVIDESMODULE },
265 { "requires", TC_REQUIRES },
266 { "rightmargin", TC_RIGHTMARGIN },
267 { "secnumdepth", TC_SECNUMDEPTH },
268 { "sides", TC_SIDES },
269 { "style", TC_STYLE },
270 { "titlelatexname", TC_TITLELATEXNAME },
271 { "titlelatextype", TC_TITLELATEXTYPE },
272 { "tocdepth", TC_TOCDEPTH }
278 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
280 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
281 FileName const tempfile = FileName::tempName("convert_layout");
282 bool success = layout2layout(filename, tempfile);
284 success = readWithoutConv(tempfile, rt) == OK;
285 tempfile.removeFile();
290 std::string TextClass::convert(std::string const & str)
292 FileName const fn = FileName::tempName("locallayout");
293 ofstream os(fn.toFilesystemEncoding().c_str());
296 FileName const tempfile = FileName::tempName("convert_locallayout");
297 bool success = layout2layout(fn, tempfile);
300 ifstream is(tempfile.toFilesystemEncoding().c_str());
308 tempfile.removeFile();
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 FileName const tempfile = FileName::tempName("TextClass_read");
374 ofstream os(tempfile.toFilesystemEncoding().c_str());
376 LYXERR0("Unable to create temporary file");
382 // now try to convert it
383 bool const worx = convertLayoutFormat(tempfile, rt);
385 LYXERR0("Unable to convert internal layout information to format "
389 tempfile.removeFile();
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)
1426 size_t i = n.find(':');
1427 if (i == string::npos)
1431 return plain_insetlayout_;
1435 docstring const & TextClass::defaultLayoutName() const
1437 return defaultlayout_;
1441 Layout const & TextClass::defaultLayout() const
1443 return operator[](defaultLayoutName());
1447 bool TextClass::isDefaultLayout(Layout const & layout) const
1449 return layout.name() == defaultLayoutName();
1453 bool TextClass::isPlainLayout(Layout const & layout) const
1455 return layout.name() == plainLayoutName();
1459 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1461 static Layout * defaultLayout = NULL;
1463 if (defaultLayout) {
1464 defaultLayout->setUnknown(unknown);
1465 defaultLayout->setName(name);
1466 return *defaultLayout;
1469 static char const * s = "Margin Static\n"
1470 "LatexType Paragraph\n"
1473 "AlignPossible Left, Right, Center\n"
1474 "LabelType No_Label\n"
1476 istringstream ss(s);
1477 Lexer lex(textClassTags);
1479 defaultLayout = new Layout;
1480 defaultLayout->setUnknown(unknown);
1481 defaultLayout->setName(name);
1482 if (!readStyle(lex, *defaultLayout)) {
1483 // The only way this happens is because the hardcoded layout above
1487 return *defaultLayout;
1491 DocumentClassPtr getDocumentClass(
1492 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1495 DocumentClassPtr doc_class =
1496 DocumentClassPtr(new DocumentClass(baseClass));
1497 LayoutModuleList::const_iterator it = modlist.begin();
1498 LayoutModuleList::const_iterator en = modlist.end();
1499 for (; it != en; ++it) {
1500 string const modName = *it;
1501 LyXModule * lm = theModuleList[modName];
1503 docstring const msg =
1504 bformat(_("The module %1$s has been requested by\n"
1505 "this document but has not been found in the list of\n"
1506 "available modules. If you recently installed it, you\n"
1507 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1509 frontend::Alert::warning(_("Module not available"), msg);
1512 if (!lm->isAvailable() && !clone) {
1513 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1514 docstring const msg =
1515 bformat(_("The module %1$s requires a package that is not\n"
1516 "available in your LaTeX installation, or a converter that\n"
1517 "you have not installed. LaTeX output may not be possible.\n"
1518 "Missing prerequisites:\n"
1520 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1521 from_utf8(modName), prereqs);
1522 frontend::Alert::warning(_("Package not available"), msg, true);
1524 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1525 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1526 docstring const msg =
1527 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1528 frontend::Alert::warning(_("Read Error"), msg);
1535 /////////////////////////////////////////////////////////////////////////
1539 /////////////////////////////////////////////////////////////////////////
1541 DocumentClass::DocumentClass(LayoutFile const & tc)
1546 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1548 LayoutList::const_iterator it = layoutlist_.begin();
1549 LayoutList::const_iterator end = layoutlist_.end();
1550 for (; it != end; ++it)
1551 if (it->latexname() == lay)
1557 bool DocumentClass::provides(string const & p) const
1559 return provides_.find(p) != provides_.end();
1563 bool DocumentClass::hasTocLevels() const
1565 return min_toclevel_ != Layout::NOT_IN_TOC;
1569 Layout const & DocumentClass::getTOCLayout() const
1571 // we're going to look for the layout with the minimum toclevel
1572 TextClass::LayoutList::const_iterator lit = begin();
1573 TextClass::LayoutList::const_iterator const len = end();
1574 int minlevel = 1000;
1575 Layout const * lay = NULL;
1576 for (; lit != len; ++lit) {
1577 int const level = lit->toclevel;
1578 // we don't want Part or unnumbered sections
1579 if (level == Layout::NOT_IN_TOC || level < 0
1580 || level >= minlevel || lit->counter.empty())
1587 // hmm. that is very odd, so we'll do our best.
1588 return operator[](defaultLayoutName());
1592 Layout const & DocumentClass::htmlTOCLayout() const
1594 if (html_toc_section_.empty())
1595 html_toc_section_ = getTOCLayout().name();
1596 return operator[](html_toc_section_);
1600 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1601 string const & entry, string const & fallback) const
1603 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1605 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1606 if (itype == cite_formats_.end())
1607 return default_format;
1608 map<string, string>::const_iterator it = itype->second.find(entry);
1609 if (it == itype->second.end() && !fallback.empty())
1610 it = itype->second.find(fallback);
1611 if (it == itype->second.end())
1612 return default_format;
1617 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1618 string const & macro) const
1620 static string empty;
1621 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1622 if (itype == cite_macros_.end())
1624 map<string, string>::const_iterator it = itype->second.find(macro);
1625 if (it == itype->second.end())
1631 vector<string> const DocumentClass::citeCommands(
1632 CiteEngineType const & type) const
1634 vector<CitationStyle> const styles = citeStyles(type);
1635 vector<CitationStyle>::const_iterator it = styles.begin();
1636 vector<CitationStyle>::const_iterator end = styles.end();
1637 vector<string> cmds;
1638 for (; it != end; ++it) {
1639 CitationStyle const cite = *it;
1640 cmds.push_back(cite.cmd);
1646 vector<CitationStyle> const & DocumentClass::citeStyles(
1647 CiteEngineType const & type) const
1649 static vector<CitationStyle> empty;
1650 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1651 if (it == cite_styles_.end())
1657 /////////////////////////////////////////////////////////////////////////
1661 /////////////////////////////////////////////////////////////////////////
1663 ostream & operator<<(ostream & os, PageSides p)