/** * \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 #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(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_, """, "\""); break; case LT_LEFTDELIM: lex >> leftdelim_; leftdelim_ = support::subst(leftdelim_, from_ascii("
"), from_ascii("\n")); break; case LT_RIGHTDELIM: lex >> rightdelim_; rightdelim_ = support::subst(rightdelim_, from_ascii("
"), 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 const req = getVectorFromString(lex.getString(true)); requires_.insert(req.begin(), req.end()); break; } case LT_AUTONESTS: { docstring const autonest = subst(subst(subst(lex.getLongString(from_ascii("EndAutoNests")), from_ascii("\n"), docstring()), from_ascii(" "), docstring()), from_ascii("\t"), docstring()); vector const autonests = getVectorFromString(autonest); autonests_.insert(autonests.begin(), autonests.end()); break; } case LT_AUTONESTEDBY: { docstring const autonest = subst(subst(subst(lex.getLongString(from_ascii("EndIsAutoNestedBy")), from_ascii("\n"), docstring()), from_ascii(" "), docstring()), from_ascii("\t"), docstring()); vector const autonests = getVectorFromString(autonest); 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(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(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(le); break; case LATEX_ENVIRONMENT: case LATEX_BIB_ENVIRONMENT: latextype = static_cast(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("
"), from_ascii("\n")); } else if (tok == "rightdelim") { lex.next(); arg.rdelim = lex.getDocString(); arg.rdelim = support::subst(arg.rdelim, from_ascii("
"), 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("
"))) << "\"\n"; if (!arg.rdelim.empty()) os << "\t\tRightDelim \"" << to_utf8(subst(arg.rdelim, from_ascii("\n"), from_ascii("
"))) << "\"\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_, "\"", """) << "\"\n"; if (!leftdelim_.empty()) os << "\tLeftDelim " << to_utf8(subst(leftdelim_, from_ascii("\n"), from_ascii("
"))) << '\n'; if (!rightdelim_.empty()) os << "\tRightDelim " << to_utf8(subst(rightdelim_, from_ascii("\n"), from_ascii("
"))) << '\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::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::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::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