lyx_mirror/src/Layout.cpp
Juergen Spitzmueller 51db94b350 Fix some parsing glitches in Autonest and IsAutonestedBy
Layouts can have spaces, underlines (that must be read as spaces, and
be enquoted)

Candidate for stable

(cherry picked from commit da273a944f)
2018-12-29 18:36:20 +01:00

1697 lines
39 KiB
C++

/**
* \file Layout.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Lars Gullik Bjønnes
* \author Jean-Marc Lasgouttes
* \author André Pönitz
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "Layout.h"
#include "FontInfo.h"
#include "Language.h"
#include "Lexer.h"
#include "output_xhtml.h"
#include "TextClass.h"
#include "support/debug.h"
#include "support/lassert.h"
#include "support/lstrings.h"
#include "support/Messages.h"
#include "support/textutils.h"
using namespace std;
using namespace lyx::support;
namespace lyx {
/// Special value of toclevel for layouts that to not belong in a TOC
const int Layout::NOT_IN_TOC = -1000;
// The order of the LayoutTags enum is no more important. [asierra300396]
// Tags indexes.
enum LayoutTags {
LT_ALIGN = 1,
LT_ALIGNPOSSIBLE,
LT_ARGUMENT,
LT_AUTONESTS,
LT_AUTONESTEDBY,
LT_MARGIN,
LT_BOTTOMSEP,
LT_CATEGORY,
LT_COMMANDDEPTH,
LT_COPYSTYLE,
LT_DEPENDSON,
LT_OBSOLETEDBY,
LT_END,
LT_FONT,
LT_FREE_SPACING,
LT_PASS_THRU,
LT_PASS_THRU_CHARS,
LT_PARBREAK_IS_NEWLINE,
LT_ITEMCOMMAND,
LT_ITEMSEP,
LT_KEEPEMPTY,
LT_LABEL_BOTTOMSEP,
LT_LABELFONT,
LT_TEXTFONT,
LT_LABELINDENT,
LT_LABELSEP,
LT_LABELSTRING,
LT_LABELSTRING_APPENDIX,
LT_LABELCOUNTER,
LT_LABELTYPE,
LT_ENDLABELSTRING,
LT_ENDLABELTYPE,
LT_LATEXNAME,
LT_LATEXPARAM,
LT_LATEXTYPE,
LT_LEFTDELIM,
LT_LEFTMARGIN,
LT_NEED_PROTECT,
LT_NEWLINE,
LT_NEXTNOINDENT,
LT_PAR_GROUP,
LT_PARINDENT,
LT_PARSEP,
LT_PARSKIP,
LT_PREAMBLE,
LT_LANGPREAMBLE,
LT_BABELPREAMBLE,
LT_REQUIRES,
LT_RIGHTMARGIN,
LT_SPACING,
LT_TOPSEP,
LT_TOCLEVEL,
LT_INNERTAG,
LT_LABELTAG,
LT_ITEMTAG,
LT_HTMLTAG,
LT_HTMLATTR,
LT_HTMLITEM,
LT_HTMLITEMATTR,
LT_HTMLLABEL,
LT_HTMLLABELATTR,
LT_HTMLLABELFIRST,
LT_HTMLPREAMBLE,
LT_HTMLSTYLE,
LT_HTMLFORCECSS,
LT_INPREAMBLE,
LT_HTMLTITLE,
LT_SPELLCHECK,
LT_REFPREFIX,
LT_RESETARGS,
LT_RESUMECOUNTER,
LT_STEPMASTERCOUNTER,
LT_RIGHTDELIM,
LT_FORCELOCAL,
LT_TOGGLE_INDENT,
LT_ADDTOTOC,
LT_ISTOCCAPTION,
LT_INTITLE // keep this last!
};
/////////////////////
Layout::Layout()
: add_to_toc_(false), is_toc_caption_(true)
{
unknown_ = false;
margintype = MARGIN_STATIC;
latextype = LATEX_PARAGRAPH;
resumecounter = false;
stepmastercounter = false;
intitle = false;
inpreamble = false;
needprotect = false;
keepempty = false;
font = inherit_font;
labelfont = inherit_font;
resfont = sane_font;
reslabelfont = sane_font;
nextnoindent = false;
parskip = 0.0;
itemsep = 0;
topsep = 0.0;
bottomsep = 0.0;
labelbottomsep = 0.0;
parsep = 0;
align = LYX_ALIGN_BLOCK;
alignpossible = LYX_ALIGN_NONE | LYX_ALIGN_LAYOUT;
labeltype = LABEL_NO_LABEL;
endlabeltype = END_LABEL_NO_LABEL;
// Should or should not. That is the question.
// spacing.set(Spacing::OneHalf);
newline_allowed = true;
free_spacing = false;
pass_thru = false;
parbreak_is_newline = false;
toclevel = NOT_IN_TOC;
commanddepth = 0;
htmllabelfirst_ = false;
htmlforcecss_ = false;
htmltitle_ = false;
spellcheck = true;
forcelocal = 0;
itemcommand_ = "item";
toggle_indent = ITOGGLE_DOCUMENT_DEFAULT;
par_group_ = false;
}
bool Layout::read(Lexer & lex, TextClass const & tclass)
{
// If this is an empty layout, or if no force local version is set,
// we know that we will not discard the stuff to read
if (forcelocal == 0)
return readIgnoreForcelocal(lex, tclass);
Layout tmp(*this);
tmp.forcelocal = 0;
bool const ret = tmp.readIgnoreForcelocal(lex, tclass);
// Keep the stuff if
// - the read version is higher
// - both versions are infinity (arbitrary decision)
// - the file did not contain any local version (needed for not
// skipping user defined local layouts)
if (tmp.forcelocal <= 0 || tmp.forcelocal > forcelocal)
*this = tmp;
return ret;
}
bool Layout::readIgnoreForcelocal(Lexer & lex, TextClass const & tclass)
{
// This table is sorted alphabetically [asierra 30March96]
LexerKeyword layoutTags[] = {
{ "addtotoc", LT_ADDTOTOC },
{ "align", LT_ALIGN },
{ "alignpossible", LT_ALIGNPOSSIBLE },
{ "argument", LT_ARGUMENT },
{ "autonests", LT_AUTONESTS },
{ "babelpreamble", LT_BABELPREAMBLE },
{ "bottomsep", LT_BOTTOMSEP },
{ "category", LT_CATEGORY },
{ "commanddepth", LT_COMMANDDEPTH },
{ "copystyle", LT_COPYSTYLE },
{ "dependson", LT_DEPENDSON },
{ "end", LT_END },
{ "endlabelstring", LT_ENDLABELSTRING },
{ "endlabeltype", LT_ENDLABELTYPE },
{ "font", LT_FONT },
{ "forcelocal", LT_FORCELOCAL },
{ "freespacing", LT_FREE_SPACING },
{ "htmlattr", LT_HTMLATTR },
{ "htmlforcecss", LT_HTMLFORCECSS },
{ "htmlitem", LT_HTMLITEM },
{ "htmlitemattr", LT_HTMLITEMATTR },
{ "htmllabel", LT_HTMLLABEL },
{ "htmllabelattr", LT_HTMLLABELATTR },
{ "htmllabelfirst", LT_HTMLLABELFIRST },
{ "htmlpreamble", LT_HTMLPREAMBLE },
{ "htmlstyle", LT_HTMLSTYLE },
{ "htmltag", LT_HTMLTAG },
{ "htmltitle", LT_HTMLTITLE },
{ "innertag", LT_INNERTAG },
{ "inpreamble", LT_INPREAMBLE },
{ "intitle", LT_INTITLE },
{ "isautonestedby", LT_AUTONESTEDBY },
{ "istoccaption", LT_ISTOCCAPTION },
{ "itemcommand", LT_ITEMCOMMAND },
{ "itemsep", LT_ITEMSEP },
{ "itemtag", LT_ITEMTAG },
{ "keepempty", LT_KEEPEMPTY },
{ "labelbottomsep", LT_LABEL_BOTTOMSEP },
{ "labelcounter", LT_LABELCOUNTER },
{ "labelfont", LT_LABELFONT },
{ "labelindent", LT_LABELINDENT },
{ "labelsep", LT_LABELSEP },
{ "labelstring", LT_LABELSTRING },
{ "labelstringappendix", LT_LABELSTRING_APPENDIX },
{ "labeltag", LT_LABELTAG },
{ "labeltype", LT_LABELTYPE },
{ "langpreamble", LT_LANGPREAMBLE },
{ "latexname", LT_LATEXNAME },
{ "latexparam", LT_LATEXPARAM },
{ "latextype", LT_LATEXTYPE },
{ "leftdelim", LT_LEFTDELIM },
{ "leftmargin", LT_LEFTMARGIN },
{ "margin", LT_MARGIN },
{ "needprotect", LT_NEED_PROTECT },
{ "newline", LT_NEWLINE },
{ "nextnoindent", LT_NEXTNOINDENT },
{ "obsoletedby", LT_OBSOLETEDBY },
{ "paragraphgroup", LT_PAR_GROUP },
{ "parbreakisnewline", LT_PARBREAK_IS_NEWLINE },
{ "parindent", LT_PARINDENT },
{ "parsep", LT_PARSEP },
{ "parskip", LT_PARSKIP },
{ "passthru", LT_PASS_THRU },
{ "passthruchars", LT_PASS_THRU_CHARS },
{ "preamble", LT_PREAMBLE },
{ "refprefix", LT_REFPREFIX },
{ "requires", LT_REQUIRES },
{ "resetargs", LT_RESETARGS },
{ "resumecounter", LT_RESUMECOUNTER },
{ "rightdelim", LT_RIGHTDELIM },
{ "rightmargin", LT_RIGHTMARGIN },
{ "spacing", LT_SPACING },
{ "spellcheck", LT_SPELLCHECK },
{ "stepmastercounter", LT_STEPMASTERCOUNTER },
{ "textfont", LT_TEXTFONT },
{ "toclevel", LT_TOCLEVEL },
{ "toggleindent", LT_TOGGLE_INDENT },
{ "topsep", LT_TOPSEP }
};
bool error = false;
bool finished = false;
lex.pushTable(layoutTags);
// parse style section
while (!finished && lex.isOK() && !error) {
int le = lex.lex();
// See comment in LyXRC.cpp.
switch (le) {
case Lexer::LEX_FEOF:
continue;
case Lexer::LEX_UNDEF:
// parse error
lex.printError("Unknown layout tag `$$Token'");
error = true;
continue;
default:
break;
}
switch (static_cast<LayoutTags>(le)) {
case LT_END:
finished = true;
break;
case LT_CATEGORY:
lex >> category_;
break;
case LT_COPYSTYLE: {
docstring style;
lex >> style;
style = subst(style, '_', ' ');
if (tclass.hasLayout(style)) {
docstring const tmpname = name_;
this->operator=(tclass[style]);
name_ = tmpname;
} else {
LYXERR0("Cannot copy unknown style `"
<< style << "'\n"
<< "All layouts so far:");
DocumentClass::const_iterator lit = tclass.begin();
DocumentClass::const_iterator len = tclass.end();
for (; lit != len; ++lit)
LYXERR0(lit->name());
}
break;
}
case LT_OBSOLETEDBY: {
docstring style;
lex >> style;
style = subst(style, '_', ' ');
if (tclass.hasLayout(style)) {
docstring const tmpname = name_;
this->operator=(tclass[style]);
name_ = tmpname;
if (obsoleted_by().empty())
obsoleted_by_ = style;
} else {
LYXERR0("Cannot replace with unknown style `"
<< style << '\'');
//lex.printError("Cannot replace with"
// " unknown style "
// "`$$Token'");
}
break;
}
case LT_DEPENDSON:
lex >> depends_on_;
depends_on_ = subst(depends_on_, '_', ' ');
break;
case LT_MARGIN:
readMargin(lex);
break;
case LT_LATEXTYPE:
readLatexType(lex);
break;
case LT_INTITLE:
lex >> intitle;
break;
case LT_INPREAMBLE:
lex >> inpreamble;
break;
case LT_TOCLEVEL:
lex >> toclevel;
break;
case LT_RESETARGS:
bool reset;
lex >> reset;
if (reset) {
latexargs_.clear();
itemargs_.clear();
postcommandargs_.clear();
}
break;
case LT_RESUMECOUNTER:
lex >> resumecounter;
break;
case LT_STEPMASTERCOUNTER:
lex >> stepmastercounter;
break;
case LT_ARGUMENT:
readArgument(lex);
break;
case LT_NEED_PROTECT:
lex >> needprotect;
break;
case LT_KEEPEMPTY:
lex >> keepempty;
break;
case LT_FONT:
font = lyxRead(lex, font);
labelfont = font;
break;
case LT_TEXTFONT:
font = lyxRead(lex, font);
break;
case LT_LABELFONT:
labelfont = lyxRead(lex, labelfont);
break;
case LT_NEXTNOINDENT:
lex >> nextnoindent;
break;
case LT_TOGGLE_INDENT: {
string tog;
lex >> tog;
tog = support::ascii_lowercase(tog);
if (tog == "always")
toggle_indent = ITOGGLE_ALWAYS;
else if (tog == "never")
toggle_indent = ITOGGLE_NEVER;
else
toggle_indent = ITOGGLE_DOCUMENT_DEFAULT;
break;
}
case LT_COMMANDDEPTH:
lex >> commanddepth;
break;
case LT_LATEXNAME:
lex >> latexname_;
break;
case LT_LATEXPARAM:
lex >> latexparam_;
latexparam_ = subst(latexparam_, "&quot;", "\"");
break;
case LT_LEFTDELIM:
lex >> leftdelim_;
leftdelim_ = support::subst(leftdelim_, from_ascii("<br/>"),
from_ascii("\n"));
break;
case LT_RIGHTDELIM:
lex >> rightdelim_;
rightdelim_ = support::subst(rightdelim_, from_ascii("<br/>"),
from_ascii("\n"));
break;
case LT_INNERTAG:
lex >> innertag_;
break;
case LT_LABELTAG:
lex >> labeltag_;
break;
case LT_ITEMTAG:
lex >> itemtag_;
break;
case LT_ITEMCOMMAND:
lex >> itemcommand_;
break;
case LT_PREAMBLE:
preamble_ = lex.getLongString(from_ascii("EndPreamble"));
break;
case LT_LANGPREAMBLE:
langpreamble_ = lex.getLongString(from_ascii("EndLangPreamble"));
break;
case LT_BABELPREAMBLE:
babelpreamble_ = lex.getLongString(from_ascii("EndBabelPreamble"));
break;
case LT_LABELTYPE:
readLabelType(lex);
break;
case LT_ENDLABELTYPE:
readEndLabelType(lex);
break;
case LT_LEFTMARGIN:
lex >> leftmargin;
break;
case LT_RIGHTMARGIN:
lex >> rightmargin;
break;
case LT_LABELINDENT:
lex >> labelindent;
break;
case LT_PARINDENT:
lex >> parindent;
break;
case LT_PARSKIP:
lex >> parskip;
break;
case LT_ITEMSEP:
lex >> itemsep;
break;
case LT_TOPSEP:
lex >> topsep;
break;
case LT_BOTTOMSEP:
lex >> bottomsep;
break;
case LT_LABEL_BOTTOMSEP:
lex >> labelbottomsep;
break;
case LT_LABELSEP:
lex >> labelsep;
labelsep = subst(labelsep, 'x', ' ');
break;
case LT_PARSEP:
lex >> parsep;
break;
case LT_NEWLINE:
lex >> newline_allowed;
break;
case LT_ALIGN:
readAlign(lex);
break;
case LT_ALIGNPOSSIBLE:
readAlignPossible(lex);
break;
case LT_LABELSTRING:
// FIXME: this means LT_LABELSTRING_APPENDIX may only
// occur after LT_LABELSTRING
lex >> labelstring_;
labelstring_appendix_ = labelstring_;
break;
case LT_ENDLABELSTRING:
lex >> endlabelstring_;
break;
case LT_LABELSTRING_APPENDIX:
lex >> labelstring_appendix_;
break;
case LT_LABELCOUNTER:
lex >> counter;
break;
case LT_PAR_GROUP:
lex >> par_group_;
break;
case LT_FREE_SPACING:
lex >> free_spacing;
break;
case LT_PASS_THRU:
lex >> pass_thru;
break;
case LT_PASS_THRU_CHARS:
lex >> pass_thru_chars;
break;
case LT_PARBREAK_IS_NEWLINE:
lex >> parbreak_is_newline;
break;
case LT_SPACING:
readSpacing(lex);
break;
case LT_REQUIRES: {
lex.eatLine();
vector<string> const req =
getVectorFromString(lex.getString(true));
requires_.insert(req.begin(), req.end());
break;
}
case LT_AUTONESTS: {
docstring autonest = lex.getLongString(from_ascii("EndAutoNests"));
autonest = subst(autonest, from_ascii("\n"), docstring());
autonest = subst(autonest, from_ascii("\t"), docstring());
autonest = subst(autonest, from_ascii("\""), docstring());
autonest = subst(autonest, '_', ' ');
vector<docstring> const autonests =
getVectorFromString(autonest, from_ascii(","), false, true);
autonests_.insert(autonests.begin(), autonests.end());
break;
}
case LT_AUTONESTEDBY: {
docstring autonest = lex.getLongString(from_ascii("EndIsAutoNestedBy"));
autonest = subst(autonest, from_ascii("\n"), docstring());
autonest = subst(autonest, from_ascii("\t"), docstring());
autonest = subst(autonest, from_ascii("\""), docstring());
autonest = subst(autonest, '_', ' ');
vector<docstring> const autonests =
getVectorFromString(autonest, from_ascii(","), false, true);
autonested_by_.insert(autonests.begin(), autonests.end());
break;
}
case LT_REFPREFIX: {
docstring arg;
lex >> arg;
if (arg == "OFF")
refprefix.clear();
else
refprefix = arg;
break;
}
case LT_HTMLTAG:
lex >> htmltag_;
break;
case LT_HTMLATTR:
lex >> htmlattr_;
break;
case LT_HTMLITEM:
lex >> htmlitemtag_;
break;
case LT_HTMLITEMATTR:
lex >> htmlitemattr_;
break;
case LT_HTMLLABEL:
lex >> htmllabeltag_;
break;
case LT_HTMLLABELATTR:
lex >> htmllabelattr_;
break;
case LT_HTMLLABELFIRST:
lex >> htmllabelfirst_;
break;
case LT_HTMLSTYLE:
htmlstyle_ = lex.getLongString(from_ascii("EndHTMLStyle"));
break;
case LT_HTMLFORCECSS:
lex >> htmlforcecss_;
break;
case LT_HTMLPREAMBLE:
htmlpreamble_ = lex.getLongString(from_ascii("EndPreamble"));
break;
case LT_HTMLTITLE:
lex >> htmltitle_;
break;
case LT_SPELLCHECK:
lex >> spellcheck;
break;
case LT_FORCELOCAL:
lex >> forcelocal;
break;
case LT_ADDTOTOC:
lex >> toc_type_;
add_to_toc_ = !toc_type_.empty();
break;
case LT_ISTOCCAPTION:
lex >> is_toc_caption_;
break;
}
}
lex.popTable();
// make sure we only have inpreamble = true for commands
if (inpreamble && latextype != LATEX_COMMAND && latextype != LATEX_PARAGRAPH) {
LYXERR0("InPreamble not permitted except with command and paragraph layouts.");
LYXERR0("Layout name: " << name());
inpreamble = false;
}
return finished && !error;
}
enum {
AT_BLOCK = 1,
AT_LEFT,
AT_RIGHT,
AT_CENTER,
AT_LAYOUT
};
LexerKeyword alignTags[] = {
{ "block", AT_BLOCK },
{ "center", AT_CENTER },
{ "layout", AT_LAYOUT },
{ "left", AT_LEFT },
{ "right", AT_RIGHT }
};
void Layout::readAlign(Lexer & lex)
{
PushPopHelper pph(lex, alignTags);
int le = lex.lex();
switch (le) {
case Lexer::LEX_UNDEF:
lex.printError("Unknown alignment `$$Token'");
return;
default: break;
};
switch (le) {
case AT_BLOCK:
align = LYX_ALIGN_BLOCK;
break;
case AT_LEFT:
align = LYX_ALIGN_LEFT;
break;
case AT_RIGHT:
align = LYX_ALIGN_RIGHT;
break;
case AT_CENTER:
align = LYX_ALIGN_CENTER;
break;
case AT_LAYOUT:
align = LYX_ALIGN_LAYOUT;
break;
}
}
void Layout::readAlignPossible(Lexer & lex)
{
lex.pushTable(alignTags);
alignpossible = LYX_ALIGN_NONE | LYX_ALIGN_LAYOUT;
int lineno = lex.lineNumber();
do {
int le = lex.lex();
switch (le) {
case Lexer::LEX_UNDEF:
lex.printError("Unknown alignment `$$Token'");
continue;
default: break;
};
switch (le) {
case AT_BLOCK:
alignpossible |= LYX_ALIGN_BLOCK;
break;
case AT_LEFT:
alignpossible |= LYX_ALIGN_LEFT;
break;
case AT_RIGHT:
alignpossible |= LYX_ALIGN_RIGHT;
break;
case AT_CENTER:
alignpossible |= LYX_ALIGN_CENTER;
break;
case AT_LAYOUT:
alignpossible |= LYX_ALIGN_LAYOUT;
break;
}
} while (lineno == lex.lineNumber());
lex.popTable();
}
void Layout::readLabelType(Lexer & lex)
{
enum {
LA_NO_LABEL = 1,
LA_MANUAL,
LA_ABOVE,
LA_CENTERED,
LA_STATIC,
LA_SENSITIVE,
LA_ENUMERATE,
LA_ITEMIZE,
LA_BIBLIO
};
LexerKeyword labelTypeTags[] = {
{ "above", LA_ABOVE },
{ "bibliography", LA_BIBLIO },
{ "centered", LA_CENTERED },
{ "enumerate", LA_ENUMERATE },
{ "itemize", LA_ITEMIZE },
{ "manual", LA_MANUAL },
{ "no_label", LA_NO_LABEL },
{ "sensitive", LA_SENSITIVE },
{ "static", LA_STATIC }
};
PushPopHelper pph(lex, labelTypeTags);
int le = lex.lex();
switch (le) {
case Lexer::LEX_UNDEF:
lex.printError("Unknown labeltype tag `$$Token'");
return;
default: break;
}
switch (le) {
case LA_NO_LABEL:
labeltype = LABEL_NO_LABEL;
break;
case LA_MANUAL:
labeltype = LABEL_MANUAL;
break;
case LA_ABOVE:
labeltype = LABEL_ABOVE;
break;
case LA_CENTERED:
labeltype = LABEL_CENTERED;
break;
case LA_STATIC:
labeltype = LABEL_STATIC;
break;
case LA_SENSITIVE:
labeltype = LABEL_SENSITIVE;
break;
case LA_ENUMERATE:
labeltype = LABEL_ENUMERATE;
break;
case LA_ITEMIZE:
labeltype = LABEL_ITEMIZE;
break;
case LA_BIBLIO:
labeltype = LABEL_BIBLIO;
break;
}
}
void Layout::readEndLabelType(Lexer & lex)
{
// this should be const, but can't be because
// of PushPopHelper.
static LexerKeyword endlabelTypeTags[] = {
{ "box", END_LABEL_BOX },
{ "filled_box", END_LABEL_FILLED_BOX },
{ "no_label", END_LABEL_NO_LABEL },
{ "static", END_LABEL_STATIC }
};
PushPopHelper pph(lex, endlabelTypeTags);
int le = lex.lex();
switch (le) {
case Lexer::LEX_UNDEF:
lex.printError("Unknown labeltype tag `$$Token'");
break;
case END_LABEL_STATIC:
case END_LABEL_BOX:
case END_LABEL_FILLED_BOX:
case END_LABEL_NO_LABEL:
endlabeltype = static_cast<EndLabelType>(le);
break;
default:
LYXERR0("Unhandled value " << le);
break;
}
}
void Layout::readMargin(Lexer & lex)
{
LexerKeyword marginTags[] = {
{ "dynamic", MARGIN_DYNAMIC },
{ "first_dynamic", MARGIN_FIRST_DYNAMIC },
{ "manual", MARGIN_MANUAL },
{ "right_address_box", MARGIN_RIGHT_ADDRESS_BOX },
{ "static", MARGIN_STATIC }
};
PushPopHelper pph(lex, marginTags);
int le = lex.lex();
switch (le) {
case Lexer::LEX_UNDEF:
lex.printError("Unknown margin type tag `$$Token'");
return;
case MARGIN_STATIC:
case MARGIN_MANUAL:
case MARGIN_DYNAMIC:
case MARGIN_FIRST_DYNAMIC:
case MARGIN_RIGHT_ADDRESS_BOX:
margintype = static_cast<MarginType>(le);
break;
default:
LYXERR0("Unhandled value " << le);
break;
}
}
void Layout::readLatexType(Lexer & lex)
{
LexerKeyword latexTypeTags[] = {
{ "bib_environment", LATEX_BIB_ENVIRONMENT },
{ "command", LATEX_COMMAND },
{ "environment", LATEX_ENVIRONMENT },
{ "item_environment", LATEX_ITEM_ENVIRONMENT },
{ "list_environment", LATEX_LIST_ENVIRONMENT },
{ "paragraph", LATEX_PARAGRAPH }
};
PushPopHelper pph(lex, latexTypeTags);
int le = lex.lex();
switch (le) {
case Lexer::LEX_UNDEF:
lex.printError("Unknown latextype tag `$$Token'");
return;
case LATEX_PARAGRAPH:
case LATEX_COMMAND:
case LATEX_ITEM_ENVIRONMENT:
case LATEX_LIST_ENVIRONMENT:
latextype = static_cast<LatexType>(le);
break;
case LATEX_ENVIRONMENT:
case LATEX_BIB_ENVIRONMENT:
latextype = static_cast<LatexType>(le);
par_group_ = true;
break;
default:
LYXERR0("Unhandled value " << le);
break;
}
}
void Layout::readSpacing(Lexer & lex)
{
enum {
ST_SPACING_SINGLE = 1,
ST_SPACING_ONEHALF,
ST_SPACING_DOUBLE,
ST_OTHER
};
LexerKeyword spacingTags[] = {
{"double", ST_SPACING_DOUBLE },
{"onehalf", ST_SPACING_ONEHALF },
{"other", ST_OTHER },
{"single", ST_SPACING_SINGLE }
};
PushPopHelper pph(lex, spacingTags);
int le = lex.lex();
switch (le) {
case Lexer::LEX_UNDEF:
lex.printError("Unknown spacing token `$$Token'");
return;
default: break;
}
switch (le) {
case ST_SPACING_SINGLE:
spacing.set(Spacing::Single);
break;
case ST_SPACING_ONEHALF:
spacing.set(Spacing::Onehalf);
break;
case ST_SPACING_DOUBLE:
spacing.set(Spacing::Double);
break;
case ST_OTHER:
lex.next();
spacing.set(Spacing::Other, lex.getString());
break;
}
}
void Layout::readArgument(Lexer & lex)
{
latexarg arg;
// writeArgument() makes use of these default values
arg.mandatory = false;
arg.autoinsert = false;
arg.insertcotext = false;
bool error = false;
bool finished = false;
arg.font = inherit_font;
arg.labelfont = inherit_font;
arg.is_toc_caption = false;
arg.passthru = PT_INHERITED;
string id;
lex >> id;
bool const itemarg = prefixIs(id, "item:");
bool const postcmd = prefixIs(id, "post:");
while (!finished && lex.isOK() && !error) {
lex.next();
string const tok = ascii_lowercase(lex.getString());
if (tok.empty()) {
continue;
} else if (tok == "endargument") {
finished = true;
} else if (tok == "labelstring") {
lex.next();
arg.labelstring = lex.getDocString();
} else if (tok == "menustring") {
lex.next();
arg.menustring = lex.getDocString();
} else if (tok == "mandatory") {
lex.next();
arg.mandatory = lex.getBool();
} else if (tok == "autoinsert") {
lex.next();
arg.autoinsert = lex.getBool();
} else if (tok == "insertcotext") {
lex.next();
arg.insertcotext = lex.getBool();
} else if (tok == "leftdelim") {
lex.next();
arg.ldelim = lex.getDocString();
arg.ldelim = support::subst(arg.ldelim, from_ascii("<br/>"),
from_ascii("\n"));
} else if (tok == "rightdelim") {
lex.next();
arg.rdelim = lex.getDocString();
arg.rdelim = support::subst(arg.rdelim, from_ascii("<br/>"),
from_ascii("\n"));
} else if (tok == "defaultarg") {
lex.next();
arg.defaultarg = lex.getDocString();
} else if (tok == "presetarg") {
lex.next();
arg.presetarg = lex.getDocString();
} else if (tok == "tooltip") {
lex.next();
arg.tooltip = lex.getDocString();
} else if (tok == "requires") {
lex.next();
arg.requires = lex.getString();
} else if (tok == "decoration") {
lex.next();
arg.decoration = lex.getString();
} else if (tok == "font") {
arg.font = lyxRead(lex, arg.font);
} else if (tok == "labelfont") {
arg.labelfont = lyxRead(lex, arg.labelfont);
} else if (tok == "passthruchars") {
lex.next();
arg.pass_thru_chars = lex.getDocString();
} else if (tok == "passthru") {
lex.next();
docstring value = lex.getDocString();
if (value == "true" || value == "1")
arg.passthru = PT_TRUE;
else if (value == "false" || value == "0")
arg.passthru = PT_FALSE;
else
arg.passthru = PT_INHERITED;
} else if (tok == "istoccaption") {
lex.next();
arg.is_toc_caption = lex.getBool();
} else {
lex.printError("Unknown tag");
error = true;
}
}
if (arg.labelstring.empty())
LYXERR0("Incomplete Argument definition!");
else if (itemarg)
itemargs_[id] = arg;
else if (postcmd)
postcommandargs_[id] = arg;
else
latexargs_[id] = arg;
}
void writeArgument(ostream & os, string const & id, Layout::latexarg const & arg)
{
os << "\tArgument " << id << '\n';
if (!arg.labelstring.empty())
os << "\t\tLabelString \"" << to_utf8(arg.labelstring) << "\"\n";
if (!arg.menustring.empty())
os << "\t\tMenuString \"" << to_utf8(arg.menustring) << "\"\n";
if (arg.mandatory)
os << "\t\tMandatory " << arg.mandatory << '\n';
if (arg.autoinsert)
os << "\t\tAutoinsert " << arg.autoinsert << '\n';
if (arg.insertcotext)
os << "\t\tInsertCotext " << arg.insertcotext << '\n';
if (!arg.ldelim.empty())
os << "\t\tLeftDelim \""
<< to_utf8(subst(arg.ldelim, from_ascii("\n"), from_ascii("<br/>")))
<< "\"\n";
if (!arg.rdelim.empty())
os << "\t\tRightDelim \""
<< to_utf8(subst(arg.rdelim, from_ascii("\n"), from_ascii("<br/>")))
<< "\"\n";
if (!arg.defaultarg.empty())
os << "\t\tDefaultArg \"" << to_utf8(arg.defaultarg) << "\"\n";
if (!arg.presetarg.empty())
os << "\t\tPresetArg \"" << to_utf8(arg.presetarg) << "\"\n";
if (!arg.tooltip.empty())
os << "\t\tToolTip \"" << to_utf8(arg.tooltip) << "\"\n";
if (!arg.requires.empty())
os << "\t\tRequires \"" << arg.requires << "\"\n";
if (!arg.decoration.empty())
os << "\t\tDecoration \"" << arg.decoration << "\"\n";
if (arg.font != inherit_font)
lyxWrite(os, arg.font, "Font", 2);
if (arg.labelfont != inherit_font)
lyxWrite(os, arg.labelfont, "LabelFont", 2);
switch (arg.passthru) {
case PT_TRUE:
os << "\t\tPassThru true\n";
break;
case PT_FALSE:
os << "\t\tPassThru false\n";
break;
case PT_INHERITED:
os << "\t\tPassThru inherited\n";
break;
}
if (!arg.pass_thru_chars.empty())
os << "\t\tPassThruChars \"" << to_utf8(arg.pass_thru_chars) << "\"\n";
os << "\tEndArgument\n";
}
void Layout::write(ostream & os) const
{
os << "Style \"" << to_utf8(name_) << "\"\n";
if (!category_.empty() && obsoleted_by_.empty())
os << "\tCategory \"" << to_utf8(category_) << "\"\n";
// Can't deduce Copystyle here :-(
if (!obsoleted_by_.empty()) {
os << "\tObsoletedBy \"" << to_utf8(obsoleted_by_)
<< "\"\nEnd\n";
return;
}
if (!depends_on_.empty())
os << "\tDependsOn " << to_utf8(depends_on_) << '\n';
switch (margintype) {
case MARGIN_DYNAMIC:
os << "\tMargin Dynamic\n";
break;
case MARGIN_FIRST_DYNAMIC:
os << "\tMargin First_Dynamic\n";
break;
case MARGIN_MANUAL:
os << "\tMargin Manual\n";
break;
case MARGIN_RIGHT_ADDRESS_BOX:
os << "\tMargin Right_Address_Box\n";
break;
case MARGIN_STATIC:
os << "\tMargin Static\n";
break;
}
switch (latextype) {
case LATEX_BIB_ENVIRONMENT:
os << "\tLatexType Bib_Environment\n";
break;
case LATEX_COMMAND:
os << "\tLatexType Command\n";
break;
case LATEX_ENVIRONMENT:
os << "\tLatexType Environment\n";
break;
case LATEX_ITEM_ENVIRONMENT:
os << "\tLatexType Item_Environment\n";
break;
case LATEX_LIST_ENVIRONMENT:
os << "\tLatexType List_Environment\n";
break;
case LATEX_PARAGRAPH:
os << "\tLatexType Paragraph\n";
break;
}
os << "\tInTitle " << intitle << "\n"
"\tInPreamble " << inpreamble << "\n"
"\tTocLevel " << toclevel << "\n"
"\tResumeCounter " << resumecounter << "\n"
"\tStepMasterCounter " << stepmastercounter << '\n';
// ResetArgs does not make sense here
for (LaTeXArgMap::const_iterator it = latexargs_.begin();
it != latexargs_.end(); ++it)
writeArgument(os, it->first, it->second);
for (LaTeXArgMap::const_iterator it = itemargs_.begin();
it != itemargs_.end(); ++it)
writeArgument(os, it->first, it->second);
for (LaTeXArgMap::const_iterator it = postcommandargs_.begin();
it != postcommandargs_.end(); ++it)
writeArgument(os, it->first, it->second);
os << "\tNeedProtect " << needprotect << "\n"
"\tKeepEmpty " << keepempty << '\n';
if (labelfont == font)
lyxWrite(os, font, "Font", 1);
else {
lyxWrite(os, font, "TextFont", 1);
lyxWrite(os, labelfont, "LabelFont", 1);
}
os << "\tNextNoIndent " << nextnoindent << "\n"
"\tCommandDepth " << commanddepth << '\n';
if (!latexname_.empty())
os << "\tLatexName \"" << latexname_ << "\"\n";
if (!latexparam_.empty())
os << "\tLatexParam \"" << subst(latexparam_, "\"", "&quot;")
<< "\"\n";
if (!leftdelim_.empty())
os << "\tLeftDelim "
<< to_utf8(subst(leftdelim_, from_ascii("\n"), from_ascii("<br/>")))
<< '\n';
if (!rightdelim_.empty())
os << "\tRightDelim "
<< to_utf8(subst(rightdelim_, from_ascii("\n"), from_ascii("<br/>")))
<< '\n';
if (!innertag_.empty())
os << "\tInnerTag \"" << innertag_ << "\"\n";
if (!labeltag_.empty())
os << "\tLabelTag \"" << labeltag_ << "\"\n";
if (!itemtag_.empty())
os << "\tItemTag \"" << itemtag_ << "\"\n";
if (!itemcommand_.empty())
os << "\tItemCommand " << itemcommand_ << '\n';
if (!preamble_.empty())
os << "\tPreamble\n\t"
<< to_utf8(subst(rtrim(preamble_, "\n"),
from_ascii("\n"), from_ascii("\n\t")))
<< "\n\tEndPreamble\n";
if (!langpreamble_.empty())
os << "\tLangPreamble\n\t"
<< to_utf8(subst(rtrim(langpreamble_, "\n"),
from_ascii("\n"), from_ascii("\n\t")))
<< "\n\tEndLangPreamble\n";
if (!babelpreamble_.empty())
os << "\tBabelPreamble\n\t"
<< to_utf8(subst(rtrim(babelpreamble_, "\n"),
from_ascii("\n"), from_ascii("\n\t")))
<< "\n\tEndBabelPreamble\n";
switch (labeltype) {
case LABEL_ABOVE:
os << "\tLabelType Above\n";
break;
case LABEL_BIBLIO:
os << "\tLabelType Bibliography\n";
break;
case LABEL_CENTERED:
os << "\tLabelType Centered\n";
break;
case LABEL_ENUMERATE:
os << "\tLabelType Enumerate\n";
break;
case LABEL_ITEMIZE:
os << "\tLabelType Itemize\n";
break;
case LABEL_MANUAL:
os << "\tLabelType Manual\n";
break;
case LABEL_NO_LABEL:
os << "\tLabelType No_Label\n";
break;
case LABEL_SENSITIVE:
os << "\tLabelType Sensitive\n";
break;
case LABEL_STATIC:
os << "\tLabelType Static\n";
break;
}
switch (endlabeltype) {
case END_LABEL_BOX:
os << "\tEndLabelType Box\n";
break;
case END_LABEL_FILLED_BOX:
os << "\tEndLabelType Filled_Box\n";
break;
case END_LABEL_NO_LABEL:
os << "\tEndLabelType No_Label\n";
break;
case END_LABEL_STATIC:
os << "\tEndLabelType Static\n";
break;
}
os << "\tParagraphGroup \"" << par_group_ << "\"\n";
if (!leftmargin.empty())
os << "\tLeftMargin \"" << to_utf8(leftmargin) << "\"\n";
if (!rightmargin.empty())
os << "\tRightMargin \"" << to_utf8(rightmargin) << "\"\n";
if (!labelindent.empty())
os << "\tLabelIndent " << to_utf8(labelindent) << '\n';
if (!parindent.empty())
os << "\tParIndent " << to_utf8(parindent) << '\n';
os << "\tParSkip " << parskip << "\n"
"\tItemSep " << itemsep << "\n"
"\tTopSep " << topsep << "\n"
"\tBottomSep " << bottomsep << "\n"
"\tLabelBottomSep " << labelbottomsep << '\n';
if (!labelsep.empty())
os << "\tLabelSep " << to_utf8(subst(labelsep, ' ', 'x'))
<< '\n';
os << "\tParSep " << parsep << "\n"
"\tNewLine " << newline_allowed << '\n';
switch (align) {
case LYX_ALIGN_BLOCK:
os << "\tAlign Block\n";
break;
case LYX_ALIGN_CENTER:
os << "\tAlign Center\n";
break;
case LYX_ALIGN_LAYOUT:
os << "\tAlign Layout\n";
break;
case LYX_ALIGN_LEFT:
os << "\tAlign Left\n";
break;
case LYX_ALIGN_RIGHT:
os << "\tAlign Right\n";
break;
case LYX_ALIGN_DECIMAL:
case LYX_ALIGN_SPECIAL:
case LYX_ALIGN_NONE:
break;
}
if (alignpossible & (LYX_ALIGN_BLOCK | LYX_ALIGN_CENTER |
LYX_ALIGN_LAYOUT | LYX_ALIGN_LEFT | LYX_ALIGN_RIGHT)) {
bool first = true;
os << "\tAlignPossible";
if (alignpossible & LYX_ALIGN_BLOCK) {
os << " Block";
first = false;
}
if (alignpossible & LYX_ALIGN_CENTER) {
if (!first)
os << ',';
os << " Center";
first = false;
}
if (alignpossible & LYX_ALIGN_LAYOUT) {
if (!first)
os << ',';
os << " Layout";
first = false;
}
if (alignpossible & LYX_ALIGN_LEFT) {
if (!first)
os << ',';
os << " Left";
first = false;
}
if (alignpossible & LYX_ALIGN_RIGHT) {
if (!first)
os << ',';
os << " Right";
first = false;
}
os << '\n';
}
// LabelString must come before LabelStringAppendix
if (!labelstring_.empty())
os << "\tLabelString \"" << to_utf8(labelstring_) << "\"\n";
if (!endlabelstring_.empty())
os << "\tEndLabelString \"" << to_utf8(endlabelstring_) << "\"\n";
if (!labelstring_appendix_.empty() && labelstring_appendix_ != labelstring_)
os << "\tLabelStringAppendix \""
<< to_utf8(labelstring_appendix_) << "\"\n";
if (!counter.empty())
os << "\tLabelCounter \"" << to_utf8(counter) << "\"\n";
os << "\tFreeSpacing " << free_spacing << '\n';
os << "\tPassThru " << pass_thru << '\n';
if (!pass_thru_chars.empty())
os << "\tPassThruChars " << to_utf8(pass_thru_chars) << '\n';
os << "\tParbreakIsNewline " << parbreak_is_newline << '\n';
switch (spacing.getSpace()) {
case Spacing::Double:
os << "\tSpacing Double\n";
break;
case Spacing::Onehalf:
os << "\tSpacing Onehalf\n";
break;
case Spacing::Other:
os << "\tSpacing Other " << spacing.getValueAsString() << '\n';
break;
case Spacing::Single:
os << "\tSpacing Single\n";
break;
case Spacing::Default:
break;
}
if (!requires_.empty()) {
os << "\tRequires ";
for (set<string>::const_iterator it = requires_.begin();
it != requires_.end(); ++it) {
if (it != requires_.begin())
os << ',';
os << *it;
}
os << '\n';
}
if (!autonests_.empty()) {
os << "\tAutoNests\n\t";
for (set<docstring>::const_iterator it = autonests_.begin();
it != autonests_.end(); ++it) {
if (it != autonests_.begin())
os << ',';
os << to_utf8(*it);
}
os << "\n\tEndAutoNests\n";
}
if (!autonested_by_.empty()) {
os << "\tIsAutoNestedBy\n\t";
for (set<docstring>::const_iterator it = autonested_by_.begin();
it != autonested_by_.end(); ++it) {
if (it != autonested_by_.begin())
os << ',';
os << to_utf8(*it);
}
os << "\n\tIsAutoNestedBy\n";
}
if (refprefix.empty())
os << "\tRefPrefix OFF\n";
else
os << "\tRefPrefix " << to_utf8(refprefix) << '\n';
if (!htmltag_.empty())
os << "\tHTMLTag " << htmltag_ << '\n';
if (!htmlattr_.empty())
os << "\tHTMLAttr " << htmlattr_ << '\n';
if (!htmlitemtag_.empty())
os << "\tHTMLItem " << htmlitemtag_ << '\n';
if (!htmlitemattr_.empty())
os << "\tHTMLItemAttr " << htmlitemattr_ << '\n';
if (!htmllabeltag_.empty())
os << "\tHTMLLabel " << htmllabeltag_ << '\n';
if (!htmllabelattr_.empty())
os << "\tHTMLLabelAttr " << htmllabelattr_ << '\n';
os << "\tHTMLLabelFirst " << htmllabelfirst_ << '\n';
if (!htmlstyle_.empty())
os << "\tHTMLStyle\n"
<< to_utf8(rtrim(htmlstyle_, "\n"))
<< "\n\tEndHTMLStyle\n";
os << "\tHTMLForceCSS " << htmlforcecss_ << '\n';
if (!htmlpreamble_.empty())
os << "\tHTMLPreamble\n"
<< to_utf8(rtrim(htmlpreamble_, "\n"))
<< "\n\tEndPreamble\n";
os << "\tHTMLTitle " << htmltitle_ << "\n"
"\tSpellcheck " << spellcheck << "\n"
"\tForceLocal " << forcelocal << "\n"
"End\n";
}
bool Layout::hasArgs() const
{
return !latexargs_.empty() || !postcommandargs_.empty() ||
!itemargs_.empty();
}
Layout::LaTeXArgMap Layout::args() const
{
LaTeXArgMap args = latexargs_;
if (!postcommandargs_.empty())
args.insert(postcommandargs_.begin(), postcommandargs_.end());
if (!itemargs_.empty())
args.insert(itemargs_.begin(), itemargs_.end());
return args;
}
int Layout::optArgs() const
{
int nr = 0;
LaTeXArgMap::const_iterator it = latexargs_.begin();
for (; it != latexargs_.end(); ++it) {
if (!(*it).second.mandatory)
++nr;
}
LaTeXArgMap::const_iterator iit = postcommandargs_.begin();
for (; iit != postcommandargs_.end(); ++iit) {
if (!(*iit).second.mandatory)
++nr;
}
return nr;
}
int Layout::requiredArgs() const
{
int nr = 0;
LaTeXArgMap::const_iterator it = latexargs_.begin();
for (; it != latexargs_.end(); ++it) {
if ((*it).second.mandatory)
++nr;
}
LaTeXArgMap::const_iterator iit = postcommandargs_.begin();
for (; iit != postcommandargs_.end(); ++iit) {
if (!(*iit).second.mandatory)
++nr;
}
return nr;
}
string const & Layout::htmltag() const
{
if (htmltag_.empty())
htmltag_ = "div";
return htmltag_;
}
string const & Layout::htmlattr() const
{
if (htmlattr_.empty())
htmlattr_ = "class=\"" + defaultCSSClass() + "\"";
return htmlattr_;
}
string const & Layout::htmlitemtag() const
{
if (htmlitemtag_.empty())
htmlitemtag_ = "div";
return htmlitemtag_;
}
string const & Layout::htmlitemattr() const
{
if (htmlitemattr_.empty())
htmlitemattr_ = "class=\"" + defaultCSSItemClass() + "\"";
return htmlitemattr_;
}
string const & Layout::htmllabeltag() const
{
if (htmllabeltag_.empty())
htmllabeltag_ = "span";
return htmllabeltag_;
}
string const & Layout::htmllabelattr() const
{
if (htmllabelattr_.empty())
htmllabelattr_ = "class=\"" + defaultCSSLabelClass() + "\"";
return htmllabelattr_;
}
docstring Layout::htmlstyle() const
{
if (!htmlstyle_.empty() && !htmlforcecss_)
return htmlstyle_;
if (htmldefaultstyle_.empty())
makeDefaultCSS();
docstring retval = htmldefaultstyle_;
if (!htmlstyle_.empty())
retval += '\n' + htmlstyle_;
return retval;
}
string Layout::defaultCSSClass() const
{
if (!defaultcssclass_.empty())
return defaultcssclass_;
docstring d;
docstring::const_iterator it = name().begin();
docstring::const_iterator en = name().end();
for (; it != en; ++it) {
char_type const c = *it;
if (!isAlphaASCII(c)) {
if (d.empty())
// make sure we don't start with an underscore,
// as that sometimes causes problems.
d = from_ascii("lyx_");
else
d += '_';
} else if (isLower(c))
d += c;
else
// this is slow, so do it only if necessary
d += lowercase(c);
}
defaultcssclass_ = to_utf8(d);
return defaultcssclass_;
}
namespace {
string makeMarginValue(char const * side, double d)
{
ostringstream os;
os << "margin-" << side << ": " << d << "ex;\n";
return os.str();
}
} // namespace
void Layout::makeDefaultCSS() const
{
// this never needs to be redone, since reloading layouts will
// wipe out what we did before.
if (!htmldefaultstyle_.empty())
return;
// main font
htmldefaultstyle_ = font.asCSS();
// bottom margins
string tmp;
if (topsep > 0)
tmp += makeMarginValue("top", topsep);
if (bottomsep > 0)
tmp += makeMarginValue("bottom", bottomsep);
if (!leftmargin.empty()) {
// we can't really do what LyX does with the margin, so
// we'll just figure out how many characters it is
int const len = leftmargin.length();
tmp += makeMarginValue("left", len);
}
if (!rightmargin.empty()) {
int const len = rightmargin.length();
tmp += makeMarginValue("right", len);
}
if (!tmp.empty()) {
if (!htmldefaultstyle_.empty())
htmldefaultstyle_ += from_ascii("\n");
htmldefaultstyle_ += from_ascii(tmp);
}
// alignment
string where = alignmentToCSS(align);
if (!where.empty()) {
htmldefaultstyle_ += from_ascii("text-align: " + where + ";\n");
}
// wrap up what we have, if anything
if (!htmldefaultstyle_.empty())
htmldefaultstyle_ =
from_ascii(htmltag() + "." + defaultCSSClass() + " {\n") +
htmldefaultstyle_ + from_ascii("\n}\n");
if (labeltype == LABEL_NO_LABEL || htmllabeltag() == "NONE")
return;
docstring labelCSS;
// label font
if (labelfont != font)
labelCSS = labelfont.asCSS() + from_ascii("\n");
if (labeltype == LABEL_CENTERED)
labelCSS += from_ascii("text-align: center;\n");
if (!labelCSS.empty())
htmldefaultstyle_ +=
from_ascii(htmllabeltag() + "." + defaultCSSLabelClass() + " {\n") +
labelCSS + from_ascii("\n}\n");
}
bool Layout::operator==(Layout const & rhs) const
{
// This is enough for the applications we actually make,
// at least at the moment. But we could check more.
return name() == rhs.name()
&& latexname() == rhs.latexname()
&& latextype == rhs.latextype;
}
} // namespace lyx