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
10 * Full author contact details are available in file CREDITS.
17 #include "TextClass.h"
21 #include "support/Messages.h"
22 #include "support/debug.h"
23 #include "support/lassert.h"
24 #include "support/lstrings.h"
26 #include <boost/regex.hpp>
29 using namespace lyx::support
;
33 /// Special value of toclevel for layouts that to not belong in a TOC
34 const int Layout::NOT_IN_TOC
= -1000;
36 // The order of the LayoutTags enum is no more important. [asierra300396]
50 //LT_ENVIRONMENT_DEFAULT,
67 LT_LABELSTRING_APPENDIX
,
105 LT_INTITLE
// keep this last!
108 /////////////////////
113 margintype
= MARGIN_STATIC
;
114 latextype
= LATEX_PARAGRAPH
;
120 labelfont
= inherit_font
;
122 reslabelfont
= sane_font
;
123 nextnoindent
= false;
128 labelbottomsep
= 0.0;
130 align
= LYX_ALIGN_BLOCK
;
131 alignpossible
= LYX_ALIGN_NONE
| LYX_ALIGN_LAYOUT
;
132 labeltype
= LABEL_NO_LABEL
;
133 endlabeltype
= END_LABEL_NO_LABEL
;
134 // Should or should not. That is the question.
135 // spacing.set(Spacing::OneHalf);
138 newline_allowed
= true;
139 free_spacing
= false;
141 toclevel
= NOT_IN_TOC
;
143 htmllabelfirst_
= false;
144 htmlforcecss_
= false;
148 bool Layout::read(Lexer
& lex
, TextClass
const & tclass
)
150 // This table is sorted alphabetically [asierra 30March96]
151 LexerKeyword layoutTags
[] = {
152 { "align", LT_ALIGN
},
153 { "alignpossible", LT_ALIGNPOSSIBLE
},
154 { "babelpreamble", LT_BABELPREAMBLE
},
155 { "bottomsep", LT_BOTTOMSEP
},
156 { "category", LT_CATEGORY
},
157 { "commanddepth", LT_COMMANDDEPTH
},
158 { "copystyle", LT_COPYSTYLE
},
159 { "dependson", LT_DEPENDSON
},
161 { "endlabelstring", LT_ENDLABELSTRING
},
162 { "endlabeltype", LT_ENDLABELTYPE
},
163 { "fill_bottom", LT_FILL_BOTTOM
},
164 { "fill_top", LT_FILL_TOP
},
166 { "freespacing", LT_FREE_SPACING
},
167 { "htmlattr", LT_HTMLATTR
},
168 { "htmlforcecss", LT_HTMLFORCECSS
},
169 { "htmlitem", LT_HTMLITEM
},
170 { "htmlitemattr", LT_HTMLITEMATTR
},
171 { "htmllabel", LT_HTMLLABEL
},
172 { "htmllabelattr", LT_HTMLLABELATTR
},
173 { "htmllabelfirst", LT_HTMLLABELFIRST
},
174 { "htmlpremable", LT_HTMLPREAMBLE
},
175 { "htmlstyle", LT_HTMLSTYLE
},
176 { "htmltag", LT_HTMLTAG
},
177 { "innertag", LT_INNERTAG
},
178 { "intitle", LT_INTITLE
},
179 { "itemsep", LT_ITEMSEP
},
180 { "itemtag", LT_ITEMTAG
},
181 { "keepempty", LT_KEEPEMPTY
},
182 { "labelbottomsep", LT_LABEL_BOTTOMSEP
},
183 { "labelcounter", LT_LABELCOUNTER
},
184 { "labelfont", LT_LABELFONT
},
185 { "labelindent", LT_LABELINDENT
},
186 { "labelsep", LT_LABELSEP
},
187 { "labelstring", LT_LABELSTRING
},
188 { "labelstringappendix", LT_LABELSTRING_APPENDIX
},
189 { "labeltag", LT_LABELTAG
},
190 { "labeltype", LT_LABELTYPE
},
191 { "langpreamble", LT_LANGPREAMBLE
},
192 { "latexname", LT_LATEXNAME
},
193 { "latexparam", LT_LATEXPARAM
},
194 { "latextype", LT_LATEXTYPE
},
195 { "leftmargin", LT_LEFTMARGIN
},
196 { "margin", LT_MARGIN
},
197 { "needprotect", LT_NEED_PROTECT
},
198 { "newline", LT_NEWLINE
},
199 { "nextnoindent", LT_NEXTNOINDENT
},
200 { "obsoletedby", LT_OBSOLETEDBY
},
201 { "optionalargs", LT_OPTARGS
},
202 { "parindent", LT_PARINDENT
},
203 { "parsep", LT_PARSEP
},
204 { "parskip", LT_PARSKIP
},
205 { "passthru", LT_PASS_THRU
},
206 { "preamble", LT_PREAMBLE
},
207 { "requires", LT_REQUIRES
},
208 { "rightmargin", LT_RIGHTMARGIN
},
209 { "spacing", LT_SPACING
},
210 { "textfont", LT_TEXTFONT
},
211 { "toclevel", LT_TOCLEVEL
},
212 { "topsep", LT_TOPSEP
}
216 bool finished
= false;
217 lex
.pushTable(layoutTags
);
218 // parse style section
219 while (!finished
&& lex
.isOK() && !error
) {
221 // See comment in LyXRC.cpp.
223 case Lexer::LEX_FEOF
:
226 case Lexer::LEX_UNDEF
: // parse error
227 lex
.printError("Unknown layout tag `$$Token'");
234 switch (static_cast<LayoutTags
>(le
)) {
235 case LT_END
: // end of structure
243 case LT_COPYSTYLE
: { // initialize with a known style
246 style
= subst(style
, '_', ' ');
248 if (tclass
.hasLayout(style
)) {
249 docstring
const tmpname
= name_
;
250 this->operator=(tclass
[style
]);
253 LYXERR0("Cannot copy unknown style `"
255 << "All layouts so far:");
256 DocumentClass::const_iterator lit
= tclass
.begin();
257 DocumentClass::const_iterator len
= tclass
.end();
258 for (; lit
!= len
; ++lit
)
259 LYXERR0(lit
->name());
264 case LT_OBSOLETEDBY
: { // replace with a known style
267 style
= subst(style
, '_', ' ');
269 if (tclass
.hasLayout(style
)) {
270 docstring
const tmpname
= name_
;
271 this->operator=(tclass
[style
]);
273 if (obsoleted_by().empty())
274 obsoleted_by_
= style
;
276 LYXERR0("Cannot replace with unknown style `"
279 //lex.printError("Cannot replace with"
288 depends_on_
= subst(depends_on_
, '_', ' ');
291 case LT_MARGIN
: // margin style definition.
295 case LT_LATEXTYPE
: // LaTeX style definition.
308 lex
>> optionalargs
;
311 case LT_NEED_PROTECT
:
320 font
= lyxRead(lex
, font
);
325 font
= lyxRead(lex
, font
);
329 labelfont
= lyxRead(lex
, labelfont
);
332 case LT_NEXTNOINDENT
: // Indent next paragraph?
336 case LT_COMMANDDEPTH
:
346 latexparam_
= subst(latexparam_
, """, "\"");
362 preamble_
= from_utf8(lex
.getLongString("EndPreamble"));
365 case LT_LANGPREAMBLE
:
366 langpreamble_
= from_utf8(lex
.getLongString("EndLangPreamble"));
369 case LT_BABELPREAMBLE
:
370 babelpreamble_
= from_utf8(lex
.getLongString("EndBabelPreamble"));
377 case LT_ENDLABELTYPE
:
378 readEndLabelType(lex
);
381 case LT_LEFTMARGIN
: // left margin type
385 case LT_RIGHTMARGIN
: // right margin type
389 case LT_LABELINDENT
: // label indenting flag
393 case LT_PARINDENT
: // paragraph indent. flag
397 case LT_PARSKIP
: // paragraph skip size
401 case LT_ITEMSEP
: // item separation size
405 case LT_TOPSEP
: // top separation size
409 case LT_BOTTOMSEP
: // bottom separation size
413 case LT_LABEL_BOTTOMSEP
: // label bottom separation size
414 lex
>> labelbottomsep
;
417 case LT_LABELSEP
: // label separator
419 labelsep
= subst(labelsep
, 'x', ' ');
422 case LT_PARSEP
: // par. separation size
426 case LT_FILL_TOP
: // fill top flag
430 case LT_FILL_BOTTOM
: // fill bottom flag
434 case LT_NEWLINE
: // newlines allowed?
435 lex
>> newline_allowed
;
438 case LT_ALIGN
: // paragraph align
441 case LT_ALIGNPOSSIBLE
: // paragraph allowed align
442 readAlignPossible(lex
);
445 case LT_LABELSTRING
: // label string definition
446 // FIXME: this means LT_ENDLABELSTRING may only
447 // occur after LT_LABELSTRING
449 labelstring_
= trim(labelstring_
);
450 labelstring_appendix_
= labelstring_
;
453 case LT_ENDLABELSTRING
: // endlabel string definition
454 lex
>> endlabelstring_
;
455 endlabelstring_
= trim(endlabelstring_
);
458 case LT_LABELSTRING_APPENDIX
: // label string appendix definition
459 lex
>> labelstring_appendix_
;
460 labelstring_appendix_
= trim(labelstring_appendix_
);
463 case LT_LABELCOUNTER
: // name of counter to use
465 counter
= trim(counter
);
468 case LT_FREE_SPACING
: // Allow for free spacing.
472 case LT_PASS_THRU
: // Allow for pass thru.
476 case LT_SPACING
: // setspace.sty
482 vector
<string
> const req
=
483 getVectorFromString(lex
.getString());
484 requires_
.insert(req
.begin(), req
.end());
500 case LT_HTMLITEMATTR
:
501 lex
>> htmlitemattr_
;
505 lex
>> htmllabeltag_
;
508 case LT_HTMLLABELATTR
:
509 lex
>> htmllabelattr_
;
512 case LT_HTMLLABELFIRST
:
513 lex
>> htmllabelfirst_
;
517 htmlstyle_
= from_utf8(lex
.getLongString("EndHTMLStyle"));
520 case LT_HTMLFORCECSS
:
521 lex
>> htmlforcecss_
;
523 case LT_HTMLPREAMBLE
:
524 htmlpreamble_
= from_utf8(lex
.getLongString("EndPreamble"));
544 LexerKeyword alignTags
[] = {
545 { "block", AT_BLOCK
},
546 { "center", AT_CENTER
},
547 { "layout", AT_LAYOUT
},
549 { "right", AT_RIGHT
}
553 void Layout::readAlign(Lexer
& lex
)
555 PushPopHelper
pph(lex
, alignTags
);
558 case Lexer::LEX_UNDEF
:
559 lex
.printError("Unknown alignment `$$Token'");
565 align
= LYX_ALIGN_BLOCK
;
568 align
= LYX_ALIGN_LEFT
;
571 align
= LYX_ALIGN_RIGHT
;
574 align
= LYX_ALIGN_CENTER
;
577 align
= LYX_ALIGN_LAYOUT
;
583 void Layout::readAlignPossible(Lexer
& lex
)
585 lex
.pushTable(alignTags
);
586 alignpossible
= LYX_ALIGN_NONE
| LYX_ALIGN_LAYOUT
;
587 int lineno
= lex
.lineNumber();
591 case Lexer::LEX_UNDEF
:
592 lex
.printError("Unknown alignment `$$Token'");
598 alignpossible
|= LYX_ALIGN_BLOCK
;
601 alignpossible
|= LYX_ALIGN_LEFT
;
604 alignpossible
|= LYX_ALIGN_RIGHT
;
607 alignpossible
|= LYX_ALIGN_CENTER
;
610 alignpossible
|= LYX_ALIGN_LAYOUT
;
613 } while (lineno
== lex
.lineNumber());
618 void Layout::readLabelType(Lexer
& lex
)
624 LA_CENTERED_TOP_ENVIRONMENT
,
634 LexerKeyword labelTypeTags
[] = {
635 { "bibliography", LA_BIBLIO
},
636 { "centered_top_environment", LA_CENTERED_TOP_ENVIRONMENT
},
637 { "counter", LA_COUNTER
},
638 { "enumerate", LA_ENUMERATE
},
639 { "itemize", LA_ITEMIZE
},
640 { "manual", LA_MANUAL
},
641 { "no_label", LA_NO_LABEL
},
642 { "sensitive", LA_SENSITIVE
},
643 { "static", LA_STATIC
},
644 { "top_environment", LA_TOP_ENVIRONMENT
}
647 PushPopHelper
pph(lex
, labelTypeTags
);
650 case Lexer::LEX_UNDEF
:
651 lex
.printError("Unknown labeltype tag `$$Token'");
657 labeltype
= LABEL_NO_LABEL
;
660 labeltype
= LABEL_MANUAL
;
662 case LA_TOP_ENVIRONMENT
:
663 labeltype
= LABEL_TOP_ENVIRONMENT
;
665 case LA_CENTERED_TOP_ENVIRONMENT
:
666 labeltype
= LABEL_CENTERED_TOP_ENVIRONMENT
;
669 labeltype
= LABEL_STATIC
;
672 labeltype
= LABEL_SENSITIVE
;
675 labeltype
= LABEL_COUNTER
;
678 labeltype
= LABEL_ENUMERATE
;
681 labeltype
= LABEL_ITEMIZE
;
684 labeltype
= LABEL_BIBLIO
;
690 void Layout::readEndLabelType(Lexer
& lex
)
692 static LexerKeyword endlabelTypeTags
[] = {
693 { "box", END_LABEL_BOX
},
694 { "filled_box", END_LABEL_FILLED_BOX
},
695 { "no_label", END_LABEL_NO_LABEL
},
696 { "static", END_LABEL_STATIC
}
699 PushPopHelper
pph(lex
, endlabelTypeTags
);
702 case Lexer::LEX_UNDEF
:
703 lex
.printError("Unknown labeltype tag `$$Token'");
705 case END_LABEL_STATIC
:
707 case END_LABEL_FILLED_BOX
:
708 case END_LABEL_NO_LABEL
:
709 endlabeltype
= static_cast<EndLabelType
>(le
);
712 LYXERR0("Unhandled value " << le
);
718 void Layout::readMargin(Lexer
& lex
)
720 LexerKeyword marginTags
[] = {
721 { "dynamic", MARGIN_DYNAMIC
},
722 { "first_dynamic", MARGIN_FIRST_DYNAMIC
},
723 { "manual", MARGIN_MANUAL
},
724 { "right_address_box", MARGIN_RIGHT_ADDRESS_BOX
},
725 { "static", MARGIN_STATIC
}
728 PushPopHelper
pph(lex
, marginTags
);
732 case Lexer::LEX_UNDEF
:
733 lex
.printError("Unknown margin type tag `$$Token'");
738 case MARGIN_FIRST_DYNAMIC
:
739 case MARGIN_RIGHT_ADDRESS_BOX
:
740 margintype
= static_cast<MarginType
>(le
);
743 LYXERR0("Unhandled value " << le
);
749 void Layout::readLatexType(Lexer
& lex
)
751 LexerKeyword latexTypeTags
[] = {
752 { "bib_environment", LATEX_BIB_ENVIRONMENT
},
753 { "command", LATEX_COMMAND
},
754 { "environment", LATEX_ENVIRONMENT
},
755 { "item_environment", LATEX_ITEM_ENVIRONMENT
},
756 { "list_environment", LATEX_LIST_ENVIRONMENT
},
757 { "paragraph", LATEX_PARAGRAPH
}
760 PushPopHelper
pph(lex
, latexTypeTags
);
763 case Lexer::LEX_UNDEF
:
764 lex
.printError("Unknown latextype tag `$$Token'");
766 case LATEX_PARAGRAPH
:
768 case LATEX_ENVIRONMENT
:
769 case LATEX_ITEM_ENVIRONMENT
:
770 case LATEX_BIB_ENVIRONMENT
:
771 case LATEX_LIST_ENVIRONMENT
:
772 latextype
= static_cast<LatexType
>(le
);
775 LYXERR0("Unhandled value " << le
);
781 void Layout::readSpacing(Lexer
& lex
)
784 ST_SPACING_SINGLE
= 1,
790 LexerKeyword spacingTags
[] = {
791 {"double", ST_SPACING_DOUBLE
},
792 {"onehalf", ST_SPACING_ONEHALF
},
793 {"other", ST_OTHER
},
794 {"single", ST_SPACING_SINGLE
}
797 PushPopHelper
pph(lex
, spacingTags
);
800 case Lexer::LEX_UNDEF
:
801 lex
.printError("Unknown spacing token `$$Token'");
806 case ST_SPACING_SINGLE
:
807 spacing
.set(Spacing::Single
);
809 case ST_SPACING_ONEHALF
:
810 spacing
.set(Spacing::Onehalf
);
812 case ST_SPACING_DOUBLE
:
813 spacing
.set(Spacing::Double
);
817 spacing
.set(Spacing::Other
, lex
.getString());
825 docstring
const i18npreamble(Language
const * lang
, docstring
const & templ
)
830 string preamble
= subst(to_utf8(templ
), "$$lang", lang
->babel());
833 // tex2lyx does not have getMessages()
834 LASSERT(false, /**/);
837 // boost::regex is not unicode-safe.
838 // Should use QRegExp or (boost::u32regex, but that requires ICU)
839 static boost::regex
const reg("_\\(([^\\)]+)\\)");
841 while (boost::regex_search(preamble
, sub
, reg
)) {
842 string
const key
= sub
.str(1);
845 translated
= to_utf8(getMessages(lang
->code()).get(key
));
847 lyxerr
<< "Warning: not translating `" << key
848 << "' because it is not pure ASCII." << endl
;
851 preamble
= subst(preamble
, sub
.str(), translated
);
854 return from_utf8(preamble
);
860 docstring
const Layout::langpreamble(Language
const * lang
) const
862 return i18npreamble(lang
, langpreamble_
);
866 docstring
const Layout::babelpreamble(Language
const * lang
) const
868 return i18npreamble(lang
, babelpreamble_
);
872 string
const & Layout::htmltag() const
874 if (htmltag_
.empty())
880 string
const & Layout::htmlattr() const
882 if (htmlattr_
.empty())
883 htmlattr_
= "class=\"" + defaultCSSClass() + "\"";
888 string
const & Layout::htmlitemtag() const
890 if (htmlitemtag_
.empty())
891 htmlitemtag_
= "div";
896 string
const & Layout::htmlitemattr() const
898 if (htmlitemattr_
.empty())
899 htmlitemattr_
= "class=\"" + defaultCSSItemClass() + "\"";
900 return htmlitemattr_
;
904 string
const & Layout::htmllabeltag() const
906 if (htmllabeltag_
.empty())
907 htmllabeltag_
= "span";
908 return htmllabeltag_
;
912 string
const & Layout::htmllabelattr() const
914 if (htmllabelattr_
.empty())
915 htmllabelattr_
= "class=\"" + defaultCSSLabelClass() + "\"";
916 return htmllabelattr_
;
920 docstring
Layout::htmlstyle() const {
921 if (!htmlstyle_
.empty() && !htmlforcecss_
)
923 if (htmldefaultstyle_
.empty())
925 docstring retval
= htmldefaultstyle_
;
926 if (!htmlstyle_
.empty())
927 retval
+= '\n' + htmlstyle_
;
932 string
Layout::defaultCSSClass() const
934 if (!defaultcssclass_
.empty())
935 return defaultcssclass_
;
937 docstring::const_iterator it
= name().begin();
938 docstring::const_iterator en
= name().end();
939 for (; it
!= en
; ++it
) {
947 // are there other characters we need to remove?
948 defaultcssclass_
= to_utf8(d
);
949 return defaultcssclass_
;
953 // NOTE There is a whole ton of stuff that could go into this.
954 // Things like bottomsep, topsep, and parsep could become various
955 // sorts of margins or padding, for example. But for now we are
956 // going to keep it simple.
957 void Layout::makeDefaultCSS() const {
958 // this never needs to be redone, since reloading layouts will
959 // wipe out what we did before.
960 if (!htmldefaultstyle_
.empty())
962 docstring
const mainfontCSS
= font
.asCSS();
963 if (!mainfontCSS
.empty())
965 from_ascii(htmltag() + "." + defaultCSSClass() + " {\n") +
966 mainfontCSS
+ from_ascii("\n}\n");
967 if (labelfont
== font
|| htmllabeltag() == "NONE")
969 docstring
const labelfontCSS
= labelfont
.asCSS();
970 if (!labelfontCSS
.empty())
972 from_ascii(htmllabeltag() + "." + defaultCSSLabelClass() + " {\n") +
973 labelfontCSS
+ from_ascii("\n}\n");
977 bool Layout::operator==(Layout
const & rhs
) const
979 // This is enough for the applications we actually make,
980 // at least at the moment. But we could check more.
981 return name() == rhs
.name()
982 && latexname() == rhs
.latexname()
983 && latextype
== rhs
.latextype
;