* qt_helpers.cpp:
[lyx.git] / src / TextClass.cpp
blob0a349e85720d3935faa1593a9d928c9a589bb722
1 /**
2 * \file TextClass.cpp
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
9 * \author John Levon
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
15 #include <config.h>
17 #include "TextClass.h"
19 #include "LayoutFile.h"
20 #include "Color.h"
21 #include "Counters.h"
22 #include "Floating.h"
23 #include "FloatList.h"
24 #include "Layout.h"
25 #include "Lexer.h"
26 #include "Font.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"
40 #include <algorithm>
41 #include <fstream>
42 #include <sstream>
44 using namespace std;
45 using namespace lyx::support;
47 namespace lyx {
49 namespace {
51 class LayoutNamesEqual : public unary_function<Layout, bool> {
52 public:
53 LayoutNamesEqual(docstring const & name)
54 : name_(name)
56 bool operator()(Layout const & c) const
58 return c.name() == name_;
60 private:
61 docstring name_;
64 // Keep the changes documented in the Customization manual.
65 int const FORMAT = 16;
68 bool layout2layout(FileName const & filename, FileName const & tempfile)
70 FileName const script = libFileSearch("scripts", "layout2layout.py");
71 if (script.empty()) {
72 LYXERR0("Could not find layout conversion "
73 "script layout2layout.py.");
74 return false;
77 ostringstream command;
78 command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
79 << ' ' << quoteName(filename.toFilesystemEncoding())
80 << ' ' << quoteName(tempfile.toFilesystemEncoding());
81 string const command_str = command.str();
83 LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
85 cmd_ret const ret = runCommand(command_str);
86 if (ret.first != 0) {
87 LYXERR0("Could not run layout conversion script layout2layout.py.");
88 return false;
90 return true;
94 std::string translateRT(TextClass::ReadType rt)
96 switch (rt) {
97 case TextClass::BASECLASS:
98 return "textclass";
99 case TextClass::MERGE:
100 return "input file";
101 case TextClass::MODULE:
102 return "module file";
103 case TextClass::VALIDATION:
104 return "validation";
106 // shutup warning
107 return string();
110 } // namespace anon
113 // This string should not be translated here,
114 // because it is a layout identifier.
115 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
118 InsetLayout DocumentClass::plain_insetlayout_;
121 /////////////////////////////////////////////////////////////////////////
123 // TextClass
125 /////////////////////////////////////////////////////////////////////////
127 TextClass::TextClass()
129 outputType_ = LATEX;
130 outputFormat_ = "latex";
131 columns_ = 1;
132 sides_ = OneSide;
133 secnumdepth_ = 3;
134 tocdepth_ = 3;
135 pagestyle_ = "default";
136 defaultfont_ = sane_font;
137 opt_fontsize_ = "10|11|12";
138 opt_pagestyle_ = "empty|plain|headings|fancy";
139 titletype_ = TITLE_COMMAND_AFTER;
140 titlename_ = "maketitle";
141 loaded_ = false;
142 _("Plain Layout"); // a hack to make this translatable
146 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
148 LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
149 if (!lay.read(lexrc, *this)) {
150 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
151 return false;
153 // Resolve fonts
154 lay.resfont = lay.font;
155 lay.resfont.realize(defaultfont_);
156 lay.reslabelfont = lay.labelfont;
157 lay.reslabelfont.realize(defaultfont_);
158 return true; // no errors
162 enum TextClassTags {
163 TC_OUTPUTTYPE = 1,
164 TC_OUTPUTFORMAT,
165 TC_INPUT,
166 TC_STYLE,
167 TC_DEFAULTSTYLE,
168 TC_INSETLAYOUT,
169 TC_NOSTYLE,
170 TC_COLUMNS,
171 TC_SIDES,
172 TC_PAGESTYLE,
173 TC_DEFAULTFONT,
174 TC_SECNUMDEPTH,
175 TC_TOCDEPTH,
176 TC_CLASSOPTIONS,
177 TC_PREAMBLE,
178 TC_HTMLPREAMBLE,
179 TC_PROVIDES,
180 TC_REQUIRES,
181 TC_LEFTMARGIN,
182 TC_RIGHTMARGIN,
183 TC_FLOAT,
184 TC_COUNTER,
185 TC_NOFLOAT,
186 TC_TITLELATEXNAME,
187 TC_TITLELATEXTYPE,
188 TC_FORMAT,
189 TC_ADDTOPREAMBLE,
190 TC_ADDTOHTMLPREAMBLE,
191 TC_DEFAULTMODULE,
192 TC_PROVIDESMODULE,
193 TC_EXCLUDESMODULE
197 namespace {
199 LexerKeyword textClassTags[] = {
200 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
201 { "addtopreamble", TC_ADDTOPREAMBLE },
202 { "classoptions", TC_CLASSOPTIONS },
203 { "columns", TC_COLUMNS },
204 { "counter", TC_COUNTER },
205 { "defaultfont", TC_DEFAULTFONT },
206 { "defaultmodule", TC_DEFAULTMODULE },
207 { "defaultstyle", TC_DEFAULTSTYLE },
208 { "excludesmodule", TC_EXCLUDESMODULE },
209 { "float", TC_FLOAT },
210 { "format", TC_FORMAT },
211 { "htmlpreamble", TC_HTMLPREAMBLE },
212 { "input", TC_INPUT },
213 { "insetlayout", TC_INSETLAYOUT },
214 { "leftmargin", TC_LEFTMARGIN },
215 { "nofloat", TC_NOFLOAT },
216 { "nostyle", TC_NOSTYLE },
217 { "outputformat", TC_OUTPUTFORMAT },
218 { "outputtype", TC_OUTPUTTYPE },
219 { "pagestyle", TC_PAGESTYLE },
220 { "preamble", TC_PREAMBLE },
221 { "provides", TC_PROVIDES },
222 { "providesmodule", TC_PROVIDESMODULE },
223 { "requires", TC_REQUIRES },
224 { "rightmargin", TC_RIGHTMARGIN },
225 { "secnumdepth", TC_SECNUMDEPTH },
226 { "sides", TC_SIDES },
227 { "style", TC_STYLE },
228 { "titlelatexname", TC_TITLELATEXNAME },
229 { "titlelatextype", TC_TITLELATEXTYPE },
230 { "tocdepth", TC_TOCDEPTH }
233 } //namespace anon
236 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
238 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
239 FileName const tempfile = FileName::tempName("convert_layout");
240 bool success = layout2layout(filename, tempfile);
241 if (success)
242 success = readWithoutConv(tempfile, rt) == OK;
243 tempfile.removeFile();
244 return success;
248 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
250 if (!filename.isReadableFile()) {
251 lyxerr << "Cannot read layout file `" << filename << "'."
252 << endl;
253 return ERROR;
256 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
257 to_utf8(makeDisplayPath(filename.absFilename())));
259 // Define the plain layout used in table cells, ert, etc. Note that
260 // we do this before loading any layout file, so that classes can
261 // override features of this layout if they should choose to do so.
262 if (rt == BASECLASS && !hasLayout(plain_layout_))
263 layoutlist_.push_back(createBasicLayout(plain_layout_));
265 Lexer lexrc(textClassTags);
266 lexrc.setFile(filename);
267 ReturnValues retval = read(lexrc, rt);
269 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
270 to_utf8(makeDisplayPath(filename.absFilename())));
272 return retval;
276 bool TextClass::read(FileName const & filename, ReadType rt)
278 ReturnValues const retval = readWithoutConv(filename, rt);
279 if (retval != FORMAT_MISMATCH)
280 return retval == OK;
282 bool const worx = convertLayoutFormat(filename, rt);
283 if (!worx) {
284 LYXERR0 ("Unable to convert " << filename <<
285 " to format " << FORMAT);
286 return false;
288 return true;
292 bool TextClass::validate(std::string const & str)
294 TextClass tc;
295 return tc.read(str, VALIDATION);
299 bool TextClass::read(std::string const & str, ReadType rt)
301 Lexer lexrc(textClassTags);
302 istringstream is(str);
303 lexrc.setStream(is);
304 ReturnValues retval = read(lexrc, rt);
306 if (retval != FORMAT_MISMATCH)
307 return retval == OK;
309 // write the layout string to a temporary file
310 FileName const tempfile = FileName::tempName("TextClass_read");
311 ofstream os(tempfile.toFilesystemEncoding().c_str());
312 if (!os) {
313 LYXERR0("Unable to create temporary file");
314 return false;
316 os << str;
317 os.close();
319 // now try to convert it
320 bool const worx = convertLayoutFormat(tempfile, rt);
321 if (!worx) {
322 LYXERR0("Unable to convert internal layout information to format "
323 << FORMAT);
325 tempfile.removeFile();
326 return worx;
330 // Reads a textclass structure from file.
331 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
333 bool error = !lexrc.isOK();
335 // Format of files before the 'Format' tag was introduced
336 int format = 1;
338 // parsing
339 while (lexrc.isOK() && !error) {
340 int le = lexrc.lex();
342 switch (le) {
343 case Lexer::LEX_FEOF:
344 continue;
346 case Lexer::LEX_UNDEF:
347 lexrc.printError("Unknown TextClass tag `$$Token'");
348 error = true;
349 continue;
351 default:
352 break;
355 switch (static_cast<TextClassTags>(le)) {
357 case TC_FORMAT:
358 if (lexrc.next())
359 format = lexrc.getInteger();
360 break;
362 case TC_OUTPUTFORMAT:
363 if (lexrc.next())
364 outputFormat_ = lexrc.getString();
365 break;
367 case TC_OUTPUTTYPE:
368 readOutputType(lexrc);
369 switch(outputType_) {
370 case LATEX:
371 outputFormat_ = "latex";
372 break;
373 case DOCBOOK:
374 outputFormat_ = "docbook";
375 break;
376 case LITERATE:
377 outputFormat_ = "literate";
378 break;
380 break;
382 case TC_INPUT: // Include file
383 if (lexrc.next()) {
384 string const inc = lexrc.getString();
385 FileName tmp = libFileSearch("layouts", inc,
386 "layout");
388 if (tmp.empty()) {
389 lexrc.printError("Could not find input file: " + inc);
390 error = true;
391 } else if (!read(tmp, MERGE)) {
392 lexrc.printError("Error reading input"
393 "file: " + tmp.absFilename());
394 error = true;
397 break;
399 case TC_DEFAULTSTYLE:
400 if (lexrc.next()) {
401 docstring const name = from_utf8(subst(lexrc.getString(),
402 '_', ' '));
403 defaultlayout_ = name;
405 break;
407 case TC_STYLE: {
408 if (!lexrc.next()) {
409 lexrc.printError("No name given for style: `$$Token'.");
410 error = true;
411 break;
413 docstring const name = from_utf8(subst(lexrc.getString(),
414 '_', ' '));
415 if (name.empty()) {
416 string s = "Could not read name for style: `$$Token' "
417 + lexrc.getString() + " is probably not valid UTF-8!";
418 lexrc.printError(s.c_str());
419 Layout lay;
420 // Since we couldn't read the name, we just scan the rest
421 // of the style and discard it.
422 error = !readStyle(lexrc, lay);
423 } else if (hasLayout(name)) {
424 Layout & lay = operator[](name);
425 error = !readStyle(lexrc, lay);
426 } else {
427 Layout layout;
428 layout.setName(name);
429 error = !readStyle(lexrc, layout);
430 if (!error)
431 layoutlist_.push_back(layout);
433 if (defaultlayout_.empty()) {
434 // We do not have a default layout yet, so we choose
435 // the first layout we encounter.
436 defaultlayout_ = name;
439 break;
442 case TC_NOSTYLE:
443 if (lexrc.next()) {
444 docstring const style = from_utf8(subst(lexrc.getString(),
445 '_', ' '));
446 if (!deleteLayout(style))
447 lyxerr << "Cannot delete style `"
448 << to_utf8(style) << '\'' << endl;
450 break;
452 case TC_COLUMNS:
453 if (lexrc.next())
454 columns_ = lexrc.getInteger();
455 break;
457 case TC_SIDES:
458 if (lexrc.next()) {
459 switch (lexrc.getInteger()) {
460 case 1: sides_ = OneSide; break;
461 case 2: sides_ = TwoSides; break;
462 default:
463 lyxerr << "Impossible number of page"
464 " sides, setting to one."
465 << endl;
466 sides_ = OneSide;
467 break;
470 break;
472 case TC_PAGESTYLE:
473 lexrc.next();
474 pagestyle_ = rtrim(lexrc.getString());
475 break;
477 case TC_DEFAULTFONT:
478 defaultfont_ = lyxRead(lexrc);
479 if (!defaultfont_.resolved()) {
480 lexrc.printError("Warning: defaultfont should "
481 "be fully instantiated!");
482 defaultfont_.realize(sane_font);
484 break;
486 case TC_SECNUMDEPTH:
487 lexrc.next();
488 secnumdepth_ = lexrc.getInteger();
489 break;
491 case TC_TOCDEPTH:
492 lexrc.next();
493 tocdepth_ = lexrc.getInteger();
494 break;
496 // First step to support options
497 case TC_CLASSOPTIONS:
498 readClassOptions(lexrc);
499 break;
501 case TC_PREAMBLE:
502 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
503 break;
505 case TC_HTMLPREAMBLE:
506 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
507 break;
509 case TC_ADDTOPREAMBLE:
510 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
511 break;
513 case TC_ADDTOHTMLPREAMBLE:
514 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
515 break;
517 case TC_PROVIDES: {
518 lexrc.next();
519 string const feature = lexrc.getString();
520 lexrc.next();
521 if (lexrc.getInteger())
522 provides_.insert(feature);
523 else
524 provides_.erase(feature);
525 break;
528 case TC_REQUIRES: {
529 lexrc.eatLine();
530 vector<string> const req
531 = getVectorFromString(lexrc.getString());
532 requires_.insert(req.begin(), req.end());
533 break;
536 case TC_DEFAULTMODULE: {
537 lexrc.next();
538 string const module = lexrc.getString();
539 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
540 default_modules_.push_back(module);
541 break;
544 case TC_PROVIDESMODULE: {
545 lexrc.next();
546 string const module = lexrc.getString();
547 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
548 provided_modules_.push_back(module);
549 break;
552 case TC_EXCLUDESMODULE: {
553 lexrc.next();
554 string const module = lexrc.getString();
555 // modules already have their own way to exclude other modules
556 if (rt == MODULE) {
557 LYXERR0("ExcludesModule tag cannot be used in a module!");
558 break;
560 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
561 excluded_modules_.push_back(module);
562 break;
565 case TC_LEFTMARGIN: // left margin type
566 if (lexrc.next())
567 leftmargin_ = lexrc.getDocString();
568 break;
570 case TC_RIGHTMARGIN: // right margin type
571 if (lexrc.next())
572 rightmargin_ = lexrc.getDocString();
573 break;
575 case TC_INSETLAYOUT: {
576 if (!lexrc.next()) {
577 lexrc.printError("No name given for InsetLayout: `$$Token'.");
578 error = true;
579 break;
581 docstring const name = subst(lexrc.getDocString(), '_', ' ');
582 if (name.empty()) {
583 string s = "Could not read name for InsetLayout: `$$Token' "
584 + lexrc.getString() + " is probably not valid UTF-8!";
585 lexrc.printError(s.c_str());
586 InsetLayout il;
587 // Since we couldn't read the name, we just scan the rest
588 // of the style and discard it.
589 il.read(lexrc, *this);
590 error = true;
591 } else if (hasInsetLayout(name)) {
592 InsetLayout & il = insetlayoutlist_[name];
593 error = !il.read(lexrc, *this);
594 } else {
595 InsetLayout il;
596 il.setName(name);
597 error = !il.read(lexrc, *this);
598 if (!error)
599 insetlayoutlist_[name] = il;
601 break;
604 case TC_FLOAT:
605 readFloat(lexrc);
606 break;
608 case TC_COUNTER:
609 if (lexrc.next()) {
610 docstring const name = lexrc.getDocString();
611 if (name.empty()) {
612 string s = "Could not read name for counter: `$$Token' "
613 + lexrc.getString() + " is probably not valid UTF-8!";
614 lexrc.printError(s.c_str());
615 Counter c;
616 // Since we couldn't read the name, we just scan the rest
617 // and discard it.
618 c.read(lexrc);
619 } else
620 error = !counters_.read(lexrc, name);
622 else {
623 lexrc.printError("No name given for style: `$$Token'.");
624 error = true;
626 break;
628 case TC_TITLELATEXTYPE:
629 readTitleType(lexrc);
630 break;
632 case TC_TITLELATEXNAME:
633 if (lexrc.next())
634 titlename_ = lexrc.getString();
635 break;
637 case TC_NOFLOAT:
638 if (lexrc.next()) {
639 string const nofloat = lexrc.getString();
640 floatlist_.erase(nofloat);
642 break;
643 } // end of switch
645 //Note that this is triggered the first time through the loop unless
646 //we hit a format tag.
647 if (format != FORMAT)
648 break;
651 if (format != FORMAT)
652 return FORMAT_MISMATCH;
654 if (rt != BASECLASS)
655 return (error ? ERROR : OK);
657 if (defaultlayout_.empty()) {
658 LYXERR0("Error: Textclass '" << name_
659 << "' is missing a defaultstyle.");
660 error = true;
663 // Try to erase "stdinsets" from the provides_ set.
664 // The
665 // Provides stdinsets 1
666 // declaration simply tells us that the standard insets have been
667 // defined. (It's found in stdinsets.inc but could also be used in
668 // user-defined files.) There isn't really any such package. So we
669 // might as well go ahead and erase it.
670 // If we do not succeed, then it was not there, which means that
671 // the textclass did not provide the definitions of the standard
672 // insets. So we need to try to load them.
673 int erased = provides_.erase("stdinsets");
674 if (!erased) {
675 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
677 if (tmp.empty()) {
678 throw ExceptionMessage(WarningException, _("Missing File"),
679 _("Could not find stdinsets.inc! This may lead to data loss!"));
680 error = true;
681 } else if (!read(tmp, MERGE)) {
682 throw ExceptionMessage(WarningException, _("Corrupt File"),
683 _("Could not read stdinsets.inc! This may lead to data loss!"));
684 error = true;
688 min_toclevel_ = Layout::NOT_IN_TOC;
689 max_toclevel_ = Layout::NOT_IN_TOC;
690 const_iterator lit = begin();
691 const_iterator len = end();
692 for (; lit != len; ++lit) {
693 int const toclevel = lit->toclevel;
694 if (toclevel != Layout::NOT_IN_TOC) {
695 if (min_toclevel_ == Layout::NOT_IN_TOC)
696 min_toclevel_ = toclevel;
697 else
698 min_toclevel_ = min(min_toclevel_, toclevel);
699 max_toclevel_ = max(max_toclevel_, toclevel);
702 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
703 << ", maximum is " << max_toclevel_);
705 return (error ? ERROR : OK);
709 void TextClass::readTitleType(Lexer & lexrc)
711 LexerKeyword titleTypeTags[] = {
712 { "commandafter", TITLE_COMMAND_AFTER },
713 { "environment", TITLE_ENVIRONMENT }
716 PushPopHelper pph(lexrc, titleTypeTags);
718 int le = lexrc.lex();
719 switch (le) {
720 case Lexer::LEX_UNDEF:
721 lexrc.printError("Unknown output type `$$Token'");
722 break;
723 case TITLE_COMMAND_AFTER:
724 case TITLE_ENVIRONMENT:
725 titletype_ = static_cast<TitleLatexType>(le);
726 break;
727 default:
728 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
729 break;
734 void TextClass::readOutputType(Lexer & lexrc)
736 LexerKeyword outputTypeTags[] = {
737 { "docbook", DOCBOOK },
738 { "latex", LATEX },
739 { "literate", LITERATE }
742 PushPopHelper pph(lexrc, outputTypeTags);
744 int le = lexrc.lex();
745 switch (le) {
746 case Lexer::LEX_UNDEF:
747 lexrc.printError("Unknown output type `$$Token'");
748 return;
749 case LATEX:
750 case DOCBOOK:
751 case LITERATE:
752 outputType_ = static_cast<OutputType>(le);
753 break;
754 default:
755 LYXERR0("Unhandled value " << le);
756 break;
761 void TextClass::readClassOptions(Lexer & lexrc)
763 enum {
764 CO_FONTSIZE = 1,
765 CO_PAGESTYLE,
766 CO_OTHER,
767 CO_HEADER,
768 CO_END
771 LexerKeyword classOptionsTags[] = {
772 {"end", CO_END },
773 {"fontsize", CO_FONTSIZE },
774 {"header", CO_HEADER },
775 {"other", CO_OTHER },
776 {"pagestyle", CO_PAGESTYLE }
779 lexrc.pushTable(classOptionsTags);
780 bool getout = false;
781 while (!getout && lexrc.isOK()) {
782 int le = lexrc.lex();
783 switch (le) {
784 case Lexer::LEX_UNDEF:
785 lexrc.printError("Unknown ClassOption tag `$$Token'");
786 continue;
787 default: break;
789 switch (le) {
790 case CO_FONTSIZE:
791 lexrc.next();
792 opt_fontsize_ = rtrim(lexrc.getString());
793 break;
794 case CO_PAGESTYLE:
795 lexrc.next();
796 opt_pagestyle_ = rtrim(lexrc.getString());
797 break;
798 case CO_OTHER:
799 lexrc.next();
800 options_ = lexrc.getString();
801 break;
802 case CO_HEADER:
803 lexrc.next();
804 class_header_ = subst(lexrc.getString(), "&quot;", "\"");
805 break;
806 case CO_END:
807 getout = true;
808 break;
811 lexrc.popTable();
815 void TextClass::readFloat(Lexer & lexrc)
817 enum {
818 FT_TYPE = 1,
819 FT_NAME,
820 FT_PLACEMENT,
821 FT_EXT,
822 FT_WITHIN,
823 FT_STYLE,
824 FT_LISTNAME,
825 FT_BUILTIN,
826 FT_HTMLSTYLE,
827 FT_HTMLCLASS,
828 FT_HTMLTYPE,
829 FT_END
832 LexerKeyword floatTags[] = {
833 { "end", FT_END },
834 { "extension", FT_EXT },
835 { "guiname", FT_NAME },
836 { "htmlclass", FT_HTMLCLASS },
837 { "htmlstyle", FT_HTMLSTYLE },
838 { "htmltype", FT_HTMLTYPE },
839 { "latexbuiltin", FT_BUILTIN },
840 { "listname", FT_LISTNAME },
841 { "numberwithin", FT_WITHIN },
842 { "placement", FT_PLACEMENT },
843 { "style", FT_STYLE },
844 { "type", FT_TYPE }
847 lexrc.pushTable(floatTags);
849 string ext;
850 string htmlclass;
851 string htmlstyle;
852 string htmltype;
853 string listName;
854 string name;
855 string placement;
856 string style;
857 string type;
858 string within;
859 bool builtin = false;
861 bool getout = false;
862 while (!getout && lexrc.isOK()) {
863 int le = lexrc.lex();
864 switch (le) {
865 case Lexer::LEX_UNDEF:
866 lexrc.printError("Unknown float tag `$$Token'");
867 continue;
868 default: break;
870 switch (le) {
871 case FT_TYPE:
872 lexrc.next();
873 type = lexrc.getString();
874 if (floatlist_.typeExist(type)) {
875 Floating const & fl = floatlist_.getType(type);
876 placement = fl.placement();
877 ext = fl.ext();
878 within = fl.within();
879 style = fl.style();
880 name = fl.name();
881 listName = fl.listName();
882 builtin = fl.builtin();
884 break;
885 case FT_NAME:
886 lexrc.next();
887 name = lexrc.getString();
888 break;
889 case FT_PLACEMENT:
890 lexrc.next();
891 placement = lexrc.getString();
892 break;
893 case FT_EXT:
894 lexrc.next();
895 ext = lexrc.getString();
896 break;
897 case FT_WITHIN:
898 lexrc.next();
899 within = lexrc.getString();
900 if (within == "none")
901 within.erase();
902 break;
903 case FT_STYLE:
904 lexrc.next();
905 style = lexrc.getString();
906 break;
907 case FT_LISTNAME:
908 lexrc.next();
909 listName = lexrc.getString();
910 break;
911 case FT_BUILTIN:
912 lexrc.next();
913 builtin = lexrc.getBool();
914 break;
915 case FT_HTMLCLASS:
916 lexrc.next();
917 htmlclass = lexrc.getString();
918 break;
919 case FT_HTMLSTYLE:
920 lexrc.next();
921 htmlstyle = lexrc.getLongString("EndHTMLStyle");
922 break;
923 case FT_HTMLTYPE:
924 lexrc.next();
925 htmltype = lexrc.getString();
926 break;
927 case FT_END:
928 getout = true;
929 break;
933 // Here if have a full float if getout == true
934 if (getout) {
935 Floating fl(type, placement, ext, within, style, name,
936 listName, htmltype, htmlclass, htmlstyle, builtin);
937 floatlist_.newFloat(fl);
938 // each float has its own counter
939 counters_.newCounter(from_ascii(type), from_ascii(within),
940 docstring(), docstring());
941 // also define sub-float counters
942 docstring const subtype = "sub-" + from_ascii(type);
943 counters_.newCounter(subtype, from_ascii(type),
944 "\\alph{" + subtype + "}", docstring());
947 lexrc.popTable();
951 bool TextClass::hasLayout(docstring const & n) const
953 docstring const name = n.empty() ? defaultLayoutName() : n;
955 return find_if(layoutlist_.begin(), layoutlist_.end(),
956 LayoutNamesEqual(name))
957 != layoutlist_.end();
961 bool TextClass::hasInsetLayout(docstring const & n) const
963 if (n.empty())
964 return false;
965 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
966 InsetLayouts::const_iterator en = insetlayoutlist_.end();
967 for (; it != en; ++it)
968 if (n == it->first)
969 return true;
970 return false;
974 Layout const & TextClass::operator[](docstring const & name) const
976 LASSERT(!name.empty(), /**/);
978 const_iterator it =
979 find_if(begin(), end(), LayoutNamesEqual(name));
981 if (it == end()) {
982 lyxerr << "We failed to find the layout '" << to_utf8(name)
983 << "' in the layout list. You MUST investigate!"
984 << endl;
985 for (const_iterator cit = begin(); cit != end(); ++cit)
986 lyxerr << " " << to_utf8(cit->name()) << endl;
988 // we require the name to exist
989 LASSERT(false, /**/);
992 return *it;
996 Layout & TextClass::operator[](docstring const & name)
998 LASSERT(!name.empty(), /**/);
1000 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1002 if (it == end()) {
1003 LYXERR0("We failed to find the layout '" << to_utf8(name)
1004 << "' in the layout list. You MUST investigate!");
1005 for (const_iterator cit = begin(); cit != end(); ++cit)
1006 LYXERR0(" " << to_utf8(cit->name()));
1008 // we require the name to exist
1009 LASSERT(false, /**/);
1012 return *it;
1016 bool TextClass::deleteLayout(docstring const & name)
1018 if (name == defaultLayoutName() || name == plainLayoutName())
1019 return false;
1021 LayoutList::iterator it =
1022 remove_if(layoutlist_.begin(), layoutlist_.end(),
1023 LayoutNamesEqual(name));
1025 LayoutList::iterator end = layoutlist_.end();
1026 bool const ret = (it != end);
1027 layoutlist_.erase(it, end);
1028 return ret;
1032 // Load textclass info if not loaded yet
1033 bool TextClass::load(string const & path) const
1035 if (loaded_)
1036 return true;
1038 // Read style-file, provided path is searched before the system ones
1039 // If path is a file, it is loaded directly.
1040 FileName layout_file(path);
1041 if (!path.empty() && !layout_file.isReadableFile())
1042 layout_file = FileName(addName(path, name_ + ".layout"));
1043 if (layout_file.empty() || !layout_file.exists())
1044 layout_file = libFileSearch("layouts", name_, "layout");
1045 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1047 if (!loaded_) {
1048 lyxerr << "Error reading `"
1049 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1050 << "'\n(Check `" << name_
1051 << "')\nCheck your installation and "
1052 "try Options/Reconfigure..." << endl;
1055 return loaded_;
1059 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1061 if (!hasLayout(n))
1062 layoutlist_.push_back(createBasicLayout(n, true));
1066 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1068 // FIXME The fix for the InsetLayout part of 4812 would be here:
1069 // Add the InsetLayout to the document class if it is not found.
1070 docstring n = name;
1071 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1072 while (!n.empty()) {
1073 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1074 if (cit != cen && cit->first == n)
1075 return cit->second;
1076 size_t i = n.find(':');
1077 if (i == string::npos)
1078 break;
1079 n = n.substr(0, i);
1081 return plain_insetlayout_;
1085 docstring const & TextClass::defaultLayoutName() const
1087 // This really should come from the actual layout... (Lgb)
1088 return defaultlayout_;
1092 Layout const & TextClass::defaultLayout() const
1094 return operator[](defaultLayoutName());
1098 bool TextClass::isDefaultLayout(Layout const & layout) const
1100 return layout.name() == defaultLayoutName();
1104 bool TextClass::isPlainLayout(Layout const & layout) const
1106 return layout.name() == plainLayoutName();
1110 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1112 static Layout * defaultLayout = NULL;
1114 if (defaultLayout) {
1115 defaultLayout->setUnknown(unknown);
1116 defaultLayout->setName(name);
1117 return *defaultLayout;
1120 static char const * s = "Margin Static\n"
1121 "LatexType Paragraph\n"
1122 "LatexName dummy\n"
1123 "Align Block\n"
1124 "AlignPossible Left, Right, Center\n"
1125 "LabelType No_Label\n"
1126 "End";
1127 istringstream ss(s);
1128 Lexer lex(textClassTags);
1129 lex.setStream(ss);
1130 defaultLayout = new Layout;
1131 defaultLayout->setUnknown(unknown);
1132 defaultLayout->setName(name);
1133 if (!readStyle(lex, *defaultLayout)) {
1134 // The only way this happens is because the hardcoded layout above
1135 // is wrong.
1136 LASSERT(false, /**/);
1138 return *defaultLayout;
1141 /////////////////////////////////////////////////////////////////////////
1143 // DocumentClassBundle
1145 /////////////////////////////////////////////////////////////////////////
1147 DocumentClassBundle::~DocumentClassBundle()
1149 for (size_t i = 0; i != documentClasses_.size(); ++i)
1150 delete documentClasses_[i];
1151 documentClasses_.clear();
1154 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1156 DocumentClass * dc = new DocumentClass(baseClass);
1157 documentClasses_.push_back(dc);
1158 return *documentClasses_.back();
1162 DocumentClassBundle & DocumentClassBundle::get()
1164 static DocumentClassBundle singleton;
1165 return singleton;
1169 DocumentClass & DocumentClassBundle::makeDocumentClass(
1170 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1172 DocumentClass & doc_class = newClass(baseClass);
1173 LayoutModuleList::const_iterator it = modlist.begin();
1174 LayoutModuleList::const_iterator en = modlist.end();
1175 for (; it != en; it++) {
1176 string const modName = *it;
1177 LyXModule * lm = moduleList[modName];
1178 if (!lm) {
1179 docstring const msg =
1180 bformat(_("The module %1$s has been requested by\n"
1181 "this document but has not been found in the list of\n"
1182 "available modules. If you recently installed it, you\n"
1183 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1184 ExceptionMessage(WarningException,_("Module not available"),
1185 msg + _("Some layouts may not be available."));
1186 continue;
1188 if (!lm->isAvailable()) {
1189 docstring const msg =
1190 bformat(_("The module %1$s requires a package that is\n"
1191 "not available in your LaTeX installation. LaTeX output\n"
1192 "may not be possible.\n"), from_utf8(modName));
1193 ExceptionMessage(WarningException, _("Package not available"), msg);
1195 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1196 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1197 docstring const msg =
1198 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1199 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1202 return doc_class;
1206 /////////////////////////////////////////////////////////////////////////
1208 // DocumentClass
1210 /////////////////////////////////////////////////////////////////////////
1212 DocumentClass::DocumentClass(LayoutFile const & tc)
1213 : TextClass(tc)
1217 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1219 LayoutList::const_iterator it = layoutlist_.begin();
1220 LayoutList::const_iterator end = layoutlist_.end();
1221 for (; it != end; ++it)
1222 if (it->latexname() == lay)
1223 return true;
1224 return false;
1228 bool DocumentClass::provides(string const & p) const
1230 return provides_.find(p) != provides_.end();
1234 bool DocumentClass::hasTocLevels() const
1236 return min_toclevel_ != Layout::NOT_IN_TOC;
1240 /////////////////////////////////////////////////////////////////////////
1242 // PageSides
1244 /////////////////////////////////////////////////////////////////////////
1246 ostream & operator<<(ostream & os, PageSides p)
1248 switch (p) {
1249 case OneSide:
1250 os << '1';
1251 break;
1252 case TwoSides:
1253 os << '2';
1254 break;
1256 return os;
1260 } // namespace lyx