* de.po: sync with branch.
[lyx.git] / src / TextClass.cpp
blob20416ee376612101c8eaf75d7cf0055fc8f1fdaf
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 = 17;
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_IFSTYLE,
168 TC_DEFAULTSTYLE,
169 TC_INSETLAYOUT,
170 TC_NOSTYLE,
171 TC_COLUMNS,
172 TC_SIDES,
173 TC_PAGESTYLE,
174 TC_DEFAULTFONT,
175 TC_SECNUMDEPTH,
176 TC_TOCDEPTH,
177 TC_CLASSOPTIONS,
178 TC_PREAMBLE,
179 TC_HTMLPREAMBLE,
180 TC_PROVIDES,
181 TC_REQUIRES,
182 TC_LEFTMARGIN,
183 TC_RIGHTMARGIN,
184 TC_FLOAT,
185 TC_COUNTER,
186 TC_IFCOUNTER,
187 TC_NOFLOAT,
188 TC_TITLELATEXNAME,
189 TC_TITLELATEXTYPE,
190 TC_FORMAT,
191 TC_ADDTOPREAMBLE,
192 TC_ADDTOHTMLPREAMBLE,
193 TC_DEFAULTMODULE,
194 TC_PROVIDESMODULE,
195 TC_EXCLUDESMODULE
199 namespace {
201 LexerKeyword textClassTags[] = {
202 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
203 { "addtopreamble", TC_ADDTOPREAMBLE },
204 { "classoptions", TC_CLASSOPTIONS },
205 { "columns", TC_COLUMNS },
206 { "counter", TC_COUNTER },
207 { "defaultfont", TC_DEFAULTFONT },
208 { "defaultmodule", TC_DEFAULTMODULE },
209 { "defaultstyle", TC_DEFAULTSTYLE },
210 { "excludesmodule", TC_EXCLUDESMODULE },
211 { "float", TC_FLOAT },
212 { "format", TC_FORMAT },
213 { "htmlpreamble", TC_HTMLPREAMBLE },
214 { "ifcounter", TC_IFCOUNTER },
215 { "ifstyle", TC_IFSTYLE },
216 { "input", TC_INPUT },
217 { "insetlayout", TC_INSETLAYOUT },
218 { "leftmargin", TC_LEFTMARGIN },
219 { "nofloat", TC_NOFLOAT },
220 { "nostyle", TC_NOSTYLE },
221 { "outputformat", TC_OUTPUTFORMAT },
222 { "outputtype", TC_OUTPUTTYPE },
223 { "pagestyle", TC_PAGESTYLE },
224 { "preamble", TC_PREAMBLE },
225 { "provides", TC_PROVIDES },
226 { "providesmodule", TC_PROVIDESMODULE },
227 { "requires", TC_REQUIRES },
228 { "rightmargin", TC_RIGHTMARGIN },
229 { "secnumdepth", TC_SECNUMDEPTH },
230 { "sides", TC_SIDES },
231 { "style", TC_STYLE },
232 { "titlelatexname", TC_TITLELATEXNAME },
233 { "titlelatextype", TC_TITLELATEXTYPE },
234 { "tocdepth", TC_TOCDEPTH }
237 } //namespace anon
240 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
242 LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
243 FileName const tempfile = FileName::tempName("convert_layout");
244 bool success = layout2layout(filename, tempfile);
245 if (success)
246 success = readWithoutConv(tempfile, rt) == OK;
247 tempfile.removeFile();
248 return success;
252 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
254 if (!filename.isReadableFile()) {
255 lyxerr << "Cannot read layout file `" << filename << "'."
256 << endl;
257 return ERROR;
260 LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
261 to_utf8(makeDisplayPath(filename.absFilename())));
263 // Define the plain layout used in table cells, ert, etc. Note that
264 // we do this before loading any layout file, so that classes can
265 // override features of this layout if they should choose to do so.
266 if (rt == BASECLASS && !hasLayout(plain_layout_))
267 layoutlist_.push_back(createBasicLayout(plain_layout_));
269 Lexer lexrc(textClassTags);
270 lexrc.setFile(filename);
271 ReturnValues retval = read(lexrc, rt);
273 LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
274 to_utf8(makeDisplayPath(filename.absFilename())));
276 return retval;
280 bool TextClass::read(FileName const & filename, ReadType rt)
282 ReturnValues const retval = readWithoutConv(filename, rt);
283 if (retval != FORMAT_MISMATCH)
284 return retval == OK;
286 bool const worx = convertLayoutFormat(filename, rt);
287 if (!worx) {
288 LYXERR0 ("Unable to convert " << filename <<
289 " to format " << FORMAT);
290 return false;
292 return true;
296 bool TextClass::validate(std::string const & str)
298 TextClass tc;
299 return tc.read(str, VALIDATION);
303 bool TextClass::read(std::string const & str, ReadType rt)
305 Lexer lexrc(textClassTags);
306 istringstream is(str);
307 lexrc.setStream(is);
308 ReturnValues retval = read(lexrc, rt);
310 if (retval != FORMAT_MISMATCH)
311 return retval == OK;
313 // write the layout string to a temporary file
314 FileName const tempfile = FileName::tempName("TextClass_read");
315 ofstream os(tempfile.toFilesystemEncoding().c_str());
316 if (!os) {
317 LYXERR0("Unable to create temporary file");
318 return false;
320 os << str;
321 os.close();
323 // now try to convert it
324 bool const worx = convertLayoutFormat(tempfile, rt);
325 if (!worx) {
326 LYXERR0("Unable to convert internal layout information to format "
327 << FORMAT);
329 tempfile.removeFile();
330 return worx;
334 // Reads a textclass structure from file.
335 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
337 bool error = !lexrc.isOK();
339 // Format of files before the 'Format' tag was introduced
340 int format = 1;
342 // parsing
343 while (lexrc.isOK() && !error) {
344 int le = lexrc.lex();
346 switch (le) {
347 case Lexer::LEX_FEOF:
348 continue;
350 case Lexer::LEX_UNDEF:
351 lexrc.printError("Unknown TextClass tag `$$Token'");
352 error = true;
353 continue;
355 default:
356 break;
359 // used below to track whether we are in an IfStyle or IfCounter tag.
360 bool ifstyle = false;
361 bool ifcounter = false;
363 switch (static_cast<TextClassTags>(le)) {
365 case TC_FORMAT:
366 if (lexrc.next())
367 format = lexrc.getInteger();
368 break;
370 case TC_OUTPUTFORMAT:
371 if (lexrc.next())
372 outputFormat_ = lexrc.getString();
373 break;
375 case TC_OUTPUTTYPE:
376 readOutputType(lexrc);
377 switch(outputType_) {
378 case LATEX:
379 outputFormat_ = "latex";
380 break;
381 case DOCBOOK:
382 outputFormat_ = "docbook";
383 break;
384 case LITERATE:
385 outputFormat_ = "literate";
386 break;
388 break;
390 case TC_INPUT: // Include file
391 if (lexrc.next()) {
392 string const inc = lexrc.getString();
393 FileName tmp = libFileSearch("layouts", inc,
394 "layout");
396 if (tmp.empty()) {
397 lexrc.printError("Could not find input file: " + inc);
398 error = true;
399 } else if (!read(tmp, MERGE)) {
400 lexrc.printError("Error reading input"
401 "file: " + tmp.absFilename());
402 error = true;
405 break;
407 case TC_DEFAULTSTYLE:
408 if (lexrc.next()) {
409 docstring const name = from_utf8(subst(lexrc.getString(),
410 '_', ' '));
411 defaultlayout_ = name;
413 break;
415 case TC_IFSTYLE:
416 ifstyle = true;
417 // fall through
418 case TC_STYLE: {
419 if (!lexrc.next()) {
420 lexrc.printError("No name given for style: `$$Token'.");
421 error = true;
422 break;
424 docstring const name = from_utf8(subst(lexrc.getString(),
425 '_', ' '));
426 if (name.empty()) {
427 string s = "Could not read name for style: `$$Token' "
428 + lexrc.getString() + " is probably not valid UTF-8!";
429 lexrc.printError(s.c_str());
430 Layout lay;
431 // Since we couldn't read the name, we just scan the rest
432 // of the style and discard it.
433 error = !readStyle(lexrc, lay);
434 } else if (hasLayout(name)) {
435 Layout & lay = operator[](name);
436 error = !readStyle(lexrc, lay);
437 } else if (!ifstyle) {
438 Layout layout;
439 layout.setName(name);
440 error = !readStyle(lexrc, layout);
441 if (!error)
442 layoutlist_.push_back(layout);
444 if (defaultlayout_.empty()) {
445 // We do not have a default layout yet, so we choose
446 // the first layout we encounter.
447 defaultlayout_ = name;
450 else {
451 // scan the rest and discard it
452 Layout lay;
453 readStyle(lexrc, lay);
454 error = false;
457 // reset flag
458 ifstyle = false;
459 break;
462 case TC_NOSTYLE:
463 if (lexrc.next()) {
464 docstring const style = from_utf8(subst(lexrc.getString(),
465 '_', ' '));
466 if (!deleteLayout(style))
467 lyxerr << "Cannot delete style `"
468 << to_utf8(style) << '\'' << endl;
470 break;
472 case TC_COLUMNS:
473 if (lexrc.next())
474 columns_ = lexrc.getInteger();
475 break;
477 case TC_SIDES:
478 if (lexrc.next()) {
479 switch (lexrc.getInteger()) {
480 case 1: sides_ = OneSide; break;
481 case 2: sides_ = TwoSides; break;
482 default:
483 lyxerr << "Impossible number of page"
484 " sides, setting to one."
485 << endl;
486 sides_ = OneSide;
487 break;
490 break;
492 case TC_PAGESTYLE:
493 lexrc.next();
494 pagestyle_ = rtrim(lexrc.getString());
495 break;
497 case TC_DEFAULTFONT:
498 defaultfont_ = lyxRead(lexrc);
499 if (!defaultfont_.resolved()) {
500 lexrc.printError("Warning: defaultfont should "
501 "be fully instantiated!");
502 defaultfont_.realize(sane_font);
504 break;
506 case TC_SECNUMDEPTH:
507 lexrc.next();
508 secnumdepth_ = lexrc.getInteger();
509 break;
511 case TC_TOCDEPTH:
512 lexrc.next();
513 tocdepth_ = lexrc.getInteger();
514 break;
516 // First step to support options
517 case TC_CLASSOPTIONS:
518 readClassOptions(lexrc);
519 break;
521 case TC_PREAMBLE:
522 preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
523 break;
525 case TC_HTMLPREAMBLE:
526 htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
527 break;
529 case TC_ADDTOPREAMBLE:
530 preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
531 break;
533 case TC_ADDTOHTMLPREAMBLE:
534 htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
535 break;
537 case TC_PROVIDES: {
538 lexrc.next();
539 string const feature = lexrc.getString();
540 lexrc.next();
541 if (lexrc.getInteger())
542 provides_.insert(feature);
543 else
544 provides_.erase(feature);
545 break;
548 case TC_REQUIRES: {
549 lexrc.eatLine();
550 vector<string> const req
551 = getVectorFromString(lexrc.getString());
552 requires_.insert(req.begin(), req.end());
553 break;
556 case TC_DEFAULTMODULE: {
557 lexrc.next();
558 string const module = lexrc.getString();
559 if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
560 default_modules_.push_back(module);
561 break;
564 case TC_PROVIDESMODULE: {
565 lexrc.next();
566 string const module = lexrc.getString();
567 if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
568 provided_modules_.push_back(module);
569 break;
572 case TC_EXCLUDESMODULE: {
573 lexrc.next();
574 string const module = lexrc.getString();
575 // modules already have their own way to exclude other modules
576 if (rt == MODULE) {
577 LYXERR0("ExcludesModule tag cannot be used in a module!");
578 break;
580 if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
581 excluded_modules_.push_back(module);
582 break;
585 case TC_LEFTMARGIN: // left margin type
586 if (lexrc.next())
587 leftmargin_ = lexrc.getDocString();
588 break;
590 case TC_RIGHTMARGIN: // right margin type
591 if (lexrc.next())
592 rightmargin_ = lexrc.getDocString();
593 break;
595 case TC_INSETLAYOUT: {
596 if (!lexrc.next()) {
597 lexrc.printError("No name given for InsetLayout: `$$Token'.");
598 error = true;
599 break;
601 docstring const name = subst(lexrc.getDocString(), '_', ' ');
602 if (name.empty()) {
603 string s = "Could not read name for InsetLayout: `$$Token' "
604 + lexrc.getString() + " is probably not valid UTF-8!";
605 lexrc.printError(s.c_str());
606 InsetLayout il;
607 // Since we couldn't read the name, we just scan the rest
608 // of the style and discard it.
609 il.read(lexrc, *this);
610 error = true;
611 } else if (hasInsetLayout(name)) {
612 InsetLayout & il = insetlayoutlist_[name];
613 error = !il.read(lexrc, *this);
614 } else {
615 InsetLayout il;
616 il.setName(name);
617 error = !il.read(lexrc, *this);
618 if (!error)
619 insetlayoutlist_[name] = il;
621 break;
624 case TC_FLOAT:
625 readFloat(lexrc);
626 break;
628 case TC_IFCOUNTER:
629 ifcounter = true;
630 case TC_COUNTER:
631 if (lexrc.next()) {
632 docstring const name = lexrc.getDocString();
633 if (name.empty()) {
634 string s = "Could not read name for counter: `$$Token' "
635 + lexrc.getString() + " is probably not valid UTF-8!";
636 lexrc.printError(s.c_str());
637 Counter c;
638 // Since we couldn't read the name, we just scan the rest
639 // and discard it.
640 c.read(lexrc);
641 } else
642 error = !counters_.read(lexrc, name, !ifcounter);
644 else {
645 lexrc.printError("No name given for style: `$$Token'.");
646 error = true;
648 // reset flag
649 ifcounter = false;
650 break;
652 case TC_TITLELATEXTYPE:
653 readTitleType(lexrc);
654 break;
656 case TC_TITLELATEXNAME:
657 if (lexrc.next())
658 titlename_ = lexrc.getString();
659 break;
661 case TC_NOFLOAT:
662 if (lexrc.next()) {
663 string const nofloat = lexrc.getString();
664 floatlist_.erase(nofloat);
666 break;
667 } // end of switch
669 //Note that this is triggered the first time through the loop unless
670 //we hit a format tag.
671 if (format != FORMAT)
672 break;
675 if (format != FORMAT)
676 return FORMAT_MISMATCH;
678 if (rt != BASECLASS)
679 return (error ? ERROR : OK);
681 if (defaultlayout_.empty()) {
682 LYXERR0("Error: Textclass '" << name_
683 << "' is missing a defaultstyle.");
684 error = true;
687 // Try to erase "stdinsets" from the provides_ set.
688 // The
689 // Provides stdinsets 1
690 // declaration simply tells us that the standard insets have been
691 // defined. (It's found in stdinsets.inc but could also be used in
692 // user-defined files.) There isn't really any such package. So we
693 // might as well go ahead and erase it.
694 // If we do not succeed, then it was not there, which means that
695 // the textclass did not provide the definitions of the standard
696 // insets. So we need to try to load them.
697 int erased = provides_.erase("stdinsets");
698 if (!erased) {
699 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
701 if (tmp.empty()) {
702 throw ExceptionMessage(WarningException, _("Missing File"),
703 _("Could not find stdinsets.inc! This may lead to data loss!"));
704 error = true;
705 } else if (!read(tmp, MERGE)) {
706 throw ExceptionMessage(WarningException, _("Corrupt File"),
707 _("Could not read stdinsets.inc! This may lead to data loss!"));
708 error = true;
712 min_toclevel_ = Layout::NOT_IN_TOC;
713 max_toclevel_ = Layout::NOT_IN_TOC;
714 const_iterator lit = begin();
715 const_iterator len = end();
716 for (; lit != len; ++lit) {
717 int const toclevel = lit->toclevel;
718 if (toclevel != Layout::NOT_IN_TOC) {
719 if (min_toclevel_ == Layout::NOT_IN_TOC)
720 min_toclevel_ = toclevel;
721 else
722 min_toclevel_ = min(min_toclevel_, toclevel);
723 max_toclevel_ = max(max_toclevel_, toclevel);
726 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
727 << ", maximum is " << max_toclevel_);
729 return (error ? ERROR : OK);
733 void TextClass::readTitleType(Lexer & lexrc)
735 LexerKeyword titleTypeTags[] = {
736 { "commandafter", TITLE_COMMAND_AFTER },
737 { "environment", TITLE_ENVIRONMENT }
740 PushPopHelper pph(lexrc, titleTypeTags);
742 int le = lexrc.lex();
743 switch (le) {
744 case Lexer::LEX_UNDEF:
745 lexrc.printError("Unknown output type `$$Token'");
746 break;
747 case TITLE_COMMAND_AFTER:
748 case TITLE_ENVIRONMENT:
749 titletype_ = static_cast<TitleLatexType>(le);
750 break;
751 default:
752 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
753 break;
758 void TextClass::readOutputType(Lexer & lexrc)
760 LexerKeyword outputTypeTags[] = {
761 { "docbook", DOCBOOK },
762 { "latex", LATEX },
763 { "literate", LITERATE }
766 PushPopHelper pph(lexrc, outputTypeTags);
768 int le = lexrc.lex();
769 switch (le) {
770 case Lexer::LEX_UNDEF:
771 lexrc.printError("Unknown output type `$$Token'");
772 return;
773 case LATEX:
774 case DOCBOOK:
775 case LITERATE:
776 outputType_ = static_cast<OutputType>(le);
777 break;
778 default:
779 LYXERR0("Unhandled value " << le);
780 break;
785 void TextClass::readClassOptions(Lexer & lexrc)
787 enum {
788 CO_FONTSIZE = 1,
789 CO_PAGESTYLE,
790 CO_OTHER,
791 CO_HEADER,
792 CO_END
795 LexerKeyword classOptionsTags[] = {
796 {"end", CO_END },
797 {"fontsize", CO_FONTSIZE },
798 {"header", CO_HEADER },
799 {"other", CO_OTHER },
800 {"pagestyle", CO_PAGESTYLE }
803 lexrc.pushTable(classOptionsTags);
804 bool getout = false;
805 while (!getout && lexrc.isOK()) {
806 int le = lexrc.lex();
807 switch (le) {
808 case Lexer::LEX_UNDEF:
809 lexrc.printError("Unknown ClassOption tag `$$Token'");
810 continue;
811 default: break;
813 switch (le) {
814 case CO_FONTSIZE:
815 lexrc.next();
816 opt_fontsize_ = rtrim(lexrc.getString());
817 break;
818 case CO_PAGESTYLE:
819 lexrc.next();
820 opt_pagestyle_ = rtrim(lexrc.getString());
821 break;
822 case CO_OTHER:
823 lexrc.next();
824 options_ = lexrc.getString();
825 break;
826 case CO_HEADER:
827 lexrc.next();
828 class_header_ = subst(lexrc.getString(), "&quot;", "\"");
829 break;
830 case CO_END:
831 getout = true;
832 break;
835 lexrc.popTable();
839 void TextClass::readFloat(Lexer & lexrc)
841 enum {
842 FT_TYPE = 1,
843 FT_NAME,
844 FT_PLACEMENT,
845 FT_EXT,
846 FT_WITHIN,
847 FT_STYLE,
848 FT_LISTNAME,
849 FT_BUILTIN,
850 FT_HTMLSTYLE,
851 FT_HTMLCLASS,
852 FT_HTMLTYPE,
853 FT_END
856 LexerKeyword floatTags[] = {
857 { "end", FT_END },
858 { "extension", FT_EXT },
859 { "guiname", FT_NAME },
860 { "htmlclass", FT_HTMLCLASS },
861 { "htmlstyle", FT_HTMLSTYLE },
862 { "htmltype", FT_HTMLTYPE },
863 { "latexbuiltin", FT_BUILTIN },
864 { "listname", FT_LISTNAME },
865 { "numberwithin", FT_WITHIN },
866 { "placement", FT_PLACEMENT },
867 { "style", FT_STYLE },
868 { "type", FT_TYPE }
871 lexrc.pushTable(floatTags);
873 string ext;
874 string htmlclass;
875 string htmlstyle;
876 string htmltype;
877 string listName;
878 string name;
879 string placement;
880 string style;
881 string type;
882 string within;
883 bool builtin = false;
885 bool getout = false;
886 while (!getout && lexrc.isOK()) {
887 int le = lexrc.lex();
888 switch (le) {
889 case Lexer::LEX_UNDEF:
890 lexrc.printError("Unknown float tag `$$Token'");
891 continue;
892 default: break;
894 switch (le) {
895 case FT_TYPE:
896 lexrc.next();
897 type = lexrc.getString();
898 if (floatlist_.typeExist(type)) {
899 Floating const & fl = floatlist_.getType(type);
900 placement = fl.placement();
901 ext = fl.ext();
902 within = fl.within();
903 style = fl.style();
904 name = fl.name();
905 listName = fl.listName();
906 builtin = fl.builtin();
908 break;
909 case FT_NAME:
910 lexrc.next();
911 name = lexrc.getString();
912 break;
913 case FT_PLACEMENT:
914 lexrc.next();
915 placement = lexrc.getString();
916 break;
917 case FT_EXT:
918 lexrc.next();
919 ext = lexrc.getString();
920 break;
921 case FT_WITHIN:
922 lexrc.next();
923 within = lexrc.getString();
924 if (within == "none")
925 within.erase();
926 break;
927 case FT_STYLE:
928 lexrc.next();
929 style = lexrc.getString();
930 break;
931 case FT_LISTNAME:
932 lexrc.next();
933 listName = lexrc.getString();
934 break;
935 case FT_BUILTIN:
936 lexrc.next();
937 builtin = lexrc.getBool();
938 break;
939 case FT_HTMLCLASS:
940 lexrc.next();
941 htmlclass = lexrc.getString();
942 break;
943 case FT_HTMLSTYLE:
944 lexrc.next();
945 htmlstyle = lexrc.getLongString("EndHTMLStyle");
946 break;
947 case FT_HTMLTYPE:
948 lexrc.next();
949 htmltype = lexrc.getString();
950 break;
951 case FT_END:
952 getout = true;
953 break;
957 // Here if have a full float if getout == true
958 if (getout) {
959 Floating fl(type, placement, ext, within, style, name,
960 listName, htmltype, htmlclass, htmlstyle, builtin);
961 floatlist_.newFloat(fl);
962 // each float has its own counter
963 counters_.newCounter(from_ascii(type), from_ascii(within),
964 docstring(), docstring());
965 // also define sub-float counters
966 docstring const subtype = "sub-" + from_ascii(type);
967 counters_.newCounter(subtype, from_ascii(type),
968 "\\alph{" + subtype + "}", docstring());
971 lexrc.popTable();
975 bool TextClass::hasLayout(docstring const & n) const
977 docstring const name = n.empty() ? defaultLayoutName() : n;
979 return find_if(layoutlist_.begin(), layoutlist_.end(),
980 LayoutNamesEqual(name))
981 != layoutlist_.end();
985 bool TextClass::hasInsetLayout(docstring const & n) const
987 if (n.empty())
988 return false;
989 InsetLayouts::const_iterator it = insetlayoutlist_.begin();
990 InsetLayouts::const_iterator en = insetlayoutlist_.end();
991 for (; it != en; ++it)
992 if (n == it->first)
993 return true;
994 return false;
998 Layout const & TextClass::operator[](docstring const & name) const
1000 LASSERT(!name.empty(), /**/);
1002 const_iterator it =
1003 find_if(begin(), end(), LayoutNamesEqual(name));
1005 if (it == end()) {
1006 lyxerr << "We failed to find the layout '" << to_utf8(name)
1007 << "' in the layout list. You MUST investigate!"
1008 << endl;
1009 for (const_iterator cit = begin(); cit != end(); ++cit)
1010 lyxerr << " " << to_utf8(cit->name()) << endl;
1012 // we require the name to exist
1013 LASSERT(false, /**/);
1016 return *it;
1020 Layout & TextClass::operator[](docstring const & name)
1022 LASSERT(!name.empty(), /**/);
1024 iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1026 if (it == end()) {
1027 LYXERR0("We failed to find the layout '" << to_utf8(name)
1028 << "' in the layout list. You MUST investigate!");
1029 for (const_iterator cit = begin(); cit != end(); ++cit)
1030 LYXERR0(" " << to_utf8(cit->name()));
1032 // we require the name to exist
1033 LASSERT(false, /**/);
1036 return *it;
1040 bool TextClass::deleteLayout(docstring const & name)
1042 if (name == defaultLayoutName() || name == plainLayoutName())
1043 return false;
1045 LayoutList::iterator it =
1046 remove_if(layoutlist_.begin(), layoutlist_.end(),
1047 LayoutNamesEqual(name));
1049 LayoutList::iterator end = layoutlist_.end();
1050 bool const ret = (it != end);
1051 layoutlist_.erase(it, end);
1052 return ret;
1056 // Load textclass info if not loaded yet
1057 bool TextClass::load(string const & path) const
1059 if (loaded_)
1060 return true;
1062 // Read style-file, provided path is searched before the system ones
1063 // If path is a file, it is loaded directly.
1064 FileName layout_file(path);
1065 if (!path.empty() && !layout_file.isReadableFile())
1066 layout_file = FileName(addName(path, name_ + ".layout"));
1067 if (layout_file.empty() || !layout_file.exists())
1068 layout_file = libFileSearch("layouts", name_, "layout");
1069 loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1071 if (!loaded_) {
1072 lyxerr << "Error reading `"
1073 << to_utf8(makeDisplayPath(layout_file.absFilename()))
1074 << "'\n(Check `" << name_
1075 << "')\nCheck your installation and "
1076 "try Options/Reconfigure..." << endl;
1079 return loaded_;
1083 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1085 if (!hasLayout(n))
1086 layoutlist_.push_back(createBasicLayout(n, true));
1090 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1092 // FIXME The fix for the InsetLayout part of 4812 would be here:
1093 // Add the InsetLayout to the document class if it is not found.
1094 docstring n = name;
1095 InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1096 while (!n.empty()) {
1097 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1098 if (cit != cen && cit->first == n)
1099 return cit->second;
1100 size_t i = n.find(':');
1101 if (i == string::npos)
1102 break;
1103 n = n.substr(0, i);
1105 return plain_insetlayout_;
1109 docstring const & TextClass::defaultLayoutName() const
1111 // This really should come from the actual layout... (Lgb)
1112 return defaultlayout_;
1116 Layout const & TextClass::defaultLayout() const
1118 return operator[](defaultLayoutName());
1122 bool TextClass::isDefaultLayout(Layout const & layout) const
1124 return layout.name() == defaultLayoutName();
1128 bool TextClass::isPlainLayout(Layout const & layout) const
1130 return layout.name() == plainLayoutName();
1134 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1136 static Layout * defaultLayout = NULL;
1138 if (defaultLayout) {
1139 defaultLayout->setUnknown(unknown);
1140 defaultLayout->setName(name);
1141 return *defaultLayout;
1144 static char const * s = "Margin Static\n"
1145 "LatexType Paragraph\n"
1146 "LatexName dummy\n"
1147 "Align Block\n"
1148 "AlignPossible Left, Right, Center\n"
1149 "LabelType No_Label\n"
1150 "End";
1151 istringstream ss(s);
1152 Lexer lex(textClassTags);
1153 lex.setStream(ss);
1154 defaultLayout = new Layout;
1155 defaultLayout->setUnknown(unknown);
1156 defaultLayout->setName(name);
1157 if (!readStyle(lex, *defaultLayout)) {
1158 // The only way this happens is because the hardcoded layout above
1159 // is wrong.
1160 LASSERT(false, /**/);
1162 return *defaultLayout;
1165 /////////////////////////////////////////////////////////////////////////
1167 // DocumentClassBundle
1169 /////////////////////////////////////////////////////////////////////////
1171 DocumentClassBundle::~DocumentClassBundle()
1173 for (size_t i = 0; i != documentClasses_.size(); ++i)
1174 delete documentClasses_[i];
1175 documentClasses_.clear();
1178 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1180 DocumentClass * dc = new DocumentClass(baseClass);
1181 documentClasses_.push_back(dc);
1182 return *documentClasses_.back();
1186 DocumentClassBundle & DocumentClassBundle::get()
1188 static DocumentClassBundle singleton;
1189 return singleton;
1193 DocumentClass & DocumentClassBundle::makeDocumentClass(
1194 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1196 DocumentClass & doc_class = newClass(baseClass);
1197 LayoutModuleList::const_iterator it = modlist.begin();
1198 LayoutModuleList::const_iterator en = modlist.end();
1199 for (; it != en; it++) {
1200 string const modName = *it;
1201 LyXModule * lm = theModuleList[modName];
1202 if (!lm) {
1203 docstring const msg =
1204 bformat(_("The module %1$s has been requested by\n"
1205 "this document but has not been found in the list of\n"
1206 "available modules. If you recently installed it, you\n"
1207 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1208 ExceptionMessage(WarningException,_("Module not available"),
1209 msg + _("Some layouts may not be available."));
1210 continue;
1212 if (!lm->isAvailable()) {
1213 docstring const msg =
1214 bformat(_("The module %1$s requires a package that is\n"
1215 "not available in your LaTeX installation. LaTeX output\n"
1216 "may not be possible.\n"), from_utf8(modName));
1217 ExceptionMessage(WarningException, _("Package not available"), msg);
1219 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1220 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1221 docstring const msg =
1222 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1223 throw ExceptionMessage(WarningException, _("Read Error"), msg);
1226 return doc_class;
1230 /////////////////////////////////////////////////////////////////////////
1232 // DocumentClass
1234 /////////////////////////////////////////////////////////////////////////
1236 DocumentClass::DocumentClass(LayoutFile const & tc)
1237 : TextClass(tc)
1241 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1243 LayoutList::const_iterator it = layoutlist_.begin();
1244 LayoutList::const_iterator end = layoutlist_.end();
1245 for (; it != end; ++it)
1246 if (it->latexname() == lay)
1247 return true;
1248 return false;
1252 bool DocumentClass::provides(string const & p) const
1254 return provides_.find(p) != provides_.end();
1258 bool DocumentClass::hasTocLevels() const
1260 return min_toclevel_ != Layout::NOT_IN_TOC;
1264 /////////////////////////////////////////////////////////////////////////
1266 // PageSides
1268 /////////////////////////////////////////////////////////////////////////
1270 ostream & operator<<(ostream & os, PageSides p)
1272 switch (p) {
1273 case OneSide:
1274 os << '1';
1275 break;
1276 case TwoSides:
1277 os << '2';
1278 break;
1280 return os;
1284 } // namespace lyx