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 "lyxtextclass.h"
22 #include "FloatList.h"
24 #include "support/lstrings.h"
25 #include "support/lyxlib.h"
26 #include "support/filetools.h"
30 using lyx::support::LibFileSearch;
31 using lyx::support::MakeDisplayPath;
32 using lyx::support::QuoteName;
33 using lyx::support::rtrim;
34 using lyx::support::subst;
45 class LayoutNamesEqual : public std::unary_function<LyXLayout_ptr, bool> {
47 LayoutNamesEqual(string const & name)
50 bool operator()(LyXLayout_ptr const & c) const
52 return c->name() == name_;
62 bool layout2layout(string const & filename, string const & tempfile)
64 string const script = LibFileSearch("scripts", "layout2layout.py");
66 lyxerr << "Could not find layout conversion "
67 "script layout2layout.py." << endl;
71 std::ostringstream command;
72 command << "python " << QuoteName(script)
73 << ' ' << QuoteName(filename)
74 << ' ' << QuoteName(tempfile);
75 string const command_str = command.str();
77 lyxerr[Debug::TCLASS] << "Running `" << command_str << '\'' << endl;
79 lyx::support::cmd_ret const ret =
80 lyx::support::RunCommand(command_str);
82 lyxerr << "Could not run layout conversion "
83 "script layout2layout.py." << endl;
92 LyXTextClass::LyXTextClass(string const & fn, string const & cln,
93 string const & desc, bool texClassAvail )
94 : name_(fn), latexname_(cln), description_(desc),
95 floatlist_(new FloatList), ctrs_(new Counters),
96 texClassAvail_(texClassAvail)
103 pagestyle_ = "default";
104 defaultfont_ = LyXFont(LyXFont::ALL_SANE);
105 opt_fontsize_ = "10|11|12";
106 opt_pagestyle_ = "empty|plain|headings|fancy";
108 titletype_ = TITLE_COMMAND_AFTER;
109 titlename_ = "maketitle";
114 bool LyXTextClass::isTeXClassAvailable() const
116 return texClassAvail_;
120 bool LyXTextClass::do_readStyle(LyXLex & lexrc, LyXLayout & lay)
122 lyxerr[Debug::TCLASS] << "Reading style " << lay.name() << endl;
123 if (!lay.Read(lexrc, *this)) {
125 lay.resfont = lay.font;
126 lay.resfont.realize(defaultfont());
127 lay.reslabelfont = lay.labelfont;
128 lay.reslabelfont.realize(defaultfont());
129 return false; // no errors
131 lyxerr << "Error parsing style `" << lay.name() << '\'' << endl;
167 // Reads a textclass structure from file.
168 bool LyXTextClass::Read(string const & filename, bool merge)
170 if (!lyx::support::IsFileReadable(filename)) {
171 lyxerr << "Cannot read layout file `" << filename << "'."
176 keyword_item textClassTags[] = {
177 { "charstyle", TC_CHARSTYLE },
178 { "classoptions", TC_CLASSOPTIONS },
179 { "columns", TC_COLUMNS },
180 { "counter", TC_COUNTER },
181 { "defaultfont", TC_DEFAULTFONT },
182 { "defaultstyle", TC_DEFAULTSTYLE },
183 { "environment", TC_ENVIRONMENT },
184 { "float", TC_FLOAT },
185 { "format", TC_FORMAT },
186 { "input", TC_INPUT },
187 { "leftmargin", TC_LEFTMARGIN },
188 { "nofloat", TC_NOFLOAT },
189 { "nostyle", TC_NOSTYLE },
190 { "outputtype", TC_OUTPUTTYPE },
191 { "pagestyle", TC_PAGESTYLE },
192 { "preamble", TC_PREAMBLE },
193 { "providesamsmath", TC_PROVIDESAMSMATH },
194 { "providesmakeidx", TC_PROVIDESMAKEIDX },
195 { "providesnatbib", TC_PROVIDESNATBIB },
196 { "providesurl", TC_PROVIDESURL },
197 { "rightmargin", TC_RIGHTMARGIN },
198 { "secnumdepth", TC_SECNUMDEPTH },
199 { "sides", TC_SIDES },
200 { "style", TC_STYLE },
201 { "titlelatexname", TC_TITLELATEXNAME },
202 { "titlelatextype", TC_TITLELATEXTYPE },
203 { "tocdepth", TC_TOCDEPTH }
207 lyxerr[Debug::TCLASS] << "Reading textclass "
208 << MakeDisplayPath(filename)
211 lyxerr[Debug::TCLASS] << "Reading input file "
212 << MakeDisplayPath(filename)
215 LyXLex lexrc(textClassTags,
216 sizeof(textClassTags) / sizeof(textClassTags[0]));
218 lexrc.setFile(filename);
219 bool error = !lexrc.isOK();
221 // Format of files before the 'Format' tag was introduced
225 while (lexrc.isOK() && !error) {
226 int le = lexrc.lex();
229 case LyXLex::LEX_FEOF:
232 case LyXLex::LEX_UNDEF:
233 lexrc.printError("Unknown TextClass tag `$$Token'");
241 switch (static_cast<TextClassTags>(le)) {
245 format = lexrc.getInteger();
248 case TC_OUTPUTTYPE: // output type definition
249 readOutputType(lexrc);
252 case TC_INPUT: // Include file
254 string tmp = LibFileSearch("layouts",
258 if (Read(tmp, true)) {
259 lexrc.printError("Error reading input"
266 case TC_DEFAULTSTYLE:
268 string const name = subst(lexrc.getString(),
270 defaultlayout_ = name;
277 string const name = subst(lexrc.getString(),
279 if (hasLayout(name)) {
280 LyXLayout * lay = operator[](name).get();
281 error = do_readStyle(lexrc, *lay);
285 if (le == TC_ENVIRONMENT)
286 lay.is_environment = true;
287 if (!(error = do_readStyle(lexrc, lay)))
288 layoutlist_.push_back(
289 boost::shared_ptr<LyXLayout>(new LyXLayout(lay))
292 if (defaultlayout_.empty()) {
293 // We do not have a default
294 // layout yet, so we choose
295 // the first layout we
297 defaultlayout_ = name;
302 lexrc.printError("No name given for style: `$$Token'.");
309 string const style = subst(lexrc.getString(),
311 if (!delete_layout(style))
312 lyxerr << "Cannot delete style `"
313 << style << '\'' << endl;
314 // lexrc.printError("Cannot delete style"
321 columns_ = lexrc.getInteger();
326 switch (lexrc.getInteger()) {
327 case 1: sides_ = OneSide; break;
328 case 2: sides_ = TwoSides; break;
330 lyxerr << "Impossible number of page"
331 " sides, setting to one."
341 pagestyle_ = rtrim(lexrc.getString());
345 defaultfont_.lyxRead(lexrc);
346 if (!defaultfont_.resolved()) {
347 lexrc.printError("Warning: defaultfont should "
348 "be fully instantiated!");
349 defaultfont_.realize(LyXFont(LyXFont::ALL_SANE));
355 secnumdepth_ = lexrc.getInteger();
360 tocdepth_ = lexrc.getInteger();
363 // First step to support options
364 case TC_CLASSOPTIONS:
365 readClassOptions(lexrc);
369 preamble_ = lexrc.getLongString("EndPreamble");
372 case TC_PROVIDESAMSMATH:
373 if (lexrc.next() && lexrc.getInteger())
374 provides_ |= amsmath;
377 case TC_PROVIDESNATBIB:
378 if (lexrc.next() && lexrc.getInteger())
382 case TC_PROVIDESMAKEIDX:
383 if (lexrc.next() && lexrc.getInteger())
384 provides_ |= makeidx;
388 if (lexrc.next() && lexrc.getInteger())
392 case TC_LEFTMARGIN: // left margin type
394 leftmargin_ = lexrc.getString();
397 case TC_RIGHTMARGIN: // right margin type
399 rightmargin_ = lexrc.getString();
403 string const name = subst(lexrc.getString(), '_', ' ');
404 readCharStyle(lexrc, name);
413 case TC_TITLELATEXTYPE:
414 readTitleType(lexrc);
416 case TC_TITLELATEXNAME:
418 titlename_ = lexrc.getString();
422 string const nofloat = lexrc.getString();
423 floatlist_->erase(nofloat);
427 if (format != FORMAT)
431 if (format != FORMAT) {
432 lyxerr[Debug::TCLASS] << "Converting layout file from format "
433 << format << " to " << FORMAT << endl;
434 string const tempfile = lyx::support::tempName();
435 error = !layout2layout(filename, tempfile);
437 error = Read(tempfile, merge);
438 lyx::support::unlink(tempfile);
442 if (!merge) { // we are at top level here.
443 lyxerr[Debug::TCLASS] << "Finished reading textclass "
444 << MakeDisplayPath(filename)
446 if (defaultlayout_.empty()) {
447 lyxerr << "Error: Textclass '" << name_
448 << "' is missing a defaultstyle." << endl;
452 min_toclevel_ = LyXLayout::NOT_IN_TOC;
453 max_toclevel_ = LyXLayout::NOT_IN_TOC;
454 const_iterator cit = begin();
455 const_iterator the_end = end();
456 for ( ; cit != the_end ; ++cit) {
457 int const toclevel = (*cit)->toclevel;
458 if (toclevel != LyXLayout::NOT_IN_TOC) {
459 if (min_toclevel_ == LyXLayout::NOT_IN_TOC)
460 min_toclevel_ = toclevel;
462 min_toclevel_ = std::min(min_toclevel_,
464 max_toclevel_ = std::max(max_toclevel_,
468 lyxerr[Debug::TCLASS]
469 << "Minimum TocLevel is " << min_toclevel_
470 << ", maximum is " << max_toclevel_ <<endl;
473 lyxerr[Debug::TCLASS] << "Finished reading input file "
474 << MakeDisplayPath(filename)
481 void LyXTextClass::readTitleType(LyXLex & lexrc)
483 keyword_item titleTypeTags[] = {
484 { "commandafter", TITLE_COMMAND_AFTER },
485 { "environment", TITLE_ENVIRONMENT }
488 pushpophelper pph(lexrc, titleTypeTags, TITLE_ENVIRONMENT);
490 int le = lexrc.lex();
492 case LyXLex::LEX_UNDEF:
493 lexrc.printError("Unknown output type `$$Token'");
495 case TITLE_COMMAND_AFTER:
496 case TITLE_ENVIRONMENT:
497 titletype_ = static_cast<LYX_TITLE_LATEX_TYPES>(le);
500 lyxerr << "Unhandled value " << le
501 << " in LyXTextClass::readTitleType." << endl;
508 void LyXTextClass::readOutputType(LyXLex & lexrc)
510 keyword_item outputTypeTags[] = {
511 { "docbook", DOCBOOK },
513 { "linuxdoc", LINUXDOC },
514 { "literate", LITERATE }
517 pushpophelper pph(lexrc, outputTypeTags, LITERATE);
519 int le = lexrc.lex();
521 case LyXLex::LEX_UNDEF:
522 lexrc.printError("Unknown output type `$$Token'");
528 outputType_ = static_cast<OutputType>(le);
531 lyxerr << "Unhandled value " << le
532 << " in LyXTextClass::readOutputType." << endl;
539 enum ClassOptionsTags {
548 void LyXTextClass::readClassOptions(LyXLex & lexrc)
550 keyword_item classOptionsTags[] = {
552 {"fontsize", CO_FONTSIZE },
553 {"header", CO_HEADER },
554 {"other", CO_OTHER },
555 {"pagestyle", CO_PAGESTYLE }
558 lexrc.pushTable(classOptionsTags, CO_END);
560 while (!getout && lexrc.isOK()) {
561 int le = lexrc.lex();
563 case LyXLex::LEX_UNDEF:
564 lexrc.printError("Unknown ClassOption tag `$$Token'");
568 switch (static_cast<ClassOptionsTags>(le)) {
571 opt_fontsize_ = rtrim(lexrc.getString());
575 opt_pagestyle_ = rtrim(lexrc.getString());
579 options_ = lexrc.getString();
583 class_header_ = subst(lexrc.getString(), """, "\"");
604 void LyXTextClass::readCharStyle(LyXLex & lexrc, string const & name)
606 keyword_item elementTags[] = {
609 { "labelfont", CS_LABELFONT },
610 { "latexname", CS_LATEXNAME },
611 { "latexparam", CS_LATEXPARAM },
612 { "latextype", CS_LATEXTYPE },
613 { "preamble", CS_PREAMBLE}
616 lexrc.pushTable(elementTags, CS_END);
621 LyXFont font(LyXFont::ALL_INHERIT);
622 LyXFont labelfont(LyXFont::ALL_INHERIT);
626 while (!getout && lexrc.isOK()) {
627 int le = lexrc.lex();
629 case LyXLex::LEX_UNDEF:
630 lexrc.printError("Unknown ClassOption tag `$$Token'");
634 switch (static_cast<CharStyleTags>(le)) {
637 latextype = lexrc.getString();
641 latexname = lexrc.getString();
645 latexparam = subst(lexrc.getString(), """, "\"");
648 labelfont.lyxRead(lexrc);
655 preamble = lexrc.getLongString("EndPreamble");
664 // Here add element to list if getout == true
668 cs.latextype = latextype;
669 cs.latexname = latexname;
670 cs.latexparam = latexparam;
672 cs.labelfont = labelfont;
673 cs.preamble = preamble;
674 charstyles().push_back(cs);
694 void LyXTextClass::readFloat(LyXLex & lexrc)
696 keyword_item floatTags[] = {
698 { "extension", FT_EXT },
699 { "guiname", FT_NAME },
700 { "latexbuiltin", FT_BUILTIN },
701 { "listname", FT_LISTNAME },
702 { "numberwithin", FT_WITHIN },
703 { "placement", FT_PLACEMENT },
704 { "style", FT_STYLE },
708 lexrc.pushTable(floatTags, FT_END);
717 bool builtin = false;
720 while (!getout && lexrc.isOK()) {
721 int le = lexrc.lex();
723 case LyXLex::LEX_UNDEF:
724 lexrc.printError("Unknown ClassOption tag `$$Token'");
728 switch (static_cast<FloatTags>(le)) {
731 type = lexrc.getString();
732 // Here we could check if this type is already defined
733 // and modify it with the rest of the vars instead.
737 name = lexrc.getString();
741 placement = lexrc.getString();
745 ext = lexrc.getString();
749 within = lexrc.getString();
750 if (within == "none")
755 style = lexrc.getString();
759 listname = lexrc.getString();
763 builtin = lexrc.getBool();
771 // Here if have a full float if getout == true
773 Floating newfloat(type, placement, ext, within,
774 style, name, listname, builtin);
775 floatlist_->newFloat(newfloat);
788 void LyXTextClass::readCounter(LyXLex & lexrc)
790 keyword_item counterTags[] = {
793 { "within", CT_WITHIN }
796 lexrc.pushTable(counterTags, CT_END);
802 while (!getout && lexrc.isOK()) {
803 int le = lexrc.lex();
805 case LyXLex::LEX_UNDEF:
806 lexrc.printError("Unknown ClassOption tag `$$Token'");
810 switch (static_cast<CounterTags>(le)) {
813 name = lexrc.getString();
817 within = lexrc.getString();
818 if (within == "none")
827 // Here if have a full counter if getout == true
829 if (within.empty()) {
830 ctrs_->newCounter(name);
832 ctrs_->newCounter(name, within);
840 LyXFont const & LyXTextClass::defaultfont() const
846 string const & LyXTextClass::leftmargin() const
852 string const & LyXTextClass::rightmargin() const
858 bool LyXTextClass::hasLayout(string const & n) const
860 string const name = (n.empty() ? defaultLayoutName() : n);
862 return find_if(layoutlist_.begin(), layoutlist_.end(),
863 LayoutNamesEqual(name))
864 != layoutlist_.end();
869 LyXLayout_ptr const & LyXTextClass::operator[](string const & name) const
871 BOOST_ASSERT(!name.empty());
873 LayoutList::const_iterator cit =
874 find_if(layoutlist_.begin(),
876 LayoutNamesEqual(name));
878 if (cit == layoutlist_.end()) {
879 lyxerr << "We failed to find the layout '" << name
880 << "' in the layout list. You MUST investigate!"
882 for (LayoutList::const_iterator it = layoutlist_.begin();
883 it != layoutlist_.end(); ++it)
884 lyxerr << " " << it->get()->name() << endl;
886 // we require the name to exist
895 bool LyXTextClass::delete_layout(string const & name)
897 if (name == defaultLayoutName())
900 LayoutList::iterator it =
901 remove_if(layoutlist_.begin(), layoutlist_.end(),
902 LayoutNamesEqual(name));
904 LayoutList::iterator end = layoutlist_.end();
905 bool const ret = (it != end);
906 layoutlist_.erase(it, end);
911 // Load textclass info if not loaded yet
912 bool LyXTextClass::load() const
918 string const real_file = LibFileSearch("layouts", name_, "layout");
919 loaded_ = const_cast<LyXTextClass*>(this)->Read(real_file) == 0;
922 lyxerr << "Error reading `"
923 << MakeDisplayPath(real_file)
924 << "'\n(Check `" << name_
925 << "')\nCheck your installation and "
926 "try Options/Reconfigure..." << endl;
933 FloatList & LyXTextClass::floats()
935 return *floatlist_.get();
939 FloatList const & LyXTextClass::floats() const
941 return *floatlist_.get();
945 Counters & LyXTextClass::counters() const
951 CharStyles::iterator LyXTextClass::charstyle(string const & s) const
953 CharStyles::iterator cs = charstyles().begin();
954 CharStyles::iterator csend = charstyles().end();
955 for (; cs != csend; ++cs) {
963 string const & LyXTextClass::defaultLayoutName() const
965 // This really should come from the actual layout... (Lgb)
966 return defaultlayout_;
970 LyXLayout_ptr const & LyXTextClass::defaultLayout() const
972 return operator[](defaultLayoutName());
976 string const & LyXTextClass::name() const
982 string const & LyXTextClass::latexname() const
984 const_cast<LyXTextClass*>(this)->load();
989 string const & LyXTextClass::description() const
995 string const & LyXTextClass::opt_fontsize() const
997 return opt_fontsize_;
1001 string const & LyXTextClass::opt_pagestyle() const
1003 return opt_pagestyle_;
1007 string const & LyXTextClass::options() const
1013 string const & LyXTextClass::class_header() const
1015 return class_header_;
1019 string const & LyXTextClass::pagestyle() const
1025 string const & LyXTextClass::preamble() const
1031 LyXTextClass::PageSides LyXTextClass::sides() const
1037 int LyXTextClass::secnumdepth() const
1039 return secnumdepth_;
1043 int LyXTextClass::tocdepth() const
1049 OutputType LyXTextClass::outputType() const
1055 bool LyXTextClass::provides(LyXTextClass::Provides p) const
1057 return provides_ & p;
1061 unsigned int LyXTextClass::columns() const
1067 LYX_TITLE_LATEX_TYPES LyXTextClass::titletype() const
1073 string const & LyXTextClass::titlename() const
1079 int LyXTextClass::size() const
1081 return layoutlist_.size();
1085 int LyXTextClass::min_toclevel() const
1087 return min_toclevel_;
1091 int LyXTextClass::max_toclevel() const
1093 return max_toclevel_;
1097 ostream & operator<<(ostream & os, LyXTextClass::PageSides p)
1100 case LyXTextClass::OneSide:
1103 case LyXTextClass::TwoSides: