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/FileName.h"
34 #include "support/filetools.h"
35 #include "support/gettext.h"
36 #include "support/lstrings.h"
37 #include "support/os.h"
48 using namespace lyx::support;
52 // Keep the changes documented in the Customization manual.
54 // If you change this format, then you MUST also make sure that
55 // your changes do not invalidate the hardcoded layout file in
56 // LayoutFile.cpp. Additions will never do so, but syntax changes
57 // could. See LayoutFileList::addEmptyClass() and, especially, the
58 // definition of the layoutpost string.
59 // You should also run (or ask someone who has bash to run) the
60 // development/tools/updatelayouts.sh script, to update the format of
61 // all of our layout files.
63 int const LAYOUT_FORMAT = 45; // rgh: New Tag "NoInsetLayout"
67 class LayoutNamesEqual : public unary_function<Layout, bool> {
69 LayoutNamesEqual(docstring const & name)
72 bool operator()(Layout const & c) const
74 return c.name() == name_;
81 bool layout2layout(FileName const & filename, FileName const & tempfile)
83 FileName const script = libFileSearch("scripts", "layout2layout.py");
85 LYXERR0("Could not find layout conversion "
86 "script layout2layout.py.");
90 ostringstream command;
91 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
92 << ' ' << quoteName(filename.toFilesystemEncoding())
93 << ' ' << quoteName(tempfile.toFilesystemEncoding());
94 string const command_str = command.str();
96 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
98 cmd_ret const ret = runCommand(command_str);
100 LYXERR0("Could not run layout conversion script layout2layout.py.");
107 string translateReadType(TextClass::ReadType rt)
110 case TextClass::BASECLASS:
112 case TextClass::MERGE:
114 case TextClass::MODULE:
115 return "module file";
116 case TextClass::VALIDATION:
126 // This string should not be translated here,
127 // because it is a layout identifier.
128 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
131 InsetLayout DocumentClass::plain_insetlayout_;
134 /////////////////////////////////////////////////////////////////////////
138 /////////////////////////////////////////////////////////////////////////
140 TextClass::TextClass()
143 outputFormat_ = "latex";
148 pagestyle_ = "default";
149 defaultfont_ = sane_font;
150 opt_enginetype_ = "authoryear|numerical";
151 opt_fontsize_ = "10|11|12";
152 opt_pagestyle_ = "empty|plain|headings|fancy";
153 cite_full_author_list_ = true;
154 titletype_ = TITLE_COMMAND_AFTER;
155 titlename_ = "maketitle";
157 _("Plain Layout"); // a hack to make this translatable
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
210 TC_ADDTOHTMLPREAMBLE,
226 LexerKeyword textClassTags[] = {
227 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
228 { "addtohtmlstyles", TC_ADDTOHTMLSTYLES },
229 { "addtopreamble", TC_ADDTOPREAMBLE },
230 { "citeengine", TC_CITEENGINE },
231 { "citeenginetype", TC_CITEENGINETYPE },
232 { "citeformat", TC_CITEFORMAT },
233 { "classoptions", TC_CLASSOPTIONS },
234 { "columns", TC_COLUMNS },
235 { "counter", TC_COUNTER },
236 { "defaultbiblio", TC_DEFAULTBIBLIO },
237 { "defaultfont", TC_DEFAULTFONT },
238 { "defaultmodule", TC_DEFAULTMODULE },
239 { "defaultstyle", TC_DEFAULTSTYLE },
240 { "excludesmodule", TC_EXCLUDESMODULE },
241 { "float", TC_FLOAT },
242 { "format", TC_FORMAT },
243 { "fullauthorlist", TC_FULLAUTHORLIST },
244 { "htmlpreamble", TC_HTMLPREAMBLE },
245 { "htmlstyles", TC_HTMLSTYLES },
246 { "htmltocsection", TC_HTMLTOCSECTION },
247 { "ifcounter", TC_IFCOUNTER },
248 { "ifstyle", TC_IFSTYLE },
249 { "input", TC_INPUT },
250 { "insetlayout", TC_INSETLAYOUT },
251 { "leftmargin", TC_LEFTMARGIN },
252 { "nocounter", TC_NOCOUNTER },
253 { "nofloat", TC_NOFLOAT },
254 { "noinsetlayout", TC_NOINSETLAYOUT },
255 { "nostyle", TC_NOSTYLE },
256 { "outputformat", TC_OUTPUTFORMAT },
257 { "outputtype", TC_OUTPUTTYPE },
258 { "pagestyle", TC_PAGESTYLE },
259 { "preamble", TC_PREAMBLE },
260 { "provides", TC_PROVIDES },
261 { "providesmodule", TC_PROVIDESMODULE },
262 { "requires", TC_REQUIRES },
263 { "rightmargin", TC_RIGHTMARGIN },
264 { "secnumdepth", TC_SECNUMDEPTH },
265 { "sides", TC_SIDES },
266 { "style", TC_STYLE },
267 { "titlelatexname", TC_TITLELATEXNAME },
268 { "titlelatextype", TC_TITLELATEXTYPE },
269 { "tocdepth", TC_TOCDEPTH }
275 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
277 LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
278 FileName const tempfile = FileName::tempName("convert_layout");
279 bool success = layout2layout(filename, tempfile);
281 success = readWithoutConv(tempfile, rt) == OK;
282 tempfile.removeFile();
287 std::string TextClass::convert(std::string const & str)
289 FileName const fn = FileName::tempName("locallayout");
290 ofstream os(fn.toFilesystemEncoding().c_str());
293 FileName const tempfile = FileName::tempName("convert_locallayout");
294 bool success = layout2layout(fn, tempfile);
297 ifstream is(tempfile.toFilesystemEncoding().c_str());
305 tempfile.removeFile();
310 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
312 if (!filename.isReadableFile()) {
313 lyxerr << "Cannot read layout file `" << filename << "'."
318 LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
319 to_utf8(makeDisplayPath(filename.absFileName())));
321 // Define the plain layout used in table cells, ert, etc. Note that
322 // we do this before loading any layout file, so that classes can
323 // override features of this layout if they should choose to do so.
324 if (rt == BASECLASS && !hasLayout(plain_layout_))
325 layoutlist_.push_back(createBasicLayout(plain_layout_));
327 Lexer lexrc(textClassTags);
328 lexrc.setFile(filename);
329 ReturnValues retval = read(lexrc, rt);
331 LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
332 to_utf8(makeDisplayPath(filename.absFileName())));
338 bool TextClass::read(FileName const & filename, ReadType rt)
340 ReturnValues const retval = readWithoutConv(filename, rt);
341 if (retval != FORMAT_MISMATCH)
344 bool const worx = convertLayoutFormat(filename, rt);
346 LYXERR0 ("Unable to convert " << filename <<
347 " to format " << LAYOUT_FORMAT);
352 TextClass::ReturnValues TextClass::validate(std::string const & str)
355 return tc.read(str, VALIDATION);
359 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
361 Lexer lexrc(textClassTags);
362 istringstream is(str);
364 ReturnValues retval = read(lexrc, rt);
366 if (retval != FORMAT_MISMATCH)
369 // write the layout string to a temporary file
370 FileName const tempfile = FileName::tempName("TextClass_read");
371 ofstream os(tempfile.toFilesystemEncoding().c_str());
373 LYXERR0("Unable to create temporary file");
379 // now try to convert it
380 bool const worx = convertLayoutFormat(tempfile, rt);
382 LYXERR0("Unable to convert internal layout information to format "
386 tempfile.removeFile();
391 // Reads a textclass structure from file.
392 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
397 // Format of files before the 'Format' tag was introduced
402 while (lexrc.isOK() && !error) {
403 int le = lexrc.lex();
406 case Lexer::LEX_FEOF:
409 case Lexer::LEX_UNDEF:
410 lexrc.printError("Unknown TextClass tag `$$Token'");
418 // used below to track whether we are in an IfStyle or IfCounter tag.
419 bool ifstyle = false;
420 bool ifcounter = false;
422 switch (static_cast<TextClassTags>(le)) {
426 format = lexrc.getInteger();
429 case TC_OUTPUTFORMAT:
431 outputFormat_ = lexrc.getString();
435 readOutputType(lexrc);
436 switch(outputType_) {
438 outputFormat_ = "latex";
441 outputFormat_ = "docbook";
444 outputFormat_ = "literate";
449 case TC_INPUT: // Include file
451 string const inc = lexrc.getString();
452 FileName tmp = libFileSearch("layouts", inc,
456 lexrc.printError("Could not find input file: " + inc);
458 } else if (!read(tmp, MERGE)) {
459 lexrc.printError("Error reading input file: " + tmp.absFileName());
465 case TC_DEFAULTSTYLE:
467 docstring const name = from_utf8(subst(lexrc.getString(),
469 defaultlayout_ = name;
478 lexrc.printError("No name given for style: `$$Token'.");
482 docstring const name = from_utf8(subst(lexrc.getString(),
485 string s = "Could not read name for style: `$$Token' "
486 + lexrc.getString() + " is probably not valid UTF-8!";
489 // Since we couldn't read the name, we just scan the rest
490 // of the style and discard it.
491 error = !readStyle(lexrc, lay);
492 } else if (hasLayout(name)) {
493 Layout & lay = operator[](name);
494 error = !readStyle(lexrc, lay);
495 } else if (!ifstyle) {
497 layout.setName(name);
498 error = !readStyle(lexrc, layout);
500 layoutlist_.push_back(layout);
502 if (defaultlayout_.empty()) {
503 // We do not have a default layout yet, so we choose
504 // the first layout we encounter.
505 defaultlayout_ = name;
509 // this was an ifstyle where we didn't have the style
510 // scan the rest and discard it
512 readStyle(lexrc, lay);
522 docstring const style = from_utf8(subst(lexrc.getString(),
524 if (!deleteLayout(style))
525 lyxerr << "Cannot delete style `"
526 << to_utf8(style) << '\'' << endl;
530 case TC_NOINSETLAYOUT:
532 docstring const style = from_utf8(subst(lexrc.getString(),
534 if (!deleteInsetLayout(style))
535 LYXERR0("Style `" << style << "' cannot be removed\n"
536 "because it was not found!");
542 columns_ = lexrc.getInteger();
547 switch (lexrc.getInteger()) {
548 case 1: sides_ = OneSide; break;
549 case 2: sides_ = TwoSides; break;
551 lyxerr << "Impossible number of page"
552 " sides, setting to one."
562 pagestyle_ = rtrim(lexrc.getString());
566 defaultfont_ = lyxRead(lexrc);
567 if (!defaultfont_.resolved()) {
568 lexrc.printError("Warning: defaultfont should "
569 "be fully instantiated!");
570 defaultfont_.realize(sane_font);
576 secnumdepth_ = lexrc.getInteger();
581 tocdepth_ = lexrc.getInteger();
584 // First step to support options
585 case TC_CLASSOPTIONS:
586 readClassOptions(lexrc);
590 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
593 case TC_HTMLPREAMBLE:
594 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
598 htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
601 case TC_HTMLTOCSECTION:
602 html_toc_section_ = from_utf8(trim(lexrc.getString()));
605 case TC_ADDTOPREAMBLE:
606 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
609 case TC_ADDTOHTMLPREAMBLE:
610 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
613 case TC_ADDTOHTMLSTYLES:
614 htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
619 string const feature = lexrc.getString();
621 if (lexrc.getInteger())
622 provides_.insert(feature);
624 provides_.erase(feature);
630 vector<string> const req
631 = getVectorFromString(lexrc.getString());
632 requires_.insert(req.begin(), req.end());
636 case TC_DEFAULTMODULE: {
638 string const module = lexrc.getString();
639 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
640 default_modules_.push_back(module);
644 case TC_PROVIDESMODULE: {
646 string const module = lexrc.getString();
647 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
648 provided_modules_.push_back(module);
652 case TC_EXCLUDESMODULE: {
654 string const module = lexrc.getString();
655 // modules already have their own way to exclude other modules
657 LYXERR0("ExcludesModule tag cannot be used in a module!");
660 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
661 excluded_modules_.push_back(module);
665 case TC_LEFTMARGIN: // left margin type
667 leftmargin_ = lexrc.getDocString();
670 case TC_RIGHTMARGIN: // right margin type
672 rightmargin_ = lexrc.getDocString();
675 case TC_INSETLAYOUT: {
677 lexrc.printError("No name given for InsetLayout: `$$Token'.");
681 docstring const name = subst(lexrc.getDocString(), '_', ' ');
683 string s = "Could not read name for InsetLayout: `$$Token' "
684 + lexrc.getString() + " is probably not valid UTF-8!";
687 // Since we couldn't read the name, we just scan the rest
688 // of the style and discard it.
689 il.read(lexrc, *this);
690 // Let's try to continue rather than abort.
692 } else if (hasInsetLayout(name)) {
693 InsetLayout & il = insetlayoutlist_[name];
694 error = !il.read(lexrc, *this);
698 error = !il.read(lexrc, *this);
700 insetlayoutlist_[name] = il;
706 error = !readFloat(lexrc);
710 error = !readCiteEngine(lexrc);
713 case TC_CITEENGINETYPE:
715 opt_enginetype_ = rtrim(lexrc.getString());
719 error = !readCiteFormat(lexrc);
722 case TC_DEFAULTBIBLIO:
724 cite_default_biblio_style_ = rtrim(lexrc.getString());
727 case TC_FULLAUTHORLIST:
729 cite_full_author_list_ &= lexrc.getBool();
734 docstring const cnt = lexrc.getDocString();
735 if (!counters_.remove(cnt))
736 LYXERR0("Unable to remove counter: " + to_utf8(cnt));
744 docstring const name = lexrc.getDocString();
746 string s = "Could not read name for counter: `$$Token' "
747 + lexrc.getString() + " is probably not valid UTF-8!";
748 lexrc.printError(s.c_str());
750 // Since we couldn't read the name, we just scan the rest
754 error = !counters_.read(lexrc, name, !ifcounter);
757 lexrc.printError("No name given for style: `$$Token'.");
764 case TC_TITLELATEXTYPE:
765 readTitleType(lexrc);
768 case TC_TITLELATEXNAME:
770 titlename_ = lexrc.getString();
775 string const nofloat = lexrc.getString();
776 floatlist_.erase(nofloat);
781 // Note that this is triggered the first time through the loop unless
782 // we hit a format tag.
783 if (format != LAYOUT_FORMAT)
784 return FORMAT_MISMATCH;
787 // at present, we abort if we encounter an error,
788 // so there is no point continuing.
793 return (error ? ERROR : OK);
795 if (defaultlayout_.empty()) {
796 LYXERR0("Error: Textclass '" << name_
797 << "' is missing a defaultstyle.");
801 // Try to erase "stdinsets" from the provides_ set.
803 // Provides stdinsets 1
804 // declaration simply tells us that the standard insets have been
805 // defined. (It's found in stdinsets.inc but could also be used in
806 // user-defined files.) There isn't really any such package. So we
807 // might as well go ahead and erase it.
808 // If we do not succeed, then it was not there, which means that
809 // the textclass did not provide the definitions of the standard
810 // insets. So we need to try to load them.
811 int erased = provides_.erase("stdinsets");
813 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
816 frontend::Alert::warning(_("Missing File"),
817 _("Could not find stdinsets.inc! This may lead to data loss!"));
819 } else if (!read(tmp, MERGE)) {
820 frontend::Alert::warning(_("Corrupt File"),
821 _("Could not read stdinsets.inc! This may lead to data loss!"));
826 min_toclevel_ = Layout::NOT_IN_TOC;
827 max_toclevel_ = Layout::NOT_IN_TOC;
828 const_iterator lit = begin();
829 const_iterator len = end();
830 for (; lit != len; ++lit) {
831 int const toclevel = lit->toclevel;
832 if (toclevel != Layout::NOT_IN_TOC) {
833 if (min_toclevel_ == Layout::NOT_IN_TOC)
834 min_toclevel_ = toclevel;
836 min_toclevel_ = min(min_toclevel_, toclevel);
837 max_toclevel_ = max(max_toclevel_, toclevel);
840 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
841 << ", maximum is " << max_toclevel_);
843 return (error ? ERROR : OK);
847 void TextClass::readTitleType(Lexer & lexrc)
849 LexerKeyword titleTypeTags[] = {
850 { "commandafter", TITLE_COMMAND_AFTER },
851 { "environment", TITLE_ENVIRONMENT }
854 PushPopHelper pph(lexrc, titleTypeTags);
856 int le = lexrc.lex();
858 case Lexer::LEX_UNDEF:
859 lexrc.printError("Unknown output type `$$Token'");
861 case TITLE_COMMAND_AFTER:
862 case TITLE_ENVIRONMENT:
863 titletype_ = static_cast<TitleLatexType>(le);
866 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
872 void TextClass::readOutputType(Lexer & lexrc)
874 LexerKeyword outputTypeTags[] = {
875 { "docbook", DOCBOOK },
877 { "literate", LITERATE }
880 PushPopHelper pph(lexrc, outputTypeTags);
882 int le = lexrc.lex();
884 case Lexer::LEX_UNDEF:
885 lexrc.printError("Unknown output type `$$Token'");
890 outputType_ = static_cast<OutputType>(le);
893 LYXERR0("Unhandled value " << le);
899 void TextClass::readClassOptions(Lexer & lexrc)
909 LexerKeyword classOptionsTags[] = {
911 {"fontsize", CO_FONTSIZE },
912 {"header", CO_HEADER },
913 {"other", CO_OTHER },
914 {"pagestyle", CO_PAGESTYLE }
917 lexrc.pushTable(classOptionsTags);
919 while (!getout && lexrc.isOK()) {
920 int le = lexrc.lex();
922 case Lexer::LEX_UNDEF:
923 lexrc.printError("Unknown ClassOption tag `$$Token'");
931 opt_fontsize_ = rtrim(lexrc.getString());
935 opt_pagestyle_ = rtrim(lexrc.getString());
939 if (options_.empty())
940 options_ = lexrc.getString();
942 options_ += ',' + lexrc.getString();
946 class_header_ = subst(lexrc.getString(), """, "\"");
957 bool TextClass::readCiteEngine(Lexer & lexrc)
959 int const type = readCiteEngineType(lexrc);
960 if (type & ENGINE_TYPE_AUTHORYEAR)
961 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
962 if (type & ENGINE_TYPE_NUMERICAL)
963 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
966 while (!getout && lexrc.isOK()) {
968 def = lexrc.getString();
969 def = subst(def, " ", "");
970 def = subst(def, "\t", "");
971 if (compare_ascii_no_case(def, "end") == 0) {
981 cs.forceUpperCase = true;
985 size_t const n = def.size();
986 for (size_t i = 0; i != n; ++i) {
989 cs.fullAuthorList = true;
990 else if (ichar == '[' && cs.textAfter)
991 cs.textBefore = true;
992 else if (ichar == '[')
994 else if (ichar != ']')
999 if (type & ENGINE_TYPE_AUTHORYEAR)
1000 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1001 if (type & ENGINE_TYPE_NUMERICAL)
1002 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1008 int TextClass::readCiteEngineType(Lexer & lexrc) const
1010 int const ENGINE_TYPE_DEFAULT =
1011 ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL;
1012 if (!lexrc.next()) {
1013 lexrc.printError("No cite engine type given for token: `$$Token'.");
1014 return ENGINE_TYPE_DEFAULT;
1016 string const type = rtrim(lexrc.getString());
1017 if (compare_ascii_no_case(type, "authoryear") == 0)
1018 return ENGINE_TYPE_AUTHORYEAR;
1019 else if (compare_ascii_no_case(type, "numerical") == 0)
1020 return ENGINE_TYPE_NUMERICAL;
1021 else if (compare_ascii_no_case(type, "default") != 0) {
1022 string const s = "Unknown cite engine type `" + type
1023 + "' given for token: `$$Token',";
1024 lexrc.printError(s);
1026 return ENGINE_TYPE_DEFAULT;
1030 bool TextClass::readCiteFormat(Lexer & lexrc)
1032 int const type = readCiteEngineType(lexrc);
1035 while (lexrc.isOK()) {
1037 etype = lexrc.getString();
1038 if (compare_ascii_no_case(etype, "end") == 0)
1043 definition = lexrc.getString();
1044 char initchar = etype[0];
1045 if (initchar == '#')
1047 if (initchar == '!' || initchar == '_') {
1048 if (type & ENGINE_TYPE_AUTHORYEAR)
1049 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1050 if (type & ENGINE_TYPE_NUMERICAL)
1051 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1053 if (type & ENGINE_TYPE_AUTHORYEAR)
1054 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1055 if (type & ENGINE_TYPE_NUMERICAL)
1056 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1063 bool TextClass::readFloat(Lexer & lexrc)
1083 LexerKeyword floatTags[] = {
1085 { "extension", FT_EXT },
1086 { "guiname", FT_NAME },
1087 { "htmlattr", FT_HTMLATTR },
1088 { "htmlstyle", FT_HTMLSTYLE },
1089 { "htmltag", FT_HTMLTAG },
1090 { "ispredefined", FT_PREDEFINED },
1091 { "listcommand", FT_LISTCOMMAND },
1092 { "listname", FT_LISTNAME },
1093 { "numberwithin", FT_WITHIN },
1094 { "placement", FT_PLACEMENT },
1095 { "refprefix", FT_REFPREFIX },
1096 { "style", FT_STYLE },
1097 { "type", FT_TYPE },
1098 { "usesfloatpkg", FT_USESFLOAT }
1101 lexrc.pushTable(floatTags);
1115 bool usesfloat = true;
1116 bool ispredefined = false;
1118 bool getout = false;
1119 while (!getout && lexrc.isOK()) {
1120 int le = lexrc.lex();
1122 case Lexer::LEX_UNDEF:
1123 lexrc.printError("Unknown float tag `$$Token'");
1131 type = lexrc.getString();
1132 if (floatlist_.typeExist(type)) {
1133 Floating const & fl = floatlist_.getType(type);
1134 placement = fl.placement();
1136 within = fl.within();
1139 listname = fl.listName();
1140 usesfloat = fl.usesFloatPkg();
1141 ispredefined = fl.isPredefined();
1142 listcommand = fl.listCommand();
1143 refprefix = fl.refPrefix();
1148 name = lexrc.getString();
1152 placement = lexrc.getString();
1156 ext = lexrc.getString();
1160 within = lexrc.getString();
1161 if (within == "none")
1166 style = lexrc.getString();
1168 case FT_LISTCOMMAND:
1170 listcommand = lexrc.getString();
1174 refprefix = lexrc.getString();
1178 listname = lexrc.getString();
1182 usesfloat = lexrc.getBool();
1186 ispredefined = lexrc.getBool();
1190 htmlattr = lexrc.getString();
1194 htmlstyle = lexrc.getLongString("EndHTMLStyle");
1198 htmltag = lexrc.getString();
1208 // Here we have a full float if getout == true
1210 if (!usesfloat && listcommand.empty()) {
1211 // if this float uses the same auxfile as an existing one,
1212 // there is no need for it to provide a list command.
1213 FloatList::const_iterator it = floatlist_.begin();
1214 FloatList::const_iterator en = floatlist_.end();
1215 bool found_ext = false;
1216 for (; it != en; ++it) {
1217 if (it->second.ext() == ext) {
1223 LYXERR0("The layout does not provide a list command " <<
1224 "for the float `" << type << "'. LyX will " <<
1225 "not be able to produce a float list.");
1227 Floating fl(type, placement, ext, within, style, name,
1228 listname, listcommand, refprefix,
1229 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1230 floatlist_.newFloat(fl);
1231 // each float has its own counter
1232 counters_.newCounter(from_ascii(type), from_ascii(within),
1233 docstring(), docstring());
1234 // also define sub-float counters
1235 docstring const subtype = "sub-" + from_ascii(type);
1236 counters_.newCounter(subtype, from_ascii(type),
1237 "\\alph{" + subtype + "}", docstring());
1243 string const & TextClass::prerequisites(string const & sep) const
1245 if (contains(prerequisites_, ',')) {
1246 vector<string> const pres = getVectorFromString(prerequisites_);
1247 prerequisites_ = getStringFromVector(pres, sep);
1249 return prerequisites_;
1253 bool TextClass::hasLayout(docstring const & n) const
1255 docstring const name = n.empty() ? defaultLayoutName() : n;
1257 return find_if(layoutlist_.begin(), layoutlist_.end(),
1258 LayoutNamesEqual(name))
1259 != layoutlist_.end();
1263 bool TextClass::hasInsetLayout(docstring const & n) const
1267 InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1268 return it != insetlayoutlist_.end();
1272 Layout const & TextClass::operator[](docstring const & name) const
1274 LASSERT(!name.empty(), /**/);
1277 find_if(begin(), end(), LayoutNamesEqual(name));
1280 lyxerr << "We failed to find the layout '" << to_utf8(name)
1281 << "' in the layout list. You MUST investigate!"
1283 for (const_iterator cit = begin(); cit != end(); ++cit)
1284 lyxerr << " " << to_utf8(cit->name()) << endl;
1286 // we require the name to exist
1287 LASSERT(false, /**/);
1294 Layout & TextClass::operator[](docstring const & name)
1296 LASSERT(!name.empty(), /**/);
1298 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1301 LYXERR0("We failed to find the layout '" << to_utf8(name)
1302 << "' in the layout list. You MUST investigate!");
1303 for (const_iterator cit = begin(); cit != end(); ++cit)
1304 LYXERR0(" " << to_utf8(cit->name()));
1306 // we require the name to exist
1307 LASSERT(false, /**/);
1314 bool TextClass::deleteLayout(docstring const & name)
1316 if (name == defaultLayoutName() || name == plainLayoutName())
1319 LayoutList::iterator it =
1320 remove_if(layoutlist_.begin(), layoutlist_.end(),
1321 LayoutNamesEqual(name));
1323 LayoutList::iterator end = layoutlist_.end();
1324 bool const ret = (it != end);
1325 layoutlist_.erase(it, end);
1330 bool TextClass::deleteInsetLayout(docstring const & name)
1332 return insetlayoutlist_.erase(name);
1336 // Load textclass info if not loaded yet
1337 bool TextClass::load(string const & path) const
1342 // Read style-file, provided path is searched before the system ones
1343 // If path is a file, it is loaded directly.
1344 FileName layout_file(path);
1345 if (!path.empty() && !layout_file.isReadableFile())
1346 layout_file = FileName(addName(path, name_ + ".layout"));
1347 if (layout_file.empty() || !layout_file.exists())
1348 layout_file = libFileSearch("layouts", name_, "layout");
1349 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1352 lyxerr << "Error reading `"
1353 << to_utf8(makeDisplayPath(layout_file.absFileName()))
1354 << "'\n(Check `" << name_
1355 << "')\nCheck your installation and "
1356 "try Options/Reconfigure..."
1364 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1369 layoutlist_.push_back(createBasicLayout(n, true));
1374 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1376 // FIXME The fix for the InsetLayout part of 4812 would be here:
1377 // Add the InsetLayout to the document class if it is not found.
1379 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1380 while (!n.empty()) {
1381 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1382 if (cit != cen && cit->first == n)
1384 size_t i = n.find(':');
1385 if (i == string::npos)
1389 return plain_insetlayout_;
1393 docstring const & TextClass::defaultLayoutName() const
1395 return defaultlayout_;
1399 Layout const & TextClass::defaultLayout() const
1401 return operator[](defaultLayoutName());
1405 bool TextClass::isDefaultLayout(Layout const & layout) const
1407 return layout.name() == defaultLayoutName();
1411 bool TextClass::isPlainLayout(Layout const & layout) const
1413 return layout.name() == plainLayoutName();
1417 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1419 static Layout * defaultLayout = NULL;
1421 if (defaultLayout) {
1422 defaultLayout->setUnknown(unknown);
1423 defaultLayout->setName(name);
1424 return *defaultLayout;
1427 static char const * s = "Margin Static\n"
1428 "LatexType Paragraph\n"
1431 "AlignPossible Left, Right, Center\n"
1432 "LabelType No_Label\n"
1434 istringstream ss(s);
1435 Lexer lex(textClassTags);
1437 defaultLayout = new Layout;
1438 defaultLayout->setUnknown(unknown);
1439 defaultLayout->setName(name);
1440 if (!readStyle(lex, *defaultLayout)) {
1441 // The only way this happens is because the hardcoded layout above
1443 LASSERT(false, /**/);
1445 return *defaultLayout;
1449 DocumentClassPtr getDocumentClass(
1450 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1452 DocumentClassPtr doc_class =
1453 DocumentClassPtr(new DocumentClass(baseClass));
1454 LayoutModuleList::const_iterator it = modlist.begin();
1455 LayoutModuleList::const_iterator en = modlist.end();
1456 for (; it != en; ++it) {
1457 string const modName = *it;
1458 LyXModule * lm = theModuleList[modName];
1460 docstring const msg =
1461 bformat(_("The module %1$s has been requested by\n"
1462 "this document but has not been found in the list of\n"
1463 "available modules. If you recently installed it, you\n"
1464 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1465 frontend::Alert::warning(_("Module not available"), msg);
1468 if (!lm->isAvailable()) {
1469 docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1470 docstring const msg =
1471 bformat(_("The module %1$s requires a package that is not\n"
1472 "available in your LaTeX installation, or a converter that\n"
1473 "you have not installed. LaTeX output may not be possible.\n"
1474 "Missing prerequisites:\n"
1476 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1477 from_utf8(modName), prereqs);
1478 frontend::Alert::warning(_("Package not available"), msg, true);
1480 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1481 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1482 docstring const msg =
1483 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1484 frontend::Alert::warning(_("Read Error"), msg);
1491 /////////////////////////////////////////////////////////////////////////
1495 /////////////////////////////////////////////////////////////////////////
1497 DocumentClass::DocumentClass(LayoutFile const & tc)
1502 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1504 LayoutList::const_iterator it = layoutlist_.begin();
1505 LayoutList::const_iterator end = layoutlist_.end();
1506 for (; it != end; ++it)
1507 if (it->latexname() == lay)
1513 bool DocumentClass::provides(string const & p) const
1515 return provides_.find(p) != provides_.end();
1519 bool DocumentClass::hasTocLevels() const
1521 return min_toclevel_ != Layout::NOT_IN_TOC;
1525 Layout const & DocumentClass::getTOCLayout() const
1527 // we're going to look for the layout with the minimum toclevel
1528 TextClass::LayoutList::const_iterator lit = begin();
1529 TextClass::LayoutList::const_iterator const len = end();
1530 int minlevel = 1000;
1531 Layout const * lay = NULL;
1532 for (; lit != len; ++lit) {
1533 int const level = lit->toclevel;
1534 // we don't want Part
1535 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1542 // hmm. that is very odd, so we'll do our best.
1543 return operator[](defaultLayoutName());
1547 Layout const & DocumentClass::htmlTOCLayout() const
1549 if (html_toc_section_.empty())
1550 html_toc_section_ = getTOCLayout().name();
1551 return operator[](html_toc_section_);
1555 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1556 string const & entry, string const & fallback) const
1558 static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1560 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1561 if (itype == cite_formats_.end())
1562 return default_format;
1563 map<string, string>::const_iterator it = itype->second.find(entry);
1564 if (it == itype->second.end() && !fallback.empty())
1565 it = itype->second.find(fallback);
1566 if (it == itype->second.end())
1567 return default_format;
1572 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1573 string const & macro) const
1575 static string empty;
1576 map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1577 if (itype == cite_macros_.end())
1579 map<string, string>::const_iterator it = itype->second.find(macro);
1580 if (it == itype->second.end())
1586 vector<string> const DocumentClass::citeCommands(
1587 CiteEngineType const & type) const
1589 vector<CitationStyle> const styles = citeStyles(type);
1590 vector<CitationStyle>::const_iterator it = styles.begin();
1591 vector<CitationStyle>::const_iterator end = styles.end();
1592 vector<string> cmds;
1593 for (; it != end; ++it) {
1594 CitationStyle const cite = *it;
1595 cmds.push_back(cite.cmd);
1601 vector<CitationStyle> const & DocumentClass::citeStyles(
1602 CiteEngineType const & type) const
1604 static vector<CitationStyle> empty;
1605 map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1606 if (it == cite_styles_.end())
1612 /////////////////////////////////////////////////////////////////////////
1616 /////////////////////////////////////////////////////////////////////////
1618 ostream & operator<<(ostream & os, PageSides p)