diff --git a/lib/symbols b/lib/symbols index 481aa8186c..aadcd0d006 100644 --- a/lib/symbols +++ b/lib/symbols @@ -300,8 +300,7 @@ spadesuit cmsy 127 170 mathord ♠ # We define lyxnot as mathrel in order to have proper alignment lyxnot cmsy 54 47 mathrel / hiddensymbol iffont cmsy -# 10mu is the extra space added to relation operators -\def\not{\lyxnot\kern-20mu} +\def\not{\lyxnot\mathrel{\kern-11mu}} else \def\not{\kern4mu\lyxnot\kern-19mu} endif @@ -669,7 +668,7 @@ hslash msb 125 0 mathord ℏ hbar msb 126 0 mathord ℏ backepsilon msb 127 0 mathrel ϶ -lyxbar cmsy 161 0 mathord — hiddensymbol +lyxbar cmsy 161 0 mathrel — hiddensymbol lyxminus cmsy 161 0 mathbin — hiddensymbol lyxplus cmr 43 43 mathbin + hiddensymbol lyxeq cmr 61 61 mathord = hiddensymbol @@ -795,7 +794,7 @@ APLup wasy 0 0 x x APLdown wasy 70 0 x x APLinput wasy 125 0 x x APLcomment wasy 127 0 x x -\def\APLinv{\div\kern-17.9mu\APLbox} wasysym +\def\APLinv{\mathord{\div\kern-13.9mu\APLbox}} wasysym APLuparrowbox wasy 110 0 x ⍐ APLdownarrowbox wasy 111 0 x ⍗ APLleftarrowbox wasy 112 0 x ⍇ @@ -976,15 +975,15 @@ bignplus stmry 112 0 mathop x stmaryrd # caution: named hugenpl #Largerrbracketbot stmry 126 0 mathclose x stmaryrd # only in the font, not the .sty caution: named Hugerrbracketbot in the font #rrbracketex stmry 127 0 mathclose x stmaryrd # only in the font, not the .sty caution: named Hugerrbracketex in the font -\def\varcopyright{c\kern-14mu\varbigcirc} stmaryrd -\def\longarrownot{\kern5.5mu\arrownot\kern-5.5mu} stmaryrd -\def\Longarrownot{\kern5.5mu\Arrownot\kern-5.5mu} stmaryrd -\def\Mapsto{\Mapstochar\kern-9mu\Rightarrow} stmaryrd +\def\varcopyright{\mathord{c\kern-11mu\varbigcirc}} stmaryrd +\def\longarrownot{\mathrel{\kern5.5mu}\arrownot\mathrel{\kern-5.5mu}} stmaryrd +\def\Longarrownot{\mathrel{\kern5.5mu}\Arrownot\mathrel{\kern-5.5mu}} stmaryrd +\def\Mapsto{\Mapstochar\mathrel{\kern-2mu}\Rightarrow} stmaryrd \def\mapsfrom{\leftarrow\kern-9mu\mapsfromchar} stmaryrd \def\Mapsfrom{\Leftarrow\kern-9mu\Mapsfromchar} stmaryrd -\def\Longmapsto{\Mapstochar\kern-7mu\Longrightarrow} stmaryrd -\def\longmapsfrom{\longleftarrow\kern-7mu\mapsfromchar} stmaryrd -\def\Longmapsfrom{\Longleftarrow\kern-7mu\Mapsfromchar} stmaryrd +\def\Longmapsto{\Mapstochar\Longrightarrow} stmaryrd +\def\longmapsfrom{\longleftarrow\mapsfromchar} stmaryrd +\def\Longmapsfrom{\Longleftarrow\mathrel{\kern1mu}\Mapsfromchar} stmaryrd # symbols from the mhchem package, all of them are equivalent to a math symbol # mhchem is not loaded because these commands can only be used inside @@ -1115,21 +1114,21 @@ pod lyxblacktext 0 0 func x amsmath # mathtools.sty -vcentcolon cmr 58 58 mathrel : mathtools -ordinarycolon cmr 58 58 mathrel : mathtools -\def\dblcolon{\vcentcolon\kern-10.9mu\vcentcolon} mathrel :: mathtools -\def\coloneqq{\vcentcolon\kern-11.2mu=} mathrel ≔ mathtools -\def\Coloneqq{\dblcolon\kern-11.2mu=} mathrel ::= mathtools -\def\coloneq{\vcentcolon\kern-11.2mu-} mathrel :- mathtools -\def\Coloneq{\dblcolon\kern-11.2mu-} mathrel ::- mathtools -\def\eqqcolon{=\kern-11.2mu\vcentcolon} mathrel ≕ mathtools -\def\Eqqcolon{=\kern-11.2mu\dblcolon} mathrel =:: mathtools -\def\eqcolon{-\kern-11.2mu\vcentcolon} mathrel -: mathtools -\def\Eqcolon{-\kern-11.2mu\dblcolon} mathrel -:: mathtools -\def\colonapprox{\vcentcolon\kern-11.2mu\approx} mathrel :≈ mathtools -\def\Colonapprox{\dblcolon\kern-11.2mu\approx} mathrel ::≈ mathtools -\def\colonsim{\vcentcolon\kern-11.2mu\sim} mathrel :∼ mathtools -\def\Colonsim{\dblcolon\kern-11.2mu\sim} mathrel ::∼ mathtools +vcentcolon cmr 58 58 mathrel : mathtools +ordinarycolon cmr 58 58 mathrel : mathtools +\def\dblcolon{\vcentcolon\mathrel{\kern-0.9mu}\vcentcolon} mathrel :: mathtools +\def\coloneqq{\vcentcolon\mathrel{\kern-1.2mu}=} mathrel ≔ mathtools +\def\Coloneqq{\dblcolon\mathrel{\kern-1.2mu}=} mathrel ::= mathtools +\def\coloneq{\vcentcolon\mathrel{\kern-1.2mu}\mathrel{-}} mathrel :- mathtools +\def\Coloneq{\dblcolon\mathrel{\kern-1.2mu}\mathrel{-}} mathrel ::- mathtools +\def\eqqcolon{=\mathrel{\kern-1.2mu}\vcentcolon} mathrel ≕ mathtools +\def\Eqqcolon{=\mathrel{\kern-1.2mu}\dblcolon} mathrel =:: mathtools +\def\eqcolon{\mathrel{-}\mathrel{\kern-1.2mu}\vcentcolon} mathrel -: mathtools +\def\Eqcolon{\mathrel{-}\mathrel{\kern-1.2mu}\dblcolon} mathrel -:: mathtools +\def\colonapprox{\vcentcolon\mathrel{\kern-1.2mu}\approx} mathrel :≈ mathtools +\def\Colonapprox{\dblcolon\mathrel{\kern-1.2mu}\approx} mathrel ::≈ mathtools +\def\colonsim{\vcentcolon\mathrel{\kern-1.2mu}\sim} mathrel :∼ mathtools +\def\Colonsim{\dblcolon\mathrel{\kern-1.2mu}\sim} mathrel ::∼ mathtools # @@ -1144,50 +1143,53 @@ ordinarycolon cmr 58 58 mathrel : mathtools \def\notin{\not\in} mathrel ∉ \def\slash{/} -\def\longleftrightarrow{\leftarrow\kern-12.5mu\rightarrow} -\def\Longleftrightarrow{\Leftarrow\kern-12.5mu\Rightarrow} -\def\iff{\Leftarrow\kern-12.5mu\Rightarrow} +\def\joinrel{\mathrel{\kern-3mu}} +\def\relbar{\lyxbar} +\def\Relbar{\mathrel{=}} +\def\longleftrightarrow{\leftarrow\joinrel\rightarrow} +\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow} +\def\iff{\Leftarrow\joinrel\Rightarrow} \def\doteq{\stackrel{\cdot}{=}} iffont cmsy -\def\longrightarrow{\lyxbar\kern-11mu\rightarrow} mathrel ⟶ -\def\longleftarrow{\leftarrow\kern-11mu\lyxbar} mathrel ⟵ -\def\Longrightarrow{\lyxeq\kern-9.5mu\Rightarrow} mathrel ⟹ -\def\Longleftarrow{\Leftarrow\kern-9.5mu\lyxeq} mathrel ⟸ +\def\longrightarrow{\relbar\joinrel\rightarrow} mathrel ⟶ +\def\longleftarrow{\leftarrow\joinrel\relbar} mathrel ⟵ +\def\Longrightarrow{\Relbar\joinrel\Rightarrow} mathrel ⟹ +\def\Longleftarrow{\Leftarrow\joinrel\Relbar} mathrel ⟸ \def\implies{\Longrightarrow} mathrel ⟹ amsmath \def\impliedby{\Longleftarrow} mathrel ⟸ amsmath -\def\mapsto{\mapstochar\kern-9mu\rightarrow} mathrel ↤ -\def\longmapsto{\mapstochar\kern-6mu\lyxbar\kern-11mu\rightarrow} mathrel ⟻ -\def\models{\vert\kern-3mu\lyxeq} mathrel ⊨ +\def\mapsto{\mapstochar\mathrel{\kern-2mu}\rightarrow} mathrel ↤ +\def\longmapsto{\mapstochar\joinrel\relbar\joinrel\rightarrow} mathrel ⟻ +\def\models{\mathrel{\vert}\joinrel\Relbar} mathrel ⊨ else \def\implies{=>} mathrel ⟹ amsmath \def\impliedby{<=} mathrel ⟸ amsmath endif iffont cmm -\def\hookrightarrow{\lhook\kern-12mu\rightarrow} mathrel ↪ -\def\hookleftarrow{\leftarrow\kern-12mu\rhook} mathrel ↩ -\def\bowtie{\triangleright\kern-10mu\triangleleft} mathrel ⋈ +\def\hookrightarrow{\lhook\joinrel\rightarrow} mathrel ↪ +\def\hookleftarrow{\leftarrow\joinrel\rhook} mathrel ↩ +\def\bowtie{\mathrel\triangleright\joinrel\mathrel\triangleleft} mathrel ⋈ endif iffont msa -\def\dashrightarrow{\lyxdabar\lyxdabar\lyxright} mathrel ⤏ amssymb -\def\dashleftarrow{\lyxleft\lyxdabar\lyxdabar} mathrel ⤎ amssymb +\def\dashrightarrow{\mathrel{\lyxdabar\lyxdabar\lyxright}} mathrel ⤏ amssymb +\def\dashleftarrow{\mathrel{\lyxleft\lyxdabar\lyxdabar}} mathrel ⤎ amssymb else \def\dashrightarrow{- - \rightarrow} mathrel ⤏ amssymb \def\dashleftarrow{\leftarrow{} - -} mathrel ⤎ amssymb endif \def\dasharrow{\dashrightarrow} mathrel ⤏ amssymb iffont msb -\def\Join{\ltimes\kern-22mu\rtimes} amssymb +\def\Join{\mathrel{\ltimes\kern-13.5mu\rtimes}} amssymb else \def\Join{|x|} amssymb endif -# Fixme: latin-1 chars in text file +# FIXME: UTF-8 chars in text file \def\AA{\AA}{Å} textmode Å amstext,lyxmathsym \def\O{\O}{Ø} textmode Ø amstext,lyxmathsym iffont cmsy # The \sim is placed too high... -\def\cong{\stackrel{_\sim}{=}} mathrel ≅ +\def\cong{\stackrel{\sim}{=}} mathrel ≅ lyxsurd cmsy 112 0 mathord √ \def\surd{^\lyxsurd} mathord √ \def\textdegree{\kern-1mu^{\circ}\kern-4mu} textmode ° textcomp,amstext,lyxmathsym diff --git a/src/Dimension.h b/src/Dimension.h index bd8f10d8f2..0607be6eba 100644 --- a/src/Dimension.h +++ b/src/Dimension.h @@ -32,6 +32,8 @@ public: void operator+=(Dimension const & dim); /// set to empty box void clear() { wid = asc = des = 0; } + /// check if box is empty + bool empty() const { return wid == 0 && asc == 0 && wid == 0; } /// get height int height() const { return asc + des; } /// get ascent diff --git a/src/Makefile.am b/src/Makefile.am index f3dd870077..f87d0290b3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -403,6 +403,7 @@ SOURCEFILESMATHED = \ mathed/InsetMathCancelto.cpp \ mathed/InsetMathCases.cpp \ mathed/InsetMathChar.cpp \ + mathed/InsetMathClass.cpp \ mathed/InsetMathColor.cpp \ mathed/InsetMathComment.cpp \ mathed/InsetMathDecoration.cpp \ @@ -447,16 +448,18 @@ SOURCEFILESMATHED = \ mathed/InsetMathXArrow.cpp \ mathed/InsetMathXYMatrix.cpp \ mathed/InsetMathDiagram.cpp \ + mathed/MacroTable.cpp \ mathed/MathAtom.cpp \ mathed/MathAutoCorrect.cpp \ + mathed/MathClass.cpp \ mathed/MathData.cpp \ mathed/MathExtern.cpp \ mathed/MathFactory.cpp \ mathed/MathMacro.cpp \ mathed/MathMacroArgument.cpp \ - mathed/MacroTable.cpp \ mathed/MathMacroTemplate.cpp \ mathed/MathParser.cpp \ + mathed/MathRow.cpp \ mathed/MathStream.cpp \ mathed/MathSupport.cpp \ mathed/TextPainter.cpp @@ -473,6 +476,7 @@ HEADERFILESMATHED = \ mathed/InsetMathCancelto.h \ mathed/InsetMathCases.h \ mathed/InsetMathChar.h \ + mathed/InsetMathClass.h \ mathed/InsetMathColor.h \ mathed/InsetMathComment.h \ mathed/InsetMathDelim.h \ @@ -518,6 +522,7 @@ HEADERFILESMATHED = \ mathed/InsetMathDiagram.h \ mathed/MathAtom.h \ mathed/MathAutoCorrect.h \ + mathed/MathClass.h \ mathed/MathData.h \ mathed/MathCompletionList.h \ mathed/MathExtern.h \ @@ -528,6 +533,7 @@ HEADERFILESMATHED = \ mathed/MathMacroTemplate.h \ mathed/MathParser.h \ mathed/MathParser_flags.h \ + mathed/MathRow.h \ mathed/ReplaceData.h \ mathed/MathStream.h \ mathed/MathSupport.h \ diff --git a/src/MetricsInfo.cpp b/src/MetricsInfo.cpp index ea3bf0247e..fdde05f3d7 100644 --- a/src/MetricsInfo.cpp +++ b/src/MetricsInfo.cpp @@ -38,8 +38,8 @@ namespace lyx { MetricsBase::MetricsBase(BufferView * b, FontInfo f, int w) : bv(b), font(move(f)), style(LM_ST_TEXT), fontname("mathnormal"), - textwidth(w), solid_line_thickness_(1), solid_line_offset_(1), - dotted_line_thickness_(1) + textwidth(w), macro_nesting(0), + solid_line_thickness_(1), solid_line_offset_(1), dotted_line_thickness_(1) { if (lyxrc.zoom >= 200) { // derive the line thickness from zoom factor diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h index d2dd8b7bcf..a48b45c563 100644 --- a/src/MetricsInfo.h +++ b/src/MetricsInfo.h @@ -65,6 +65,8 @@ public: std::string fontname; /// This is the width available in pixels int textwidth; + /// count wether the current mathdata is nested in macro(s) + int macro_nesting; /// Temporarily change a full font. Changer changeFontSet(std::string const & font, bool cond = true); diff --git a/src/insets/InsetCode.h b/src/insets/InsetCode.h index 1df6800d2d..7d4632cd16 100644 --- a/src/insets/InsetCode.h +++ b/src/insets/InsetCode.h @@ -235,6 +235,8 @@ enum InsetCode { /// IPADECO_CODE, /// + MATH_CLASS_CODE, + /// INSET_CODE_SIZE }; diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp index 2fb1ff5a55..cbf33cdf87 100644 --- a/src/mathed/InsetMath.cpp +++ b/src/mathed/InsetMath.cpp @@ -13,15 +13,18 @@ #include "InsetMath.h" #include "MathData.h" +#include "MathRow.h" #include "MathStream.h" +#include "MetricsInfo.h" + #include "support/debug.h" #include "support/docstream.h" #include "support/gettext.h" +#include "support/lassert.h" #include "support/lstrings.h" #include "support/textutils.h" -#include "support/lassert.h" using namespace std; @@ -49,6 +52,52 @@ MathData const & InsetMath::cell(idx_type) const } +MathClass InsetMath::mathClass() const +{ + return MC_ORD; +} + + +bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo & mi) const +{ + MathRow::Element e(MathRow::INSET, mi); + e.inset = this; + e.mclass = mathClass(); + mrow.push_back(e); + return true; +} + +void InsetMath::metricsMarkers(MetricsInfo & mi, Dimension & dim, + int framesize) const +{ + if (!mi.base.macro_nesting) + Inset::metricsMarkers(dim, framesize); +} + + +void InsetMath::metricsMarkers2(MetricsInfo & mi, Dimension & dim, + int framesize) const +{ + if (!mi.base.macro_nesting) + Inset::metricsMarkers2(dim, framesize); +} + + +void InsetMath::drawMarkers(PainterInfo & pi, int x, int y) const +{ + if (!pi.base.macro_nesting) + Inset::drawMarkers(pi, x, y); +} + + +void InsetMath::drawMarkers2(PainterInfo & pi, int x, int y) const +{ + if (!pi.base.macro_nesting) + Inset::drawMarkers2(pi, x, y); +} + + + void InsetMath::dump() const { lyxerr << "---------------------------------------------" << endl; diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h index da84af1010..e8c893bda5 100644 --- a/src/mathed/InsetMath.h +++ b/src/mathed/InsetMath.h @@ -13,6 +13,8 @@ #ifndef MATH_INSET_H #define MATH_INSET_H +#include "MathClass.h" + #include "insets/Inset.h" @@ -51,7 +53,10 @@ inclusion in the "real LyX insets" FormulaInset and FormulaMacroInset. */ +class Cursor; class OutputParams; +class MetricsInfo; + class InsetMathArray; class InsetMathAMSArray; class InsetMathBrace; @@ -70,7 +75,6 @@ class InsetMathSpace; class InsetMathSpecialChar; class InsetMathSymbol; class InsetMathUnknown; - class InsetMathRef; class HtmlStream; @@ -85,7 +89,7 @@ class WriteStream; class MathData; class MathMacroTemplate; class MathMacro; -class Cursor; +class MathRow; class TextPainter; class TextMetricsInfo; class ReplaceData; @@ -160,14 +164,22 @@ public: virtual InsetMathRef * asRefInset() { return 0; } virtual InsetMathSpecialChar const * asSpecialCharInset() const { return 0; } + /// The class of the math object (used primarily for spacing) + virtual MathClass mathClass() const; + /// Add this inset to a math row. Return true if contents got added + virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const; + + /// draw four angular markers + void drawMarkers(PainterInfo & pi, int x, int y) const; + /// draw two angular markers + void drawMarkers2(PainterInfo & pi, int x, int y) const; + /// add space for markers + void metricsMarkers(MetricsInfo & mi, Dimension & dim, int framesize = 1) const; + /// add space for markers + void metricsMarkers2(MetricsInfo & mi, Dimension & dim, int framesize = 1) const; + /// identifies things that can get scripts virtual bool isScriptable() const { return false; } - /// identifies a binary operators (used for spacing) - virtual bool isMathBin() const { return false; } - /// identifies relational operators (used for spacing and splitting equations) - virtual bool isMathRel() const { return false; } - /// identifies punctuation (used for spacing) - virtual bool isMathPunct() const { return false; } /// will this get written as a single block in {..} virtual bool extraBraces() const { return false; } diff --git a/src/mathed/InsetMathBoldSymbol.cpp b/src/mathed/InsetMathBoldSymbol.cpp index 215bd5ff30..ade29f7de1 100644 --- a/src/mathed/InsetMathBoldSymbol.cpp +++ b/src/mathed/InsetMathBoldSymbol.cpp @@ -51,7 +51,7 @@ void InsetMathBoldSymbol::metrics(MetricsInfo & mi, Dimension & dim) const { //Changer dummy = mi.base.changeFontSet("mathbf"); cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); ++dim.wid; // for 'double stroke' } diff --git a/src/mathed/InsetMathBox.cpp b/src/mathed/InsetMathBox.cpp index e9e483c29f..d0ac82f1a6 100644 --- a/src/mathed/InsetMathBox.cpp +++ b/src/mathed/InsetMathBox.cpp @@ -83,7 +83,7 @@ void InsetMathBox::metrics(MetricsInfo & mi, Dimension & dim) const { Changer dummy = mi.base.changeFontSet("textnormal"); cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } @@ -135,7 +135,7 @@ void InsetMathFBox::metrics(MetricsInfo & mi, Dimension & dim) const { Changer dummy = mi.base.changeFontSet("textnormal"); cell(0).metrics(mi, dim); - metricsMarkers2(dim, 3); // 1 pixel space, 1 frame, 1 space + metricsMarkers2(mi, dim, 3); // 1 pixel space, 1 frame, 1 space } @@ -246,7 +246,7 @@ void InsetMathMakebox::metrics(MetricsInfo & mi, Dimension & dim) const dim.des += 1; } - metricsMarkers(dim); + metricsMarkers(mi, dim); } @@ -360,7 +360,7 @@ InsetMathBoxed::InsetMathBoxed(Buffer * buf) void InsetMathBoxed::metrics(MetricsInfo & mi, Dimension & dim) const { cell(0).metrics(mi, dim); - metricsMarkers2(dim, 3); // 1 pixel space, 1 frame, 1 space + metricsMarkers2(mi, dim, 3); // 1 pixel space, 1 frame, 1 space } diff --git a/src/mathed/InsetMathBrace.cpp b/src/mathed/InsetMathBrace.cpp index 52ccc01b83..6870fa609d 100644 --- a/src/mathed/InsetMathBrace.cpp +++ b/src/mathed/InsetMathBrace.cpp @@ -55,7 +55,7 @@ void InsetMathBrace::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc = max(dim0.asc, t.asc); dim.des = max(dim0.des, t.des); dim.wid = dim0.width() + 2 * t.wid; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathCancel.cpp b/src/mathed/InsetMathCancel.cpp index dcda5b63ea..0a8aad90fa 100644 --- a/src/mathed/InsetMathCancel.cpp +++ b/src/mathed/InsetMathCancel.cpp @@ -38,7 +38,7 @@ Inset * InsetMathCancel::clone() const void InsetMathCancel::metrics(MetricsInfo & mi, Dimension & dim) const { cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathCancelto.cpp b/src/mathed/InsetMathCancelto.cpp index 6a04cfa246..6170cbd6fa 100644 --- a/src/mathed/InsetMathCancelto.cpp +++ b/src/mathed/InsetMathCancelto.cpp @@ -47,7 +47,7 @@ void InsetMathCancelto::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc = max(dim0.ascent() + 2, dim0.ascent() + dim1.ascent()) + 2 + 8; dim.des = max(dim0.descent() - 2, dim1.descent()) + 2; dim.wid = dim0.width() + dim1.width() + 10; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathChar.cpp b/src/mathed/InsetMathChar.cpp index ae843bf00d..1669515b66 100644 --- a/src/mathed/InsetMathChar.cpp +++ b/src/mathed/InsetMathChar.cpp @@ -121,8 +121,6 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const dim = fm.dimension(char_); kerning_ = fm.rbearing(char_) - dim.wid; } - if (mathfont && isMathPunct()) - dim.wid += mathed_thinmuskip(mi.base.font); } @@ -266,22 +264,19 @@ void InsetMathChar::htmlize(HtmlStream & ms) const } -bool InsetMathChar::isMathBin() const +MathClass InsetMathChar::mathClass() const { - return subst_ && subst_->extra == "mathbin"; -} - - -bool InsetMathChar::isMathRel() const -{ - return subst_ && subst_->extra == "mathrel"; -} - - -bool InsetMathChar::isMathPunct() const -{ - return support::contains(",;", static_cast<char>(char_)) - || (subst_ && subst_->extra == "mathpunct"); + // this information comes from fontmath.ltx in LaTeX source. + char const ch = static_cast<char>(char_); + if (subst_) + return string_to_class(subst_->extra); + else if (support::contains(",;", ch)) + return MC_PUNCT; + else if (support::contains("([", ch)) + return MC_OPEN; + else if (support::contains(")]!?", ch)) + return MC_CLOSE; + else return MC_ORD; } diff --git a/src/mathed/InsetMathChar.h b/src/mathed/InsetMathChar.h index ea190211b7..6e7ff72c9c 100644 --- a/src/mathed/InsetMathChar.h +++ b/src/mathed/InsetMathChar.h @@ -51,11 +51,7 @@ public: /// char_type getChar() const { return char_; } /// - bool isMathBin() const; - /// - bool isMathRel() const; - /// - bool isMathPunct() const; + MathClass mathClass() const; /// InsetCode lyxCode() const { return MATH_CHAR_CODE; } diff --git a/src/mathed/InsetMathClass.cpp b/src/mathed/InsetMathClass.cpp new file mode 100644 index 0000000000..0237f250d0 --- /dev/null +++ b/src/mathed/InsetMathClass.cpp @@ -0,0 +1,57 @@ +/** + * \file InsetMathClass.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author André Pönitz + * + * Full author contact details are available in file CREDITS. + */ + +#include <config.h> + +#include "InsetMathClass.h" + +#include "support/docstream.h" + + +namespace lyx { + +InsetMathClass::InsetMathClass(Buffer * buf, MathClass mc) + : InsetMathNest(buf, 1), math_class_(mc) +{} + + +Inset * InsetMathClass::clone() const +{ + return new InsetMathClass(*this); +} + + +void InsetMathClass::metrics(MetricsInfo & mi, Dimension & dim) const +{ + cell(0).metrics(mi, dim); + metricsMarkers(mi, dim); +} + + +void InsetMathClass::draw(PainterInfo & pi, int x, int y) const +{ + cell(0).draw(pi, x + 1, y); + drawMarkers(pi, x, y); +} + + +docstring InsetMathClass::name() const +{ + return class_to_string(math_class_); +} + + +void InsetMathClass::infoize(odocstream & os) const +{ + os << name() << " "; +} + + +} // namespace lyx diff --git a/src/mathed/InsetMathClass.h b/src/mathed/InsetMathClass.h new file mode 100644 index 0000000000..f50040d3b4 --- /dev/null +++ b/src/mathed/InsetMathClass.h @@ -0,0 +1,50 @@ +// -*- C++ -*- +/** + * \file InsetMathClass.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author André Pönitz + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef MATH_CLASSINSET_H +#define MATH_CLASSINSET_H + +#include "MathClass.h" + +#include "InsetMathNest.h" + + +namespace lyx { + + +/// Support for LaTeX's \\mathxxx class-changing commands + +class InsetMathClass : public InsetMathNest { +public: + /// + InsetMathClass(Buffer * buf, MathClass); + /// + docstring name() const; + /// + MathClass mathClass() const { return math_class_; } + /// + void metrics(MetricsInfo & mi, Dimension & dim) const; + /// + void draw(PainterInfo & pi, int x, int y) const; + /// + void infoize(odocstream & os) const; + /// + InsetCode lyxCode() const { return MATH_CLASS_CODE; } + +private: + virtual Inset * clone() const; + /// + MathClass math_class_; +}; + + +} // namespace lyx +#endif diff --git a/src/mathed/InsetMathColor.cpp b/src/mathed/InsetMathColor.cpp index 0a6d0aa6b9..966fe5e99a 100644 --- a/src/mathed/InsetMathColor.cpp +++ b/src/mathed/InsetMathColor.cpp @@ -49,7 +49,7 @@ Inset * InsetMathColor::clone() const void InsetMathColor::metrics(MetricsInfo & mi, Dimension & dim) const { cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathComment.cpp b/src/mathed/InsetMathComment.cpp index ff91991aa8..b8c157b2c2 100644 --- a/src/mathed/InsetMathComment.cpp +++ b/src/mathed/InsetMathComment.cpp @@ -50,7 +50,7 @@ Inset * InsetMathComment::clone() const void InsetMathComment::metrics(MetricsInfo & mi, Dimension & dim) const { cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathDecoration.cpp b/src/mathed/InsetMathDecoration.cpp index cd34d4d241..acc49743fa 100644 --- a/src/mathed/InsetMathDecoration.cpp +++ b/src/mathed/InsetMathDecoration.cpp @@ -122,7 +122,7 @@ void InsetMathDecoration::metrics(MetricsInfo & mi, Dimension & dim) const dim.des += dh_ + 2; } - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathDelim.cpp b/src/mathed/InsetMathDelim.cpp index 68e4430514..4ab179e7b2 100644 --- a/src/mathed/InsetMathDelim.cpp +++ b/src/mathed/InsetMathDelim.cpp @@ -115,7 +115,7 @@ void InsetMathDelim::metrics(MetricsInfo & mi, Dimension & dim) const dw_ = 8; if (dw_ < 4) dw_ = 4; - dim.wid = dim0.width() + 2 * dw_ + 2 * mathed_thinmuskip(mi.base.font); + dim.wid = dim0.width() + 2 * dw_; dim.asc = max(a0, d0) + h0; dim.des = max(a0, d0) - h0; } @@ -125,10 +125,9 @@ void InsetMathDelim::draw(PainterInfo & pi, int x, int y) const { Dimension const dim = dimension(*pi.base.bv); int const b = y - dim.asc; - int const skip = mathed_thinmuskip(pi.base.font); - cell(0).draw(pi, x + dw_ + skip, y); - mathed_draw_deco(pi, x + skip, b, dw_, dim.height(), left_); - mathed_draw_deco(pi, x + dim.width() - dw_ - skip, + cell(0).draw(pi, x + dw_, y); + mathed_draw_deco(pi, x, b, dw_, dim.height(), left_); + mathed_draw_deco(pi, x + dim.width() - dw_, b, dw_, dim.height(), right_); setPosCache(pi, x, y); } diff --git a/src/mathed/InsetMathDelim.h b/src/mathed/InsetMathDelim.h index 3a09ede2f3..5a7e816404 100644 --- a/src/mathed/InsetMathDelim.h +++ b/src/mathed/InsetMathDelim.h @@ -30,6 +30,8 @@ public: InsetMathDelim * asDelimInset() { return this; } /// InsetMathDelim const * asDelimInset() const { return this; } + /// + MathClass mathClass() const { return MC_INNER; } /// is it (...)? bool isParenthesis() const; /// is it [...]? diff --git a/src/mathed/InsetMathEnsureMath.cpp b/src/mathed/InsetMathEnsureMath.cpp index d1e69fb6c2..382c6fedd8 100644 --- a/src/mathed/InsetMathEnsureMath.cpp +++ b/src/mathed/InsetMathEnsureMath.cpp @@ -41,7 +41,7 @@ void InsetMathEnsureMath::metrics(MetricsInfo & mi, Dimension & dim) const bool really_change_font = isTextFont(mi.base.fontname); Changer dummy = mi.base.changeFontSet("mathnormal", really_change_font); cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathEnv.cpp b/src/mathed/InsetMathEnv.cpp index cc613fd440..7582201e61 100644 --- a/src/mathed/InsetMathEnv.cpp +++ b/src/mathed/InsetMathEnv.cpp @@ -39,7 +39,7 @@ Inset * InsetMathEnv::clone() const void InsetMathEnv::metrics(MetricsInfo & mi, Dimension & dim) const { cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathFont.cpp b/src/mathed/InsetMathFont.cpp index 40623f6ec6..825cf332c4 100644 --- a/src/mathed/InsetMathFont.cpp +++ b/src/mathed/InsetMathFont.cpp @@ -86,7 +86,7 @@ void InsetMathFont::metrics(MetricsInfo & mi, Dimension & dim) const { Changer dummy = mi.base.changeFontSet(font()); cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathFontOld.cpp b/src/mathed/InsetMathFontOld.cpp index 20f3b1d45b..aa574bd1c6 100644 --- a/src/mathed/InsetMathFontOld.cpp +++ b/src/mathed/InsetMathFontOld.cpp @@ -61,7 +61,7 @@ void InsetMathFontOld::metrics(MetricsInfo & mi, Dimension & dim) const Changer dummy = mi.base.changeFontSet(fontname, really_change_font); cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathFrac.cpp b/src/mathed/InsetMathFrac.cpp index ab876e060d..6254924db8 100644 --- a/src/mathed/InsetMathFrac.cpp +++ b/src/mathed/InsetMathFrac.cpp @@ -123,6 +123,31 @@ bool InsetMathFrac::idxBackward(Cursor & cur) const } +MathClass InsetMathFrac::mathClass() const +{ + // Generalized fractions are of inner class (see The TeXbook, p. 292) + // But stuff from the unit/nicefrac packages are not real fractions. + MathClass mc = MC_ORD; + switch (kind_) { + case ATOP: + case OVER: + case FRAC: + case DFRAC: + case TFRAC: + case CFRAC: + case CFRACLEFT: + case CFRACRIGHT: + mc = MC_INNER; + break; + case NICEFRAC: + case UNITFRAC: + case UNIT: + break; + } + return mc; +} + + void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const { Dimension dim0, dim1, dim2; @@ -182,7 +207,7 @@ void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const dim.des = dim1.height() + 2 - 5; } } - metricsMarkers(dim); + metricsMarkers(mi, dim); } @@ -557,7 +582,7 @@ void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc = dim0.height() + 4 + 5; dim.des = dim1.height() + 4 - 5; dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4; - metricsMarkers2(dim); + metricsMarkers2(mi, dim); } diff --git a/src/mathed/InsetMathFrac.h b/src/mathed/InsetMathFrac.h index c030c8a51a..96b62930ba 100644 --- a/src/mathed/InsetMathFrac.h +++ b/src/mathed/InsetMathFrac.h @@ -62,6 +62,8 @@ public: /// bool idxBackward(Cursor &) const; /// + MathClass mathClass() const; + /// void metrics(MetricsInfo & mi, Dimension & dim) const; /// void draw(PainterInfo &, int x, int y) const; @@ -117,6 +119,8 @@ public: void write(WriteStream & os) const; /// void normalize(NormalStream &) const; + /// Generalized fractions are of inner class (see The TeXbook, p.292) + MathClass mathClass() const { return MC_INNER; } /// void metrics(MetricsInfo & mi, Dimension & dim) const; /// diff --git a/src/mathed/InsetMathGrid.cpp b/src/mathed/InsetMathGrid.cpp index 053958d1c5..fca8f380d2 100644 --- a/src/mathed/InsetMathGrid.cpp +++ b/src/mathed/InsetMathGrid.cpp @@ -588,7 +588,7 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const } */ dim.wid += leftMargin() + rightMargin(); - metricsMarkers2(dim); + metricsMarkers2(mi, dim); // Cache the inset dimension. setDimCache(mi, dim); } diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp index e2ceb5c903..dc943bb520 100644 --- a/src/mathed/InsetMathHull.cpp +++ b/src/mathed/InsetMathHull.cpp @@ -108,7 +108,7 @@ namespace { size_t firstRelOp(MathData const & ar) { for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) - if ((*it)->isMathRel()) + if ((*it)->mathClass() == MC_REL) return it - ar.begin(); return ar.size(); } diff --git a/src/mathed/InsetMathLefteqn.cpp b/src/mathed/InsetMathLefteqn.cpp index ec4fe7e9b0..176c7431cf 100644 --- a/src/mathed/InsetMathLefteqn.cpp +++ b/src/mathed/InsetMathLefteqn.cpp @@ -34,7 +34,7 @@ void InsetMathLefteqn::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc += 2; dim.des += 2; dim.wid = 4; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathOverset.cpp b/src/mathed/InsetMathOverset.cpp index f05bbd7df7..b791c2482f 100644 --- a/src/mathed/InsetMathOverset.cpp +++ b/src/mathed/InsetMathOverset.cpp @@ -39,7 +39,7 @@ void InsetMathOverset::metrics(MetricsInfo & mi, Dimension & dim) const dim.wid = max(dim0.width(), dim1.wid) + 4; dim.asc = dim1.asc + dim0.height() + 4; dim.des = dim1.des; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathPhantom.cpp b/src/mathed/InsetMathPhantom.cpp index 4a190ea760..6d0b88df78 100644 --- a/src/mathed/InsetMathPhantom.cpp +++ b/src/mathed/InsetMathPhantom.cpp @@ -39,7 +39,7 @@ Inset * InsetMathPhantom::clone() const void InsetMathPhantom::metrics(MetricsInfo & mi, Dimension & dim) const { cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathRoot.cpp b/src/mathed/InsetMathRoot.cpp index 2b4b668b9d..aa0a81bce1 100644 --- a/src/mathed/InsetMathRoot.cpp +++ b/src/mathed/InsetMathRoot.cpp @@ -47,7 +47,7 @@ void InsetMathRoot::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc = max(dim0.ascent() + 5, dim1.ascent()) + 2; dim.des = max(dim0.descent() - 5, dim1.descent()) + 2; dim.wid = dim0.width() + dim1.width() + 10; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathScript.cpp b/src/mathed/InsetMathScript.cpp index b4cd5909be..7363c88554 100644 --- a/src/mathed/InsetMathScript.cpp +++ b/src/mathed/InsetMathScript.cpp @@ -273,6 +273,19 @@ int InsetMathScript::nker(BufferView const * bv) const } +MathClass InsetMathScript::mathClass() const +{ + // FIXME: this is a hack, since the class will not be correct if + // the nucleus has several elements. + // The correct implementation would require to linearize the nucleus. + if (nuc().empty()) + return MC_ORD; + else + // return the class of last element since this is the one that counts. + return nuc().back()->mathClass(); +} + + void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const { Dimension dim0; @@ -320,7 +333,7 @@ void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const dim.des = max(nd, des); } else dim.des = nd; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathScript.h b/src/mathed/InsetMathScript.h index e84886b7d3..d0707439b9 100644 --- a/src/mathed/InsetMathScript.h +++ b/src/mathed/InsetMathScript.h @@ -33,6 +33,8 @@ public: /// mode_type currentMode() const { return MATH_MODE; } /// + MathClass mathClass() const; + /// void metrics(MetricsInfo & mi, Dimension & dim) const; /// void draw(PainterInfo & pi, int x, int y) const; diff --git a/src/mathed/InsetMathSideset.cpp b/src/mathed/InsetMathSideset.cpp index 37f437db4f..b04eca7730 100644 --- a/src/mathed/InsetMathSideset.cpp +++ b/src/mathed/InsetMathSideset.cpp @@ -221,7 +221,7 @@ void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const int nd = ndes(bv); int des = dyb(bv) + max(dimbl.descent(), dimbr.descent()); dim.des = max(nd, des); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathSize.cpp b/src/mathed/InsetMathSize.cpp index 31cdb6a9e8..46ea29b30f 100644 --- a/src/mathed/InsetMathSize.cpp +++ b/src/mathed/InsetMathSize.cpp @@ -45,7 +45,7 @@ void InsetMathSize::metrics(MetricsInfo & mi, Dimension & dim) const { Changer dummy = mi.base.changeStyle(style_); cell(0).metrics(mi, dim); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathSqrt.cpp b/src/mathed/InsetMathSqrt.cpp index d04eab3abf..3e49abccb2 100644 --- a/src/mathed/InsetMathSqrt.cpp +++ b/src/mathed/InsetMathSqrt.cpp @@ -41,7 +41,7 @@ void InsetMathSqrt::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc += 4; dim.des += 2; dim.wid += 12; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathStackrel.cpp b/src/mathed/InsetMathStackrel.cpp index 9412d99182..e34448a419 100644 --- a/src/mathed/InsetMathStackrel.cpp +++ b/src/mathed/InsetMathStackrel.cpp @@ -52,6 +52,13 @@ bool InsetMathStackrel::idxUpDown(Cursor & cur, bool up) const } +MathClass InsetMathStackrel::mathClass() const +{ + // FIXME: update this when/if \stackbin is supported + return MC_REL; +} + + void InsetMathStackrel::metrics(MetricsInfo & mi, Dimension & dim) const { Dimension dim1; @@ -70,7 +77,7 @@ void InsetMathStackrel::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc = dim1.ascent() + dim0.height() + 4; dim.des = dim1.descent(); } - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathStackrel.h b/src/mathed/InsetMathStackrel.h index 91862a7792..d6ac815182 100644 --- a/src/mathed/InsetMathStackrel.h +++ b/src/mathed/InsetMathStackrel.h @@ -24,6 +24,8 @@ public: /// bool idxUpDown(Cursor &, bool up) const; /// + MathClass mathClass() const; + /// void metrics(MetricsInfo & mi, Dimension & dim) const; /// void draw(PainterInfo & pi, int x, int y) const; @@ -35,7 +37,7 @@ public: void mathmlize(MathStream &) const; /// void htmlize(HtmlStream &) const; - /// + /// void validate(LaTeXFeatures &) const; /// InsetCode lyxCode() const { return MATH_STACKREL_CODE; } diff --git a/src/mathed/InsetMathSymbol.cpp b/src/mathed/InsetMathSymbol.cpp index 620ca226a2..01bafe614d 100644 --- a/src/mathed/InsetMathSymbol.cpp +++ b/src/mathed/InsetMathSymbol.cpp @@ -70,7 +70,7 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc += h_; dim.des -= h_; } - // set striptable_ + // set scriptable_ scriptable_ = false; if (mi.base.style == LM_ST_DISPLAY) if (sym_->inset == "cmex" || sym_->inset == "esint" || @@ -92,30 +92,21 @@ InsetMath::mode_type InsetMathSymbol::currentMode() const } -bool InsetMathSymbol::isMathBin() const -{ - return sym_->extra == "mathbin"; -} - - -bool InsetMathSymbol::isMathRel() const -{ - return sym_->extra == "mathrel"; -} - - -bool InsetMathSymbol::isMathPunct() const -{ - return sym_->extra == "mathpunct"; -} - - bool InsetMathSymbol::isOrdAlpha() const { return sym_->extra == "mathord" || sym_->extra == "mathalpha"; } +MathClass InsetMathSymbol::mathClass() const +{ + if (sym_->extra == "func" || sym_->extra == "funclim") + return MC_OP; + MathClass const mc = string_to_class(sym_->extra); + return (mc == MC_UNKNOWN) ? MC_ORD : mc; +} + + bool InsetMathSymbol::isScriptable() const { return scriptable_; diff --git a/src/mathed/InsetMathSymbol.h b/src/mathed/InsetMathSymbol.h index 6ca3ded7d1..67b33fd85a 100644 --- a/src/mathed/InsetMathSymbol.h +++ b/src/mathed/InsetMathSymbol.h @@ -40,11 +40,7 @@ public: /// mode_type currentMode() const; /// - bool isMathRel() const; - /// - bool isMathBin() const; - /// - bool isMathPunct() const; + MathClass mathClass() const; /// bool isOrdAlpha() const; /// do we take scripts? diff --git a/src/mathed/InsetMathUnderset.cpp b/src/mathed/InsetMathUnderset.cpp index 46f5277543..2027cf2337 100644 --- a/src/mathed/InsetMathUnderset.cpp +++ b/src/mathed/InsetMathUnderset.cpp @@ -40,7 +40,7 @@ void InsetMathUnderset::metrics(MetricsInfo & mi, Dimension & dim) const dim.wid = max(dim0.width(), dim1.width()) + 4; dim.asc = dim1.ascent(); dim.des = dim1.descent() + dim0.height() + 4; - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/InsetMathXArrow.cpp b/src/mathed/InsetMathXArrow.cpp index 96fdc83aac..a4c1c12c75 100644 --- a/src/mathed/InsetMathXArrow.cpp +++ b/src/mathed/InsetMathXArrow.cpp @@ -51,7 +51,7 @@ void InsetMathXArrow::metrics(MetricsInfo & mi, Dimension & dim) const dim.wid = max(dim0.width(), dim1.width()) + 10; dim.asc = dim0.height() + 10; dim.des = dim1.height(); - metricsMarkers(dim); + metricsMarkers(mi, dim); } diff --git a/src/mathed/MathClass.cpp b/src/mathed/MathClass.cpp new file mode 100644 index 0000000000..a46aad3794 --- /dev/null +++ b/src/mathed/MathClass.cpp @@ -0,0 +1,180 @@ +/** + * \file MathClass.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#include <config.h> + +#include "MathClass.h" +#include "MathSupport.h" + +#include "MetricsInfo.h" +#include "FontInfo.h" + +#include "support/debug.h" +#include "support/docstring.h" +#include "support/lassert.h" + +#include <ostream> + +using namespace std; + +namespace lyx { + + +docstring const class_to_string(MathClass const mc) +{ + string s; + switch (mc) { + case MC_ORD: + s = "mathord"; + break; + case MC_OP: + s = "mathop"; + break; + case MC_BIN: + s = "mathbin"; + break; + case MC_REL: + s = "mathrel"; + break; + case MC_OPEN: + s = "mathopen"; + break; + case MC_CLOSE: + s = "mathclose"; + break; + case MC_PUNCT: + s = "mathpunct"; + break; + case MC_INNER: + s = "mathinner"; + break; + case MC_UNKNOWN: + LATTEST(false); + s = "mathord"; + } + return from_ascii(s); +} + + +MathClass string_to_class(docstring const &s) +{ + if (s == "mathop") + return MC_OP; + else if (s == "mathbin") + return MC_BIN; + else if (s == "mathrel") + return MC_REL; + else if (s == "mathopen") + return MC_OPEN; + else if (s == "mathclose") + return MC_CLOSE; + else if (s == "mathpunct") + return MC_PUNCT; + else if (s == "mathinner") + return MC_INNER; + else if (s == "mathord") + return MC_ORD; + else + return MC_UNKNOWN; +} + + +/* + * The TeXbook presents in Appendix G a set of 22 rules (!) explaining + * how to typeset mathematic formulas. Of interest here are rules 5 + * and 6: + + * 5. If the current item is a Bin atom, and if this was the first + * atom in the list, or if the most recent previous atom was Bin, + * Op, Rel, Open, or Punct, change the current Bin to Ord [and + * continue with Rule 14. Otherwise continue with Rule 17] + * + * 6. If the current item is a Rel or Close or Punct atom, and if the + * most recent previous atom was Bin, change that previous Bin to + * Ord. [Continue with Rule 17.] + */ +void update_class(MathClass & mc, MathClass const prev, MathClass const next) +{ + if (mc == MC_BIN + && (prev == MC_BIN || prev == MC_OP || prev == MC_OPEN + || prev == MC_PUNCT || prev == MC_REL + || next == MC_CLOSE || next == MC_PUNCT || next == MC_REL)) + mc = MC_ORD; +} + + +/* + * This table of spacing between two classes can be found p. 170 of + * The TeXbook. + * + * The line is the class of the first class, and the column the second + * class. The number encodes the spacing between the two atoms, as + * follows + * + * + 0: no spacing + * + 1: thin mu skip + * + 2: med mu skip + * + 3: thick mu skip + * + 9: should never happen + * + negative value: either 0 if the atom is in script or scriptscript mode, + * or the spacing given by the absolute value. + */ +int pair_spc[MC_UNKNOWN][MC_UNKNOWN] = { +// ORD OP BIN REL OPEN CLOSE PUNCT INNER + { 0, 1, -2, -3, 0, 0, 0, -1}, // ORD + { 1, 1, 9, -3, 0, 0, 0, -1}, // OP + { -2, -2, 9, 9, -2, 9, 9, -2}, // BIN + { -3, -3, 9, 0, -3, 0, 0, -3}, // REL + { 0, 0, 9, 0, 0, 0, 0, 0}, // OPEN + { 0, 1, -2, -3, 0, 0, 0, -1}, // CLOSE + { -1, -1, 9, -1, -1, -1, -1, -1}, // PUNCT + { -1, 1, -2, -3, -1, 0, -1, -1}, // INNER +}; + + +int class_spacing(MathClass const mc1, MathClass const mc2, + MetricsBase const & mb) +{ + int spc_code = pair_spc[mc1][mc2]; + //lyxerr << class_to_string(mc1) << "+" << class_to_string(mc2) + // << "=" << spc_code << " @" << mb.style << endl; + if (spc_code < 0) { + switch (mb.style) { + case LM_ST_DISPLAY: + case LM_ST_TEXT: + spc_code = abs(spc_code); + break; + case LM_ST_SCRIPT: + case LM_ST_SCRIPTSCRIPT: + spc_code = 0; + } + } + + int spc = 0; + switch(spc_code) { + case 0: + break; + case 1: + spc = mathed_thinmuskip(mb.font); + break; + case 2: + spc = mathed_medmuskip(mb.font); + break; + case 3: + spc = mathed_thickmuskip(mb.font); + break; + default: + LYXERR0("Impossible pair of classes: (" << mc1 << ", " << mc2 << ")"); + LATTEST(false); + } + return spc; +} + +} // namespace lyx diff --git a/src/mathed/MathClass.h b/src/mathed/MathClass.h new file mode 100644 index 0000000000..169bfdb51a --- /dev/null +++ b/src/mathed/MathClass.h @@ -0,0 +1,69 @@ +// -*- C++ -*- +/** + * \file MathClass.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef MATH_CLASS_H +#define MATH_CLASS_H + +#include "support/strfwd.h" + +namespace lyx { + +class MetricsBase; + +/* The TeXbook, p. 158: + * + * There are thirteen kinds of atoms, each of which might act + * differently in a formula; for example, ‘(’ is an Open atom because + * it comes from an opening. Here is a complete list of the different + * kinds: + + * + Ord: an ordinary atom like ‘x’ + * + Op: a large operator atom like ‘\sum’ + * + Bin: a binary operation atom like ‘+’ + * + Rel: a relation atom like ‘=’ + * + Open: an opening atom like ‘(’ + * + Close: a closing atom like ‘)’ + * + Punct: a punctuation atom like ‘,’ + * + Inner: an inner atom like ‘\frac{1}{2}’ + * + Over: an overline atom like ‘\overline{x}’ + * + Under: an underline atom like ‘\underline{x}’ + * + Acc: an accented atom like ‘\hat{x}’ + * + Rad: a radical atom like ‘\sqrt{2}’ + * + Vcent: a vbox to be centered, produced by \vcenter. + * + * Over, Under, Acc, Rad and Vcent are not considered in the enum + * below. The relvant elements will be considered as Ord. + */ +enum MathClass { + MC_ORD, + MC_OP, + MC_BIN, + MC_REL, + MC_OPEN, + MC_CLOSE, + MC_PUNCT, + MC_INNER, + MC_UNKNOWN +}; + + +MathClass string_to_class(docstring const &); + +docstring const class_to_string(MathClass); + +void update_class(MathClass & mc, MathClass const prev, MathClass const next); + +int class_spacing(MathClass const mc1, MathClass const mc2, + MetricsBase const & mb); + +} // namespace lyx + +#endif diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp index bd4d53ead2..f2100afb34 100644 --- a/src/mathed/MathData.cpp +++ b/src/mathed/MathData.cpp @@ -30,12 +30,10 @@ #include "mathed/InsetMathUnknown.h" +#include "frontends/FontMetrics.h" + #include "support/debug.h" #include "support/docstream.h" - -#include "frontends/FontMetrics.h" -#include "frontends/Painter.h" - #include "support/gettext.h" #include "support/lassert.h" #include "support/lyxalgo.h" @@ -218,6 +216,34 @@ void MathData::touch() const } +bool MathData::addToMathRow(MathRow & mrow, MetricsInfo & mi) const +{ + bool has_contents = false; + BufferView * bv = mi.base.bv; + MathData * ar = const_cast<MathData*>(this); + ar->updateMacros(&bv->cursor(), mi.macrocontext, + InternalUpdate); + + // FIXME: for completion, try to insert the relevant data in the + // mathrow (like is done for text rows). We could add a pair of + // InsetMathColor inset, but these come with extra spacing of + // their own. + DocIterator const & inlineCompletionPos = bv->inlineCompletionPos(); + bool const has_completion = inlineCompletionPos.inMathed() + && &inlineCompletionPos.cell() == this; + size_t const compl_pos = has_completion ? inlineCompletionPos.pos() : 0; + + for (size_t i = 0 ; i < size() ; ++i) { + has_contents |= (*this)[i]->addToMathRow(mrow, mi); + if (i + 1 == compl_pos) { + mrow.back().compl_text = bv->inlineCompletion(); + mrow.back().compl_unique_to = bv->inlineCompletionUniqueChars(); + } + } + return has_contents; +} + + #if 0 namespace { @@ -247,53 +273,18 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const mindes_ = (3 * xascent) / 4; slevel_ = (4 * xascent) / 5; sshift_ = xascent / 4; - kerning_ = 0; - if (empty()) { - // Cache the dimension. - mi.base.bv->coordCache().arrays().add(this, dim); - return; - } + MathRow mrow(mi, this); + mrow_cache_[mi.base.bv] = mrow; + mrow.metrics(mi, dim); + kerning_ = mrow.kerning(mi.base.bv); - Cursor & cur = mi.base.bv->cursor(); - const_cast<MathData*>(this)->updateMacros(&cur, mi.macrocontext, InternalUpdate); - - DocIterator const & inlineCompletionPos = mi.base.bv->inlineCompletionPos(); - MathData const * inlineCompletionData = 0; - if (inlineCompletionPos.inMathed()) - inlineCompletionData = &inlineCompletionPos.cell(); - - dim.asc = 0; - dim.wid = 0; - Dimension d; - CoordCache::Insets & coords = mi.base.bv->coordCache().insets(); - for (pos_type i = 0, n = size(); i != n; ++i) { - MathAtom const & at = operator[](i); - at->metrics(mi, d); - coords.add(at.nucleus(), d); - dim += d; - if (i == n - 1) - kerning_ = at->kerning(mi.base.bv); - - // HACK to draw completion suggestion inline - if (inlineCompletionData != this - || size_t(inlineCompletionPos.pos()) != i + 1) - continue; - - docstring const & completion = mi.base.bv->inlineCompletion(); - if (completion.length() == 0) - continue; - - FontInfo font = mi.base.font; - augmentFont(font, "mathnormal"); - dim.wid += mathed_string_width(font, completion); - } // Cache the dimension. mi.base.bv->coordCache().arrays().add(this, dim); } -void MathData::draw(PainterInfo & pi, int x, int y) const +void MathData::draw(PainterInfo & pi, int const x, int const y) const { //lyxerr << "MathData::draw: x: " << x << " y: " << y << endl; BufferView & bv = *pi.base.bv; @@ -301,11 +292,6 @@ void MathData::draw(PainterInfo & pi, int x, int y) const Dimension const & dim = bv.coordCache().getArrays().dim(this); - if (empty()) { - pi.pain.rectangle(x, y - dim.ascent(), dim.width(), dim.height(), Color_mathline); - return; - } - // don't draw outside the workarea if (y + dim.descent() <= 0 || y - dim.ascent() >= bv.workHeight() @@ -313,48 +299,8 @@ void MathData::draw(PainterInfo & pi, int x, int y) const || x >= bv. workWidth()) return; - DocIterator const & inlineCompletionPos = bv.inlineCompletionPos(); - MathData const * inlineCompletionData = 0; - if (inlineCompletionPos.inMathed()) - inlineCompletionData = &inlineCompletionPos.cell(); - - CoordCache::Insets & coords = pi.base.bv->coordCache().insets(); - for (size_t i = 0, n = size(); i != n; ++i) { - MathAtom const & at = operator[](i); - coords.add(at.nucleus(), x, y); - at->drawSelection(pi, x, y); - at->draw(pi, x, y); - x += coords.dim(at.nucleus()).wid; - - // Is the inline completion here? - if (inlineCompletionData != this - || size_t(inlineCompletionPos.pos()) != i + 1) - continue; - docstring const & completion = bv.inlineCompletion(); - if (completion.length() == 0) - continue; - FontInfo f = pi.base.font; - augmentFont(f, "mathnormal"); - - // draw the unique and the non-unique completion part - // Note: this is not time-critical as it is - // only done once per screen. - size_t uniqueTo = bv.inlineCompletionUniqueChars(); - docstring s1 = completion.substr(0, uniqueTo); - docstring s2 = completion.substr(uniqueTo); - - if (!s1.empty()) { - f.setColor(Color_inlinecompletion); - pi.pain.text(x, y, s1, f); - x += mathed_string_width(f, s1); - } - - if (!s2.empty()) { - f.setColor(Color_nonunique_inlinecompletion); - pi.pain.text(x, y, s2, f); - x += mathed_string_width(f, s2); - } - } + MathRow const & mrow = mrow_cache_[pi.base.bv]; + mrow.draw(pi, x, y); } diff --git a/src/mathed/MathData.h b/src/mathed/MathData.h index 4b79f805fe..9eae46671a 100644 --- a/src/mathed/MathData.h +++ b/src/mathed/MathData.h @@ -16,7 +16,9 @@ #define MATH_DATA_H #include "Dimension.h" + #include "MathAtom.h" +#include "MathRow.h" #include "OutputEnums.h" @@ -24,6 +26,7 @@ #include <cstddef> #include <vector> +#include <map> namespace lyx { @@ -117,6 +120,10 @@ public: MathAtom & operator[](pos_type); /// checked read access MathAtom const & operator[](pos_type) const; + + /// Add this array to a math row. Return true if contents got added + bool addToMathRow(MathRow &, MetricsInfo & mi) const; + /// rebuild cached metrics information void metrics(MetricsInfo & mi, Dimension & dim) const; /// @@ -177,6 +184,9 @@ protected: mutable int kerning_; Buffer * buffer_; + /// cached object that describes typeset data + mutable std::map<BufferView*, MathRow> mrow_cache_; + private: /// is this an exact match at this position? bool find1(MathData const & ar, size_type pos) const; diff --git a/src/mathed/MathFactory.cpp b/src/mathed/MathFactory.cpp index bb7be8ba6b..f00093746f 100644 --- a/src/mathed/MathFactory.cpp +++ b/src/mathed/MathFactory.cpp @@ -19,6 +19,7 @@ #include "InsetMathCancel.h" #include "InsetMathCancelto.h" #include "InsetMathCases.h" +#include "InsetMathClass.h" #include "InsetMathColor.h" #include "InsetMathDecoration.h" #include "InsetMathDots.h" @@ -662,10 +663,13 @@ MathAtom createInsetMath(docstring const & s, Buffer * buf) return MathAtom(new InsetMathSpecialChar(s)); if (s == " ") return MathAtom(new InsetMathSpace(" ", "")); - if (s == "regexp") return MathAtom(new InsetMathHull(buf, hullRegexp)); + MathClass const mc = string_to_class(s); + if (mc != MC_UNKNOWN) + return MathAtom(new InsetMathClass(buf, mc)); + return MathAtom(new MathMacro(buf, s)); } diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp index 4a94e03189..a5b1803f6d 100644 --- a/src/mathed/MathMacro.cpp +++ b/src/mathed/MathMacro.cpp @@ -68,7 +68,49 @@ public: /// InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; } /// + bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const + { + // macro arguments are in macros + LATTEST(mi.base.macro_nesting > 0); + if (mi.base.macro_nesting == 1) + mi.base.macro_nesting = 0; + + MathRow::Element e_beg(MathRow::BEG_ARG, mi); + e_beg.macro = mathMacro_; + e_beg.ar = &mathMacro_->cell(idx_); + mrow.push_back(e_beg); + + mathMacro_->macro()->unlock(); + bool has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, mi); + mathMacro_->macro()->lock(); + + // if there was no contents, and the contents is editable, + // then we insert a box instead. + if (!has_contents && mi.base.macro_nesting == 0) { + MathRow::Element e(MathRow::BOX, mi); + e.color = Color_mathline; + mrow.push_back(e); + has_contents = true; + } + + if (mi.base.macro_nesting == 0) + mi.base.macro_nesting = 1; + + MathRow::Element e_end(MathRow::END_ARG, mi); + e_end.macro = mathMacro_; + e_end.ar = &mathMacro_->cell(idx_); + + mrow.push_back(e_end); + + return has_contents; + } + /// void metrics(MetricsInfo & mi, Dimension & dim) const { + // macro arguments are in macros + LATTEST(mi.base.macro_nesting > 0); + if (mi.base.macro_nesting == 1) + mi.base.macro_nesting = 0; + mathMacro_->macro()->unlock(); mathMacro_->cell(idx_).metrics(mi, dim); @@ -77,6 +119,8 @@ public: def_.metrics(mi, dim); mathMacro_->macro()->lock(); + if (mi.base.macro_nesting == 0) + mi.base.macro_nesting = 1; } // write(), normalize(), infoize() and infoize2() are not needed since // MathMacro uses the definition and not the expanded cells. @@ -94,6 +138,10 @@ public: void octave(OctaveStream & os) const { os << mathMacro_->cell(idx_); } /// void draw(PainterInfo & pi, int x, int y) const { + LATTEST(pi.base.macro_nesting > 0); + if (pi.base.macro_nesting == 1) + pi.base.macro_nesting = 0; + if (mathMacro_->editMetrics(pi.base.bv)) { // The only way a ArgumentProxy can appear is in a cell of the // MathMacro. Moreover the cells are only drawn in the DISPLAY_FOLDED @@ -111,6 +159,9 @@ public: def_.draw(pi, x, y); } else mathMacro_->cell(idx_).draw(pi, x, y); + + if (pi.base.macro_nesting == 0) + pi.base.macro_nesting = 1; } /// size_t idx() const { return idx_; } @@ -266,6 +317,45 @@ MathMacro::~MathMacro() } +bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const +{ + // set edit mode for which we will have calculated row. + // This is the same as what is done in metrics(). + d->editing_[mi.base.bv] = editMode(mi.base.bv); + + if (displayMode() != MathMacro::DISPLAY_NORMAL + || d->editing_[mi.base.bv]) + return InsetMath::addToMathRow(mrow, mi); + + MathRow::Element e_beg(MathRow::BEG_MACRO, mi); + e_beg.macro = this; + mrow.push_back(e_beg); + + ++mi.base.macro_nesting; + + d->macro_->lock(); + bool has_contents = d->expanded_.addToMathRow(mrow, mi); + d->macro_->unlock(); + + // if there was no contents and the array is editable, then we + // insert a grey box instead. + if (!has_contents && mi.base.macro_nesting == 1) { + MathRow::Element e(MathRow::BOX, mi); + e.color = Color_mathmacroblend; + mrow.push_back(e); + has_contents = true; + } + + --mi.base.macro_nesting; + + MathRow::Element e_end(MathRow::END_MACRO, mi); + e_end.macro = this; + mrow.push_back(e_end); + + return has_contents; +} + + Inset * MathMacro::clone() const { MathMacro * copy = new MathMacro(*this); @@ -344,7 +434,7 @@ bool MathMacro::editMode(BufferView const * bv) const { } -MacroData const * MathMacro::macro() +MacroData const * MathMacro::macro() const { return d->macro_; } @@ -358,6 +448,9 @@ bool MathMacro::editMetrics(BufferView const * bv) const void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const { + // the macro contents is not editable (except the arguments) + ++mi.base.macro_nesting; + // set edit mode for which we will have calculated metrics. But only d->editing_[mi.base.bv] = editMode(mi.base.bv); @@ -371,7 +464,7 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const dim.wid += bsdim.width() + 1; dim.asc = max(bsdim.ascent(), dim.ascent()); dim.des = max(bsdim.descent(), dim.descent()); - metricsMarkers(dim); + metricsMarkers(mi, dim); } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST && d->editing_[mi.base.bv]) { // Macro will be edited in a old-style list mode here: @@ -411,7 +504,7 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const dim.asc += 1; dim.des += 1; dim.wid += 2; - metricsMarkers2(dim); + metricsMarkers2(mi, dim); } else { LBUFERR(d->macro_); @@ -446,6 +539,9 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const dim.des += 2; } } + + // restore macro nesting + --mi.base.macro_nesting; } @@ -614,6 +710,9 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const dim.height() - 2, Color_mathmacroframe); drawMarkers2(pi, expx, expy); } else { + // the macro contents is not editable (except the arguments) + ++pi.base.macro_nesting; + bool drawBox = lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX; bool upshape = currentMode() == TEXT_MODE; Changer dummy = pi.base.font.changeShape(upshape ? UP_SHAPE @@ -648,8 +747,11 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const } else d->expanded_.draw(pi, expx, expy); + --pi.base.macro_nesting; + if (!drawBox) drawMarkers(pi, x, y); + } // edit mode changed? diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h index 1a8654ac40..4eb93ba534 100644 --- a/src/mathed/MathMacro.h +++ b/src/mathed/MathMacro.h @@ -37,6 +37,10 @@ public: /// virtual MathMacro const * asMacro() const { return this; } /// + /// If the macro is in normal edit mode, dissolve its contents in + /// the row. Otherwise, just insert the inset. + bool addToMathRow(MathRow &, MetricsInfo & mi) const; + /// void draw(PainterInfo & pi, int x, int y) const; /// draw selection background void drawSelection(PainterInfo & pi, int x, int y) const; @@ -45,6 +49,8 @@ public: { drawMarkers2(pi, x, y); } /// void metrics(MetricsInfo & mi, Dimension & dim) const; + /// was the macro in edit mode when computing metrics? + bool editMetrics(BufferView const * bv) const; /// int kerning(BufferView const * bv) const; /// get cursor position @@ -117,6 +123,8 @@ public: /// docstring name() const; /// + MacroData const * macro() const; + /// docstring macroName() const; /// bool validName() const; @@ -151,10 +159,6 @@ protected: /// attach arguments (maybe less than arity at the end of an MathData), /// including the optional ones (even if it can be empty here) void attachArguments(std::vector<MathData> const & args, size_t arity, int optionals); - /// - MacroData const * macro(); - /// - bool editMetrics(BufferView const * bv) const; private: /// diff --git a/src/mathed/MathMacroTemplate.cpp b/src/mathed/MathMacroTemplate.cpp index cf7427205d..b4d1c1cfcb 100644 --- a/src/mathed/MathMacroTemplate.cpp +++ b/src/mathed/MathMacroTemplate.cpp @@ -269,7 +269,7 @@ Inset * InsetMathWrapper::clone() const void InsetMathWrapper::metrics(MetricsInfo & mi, Dimension & dim) const { value_->metrics(mi, dim); - //metricsMarkers2(dim); + //metricsMarkers2(mi, dim); } diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp new file mode 100644 index 0000000000..3dbfda6974 --- /dev/null +++ b/src/mathed/MathRow.cpp @@ -0,0 +1,319 @@ +/** + * \file MathRow.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#include <config.h> + +#include "MathRow.h" + +#include "InsetMath.h" +#include "MathClass.h" +#include "MathData.h" +#include "MathMacro.h" +#include "MathSupport.h" + +#include "BufferView.h" +#include "CoordCache.h" +#include "MetricsInfo.h" + +#include "frontends/FontMetrics.h" +#include "frontends/Painter.h" + +#include "support/debug.h" +#include "support/docstring.h" +#include "support/lassert.h" + +#include <ostream> + +using namespace std; + +namespace lyx { + + +MathRow::Element::Element(Type t, MetricsInfo &mi) + : type(t), macro_nesting(mi.base.macro_nesting), + inset(0), mclass(MC_ORD), before(0), after(0), compl_unique_to(0), + macro(0), color(Color_red) +{} + + +MathRow::MathRow(MetricsInfo & mi, MathData const * ar) +{ + // First there is a dummy element of type "open" + push_back(Element(BEGIN, mi)); + back().mclass = MC_OPEN; + + // Then insert the MathData argument + bool const has_contents = ar->addToMathRow(*this, mi); + + // empty arrays are visible when they are editable + // we reserve the necessary space anyway (even if nothing gets drawn) + if (!has_contents) { + Element e(BOX, mi); + e.color = Color_mathline; + push_back(e); + } + + // Finally there is a dummy element of type "close" + push_back(Element(END, mi)); + back().mclass = MC_CLOSE; + + /* Do spacing only in math mode. This test is a bit clumsy, + * but it is used in other places for guessing the current mode. + */ + if (!isMathFont(mi.base.fontname)) + return; + + // update classes + for (int i = 1 ; i != static_cast<int>(elements_.size()) - 1 ; ++i) { + if (elements_[i].type != INSET) + continue; + update_class(elements_[i].mclass, elements_[before(i)].mclass, + elements_[after(i)].mclass); + } + + // set spacing + // We go to the end to handle spacing at the end of equation + for (int i = 1 ; i != static_cast<int>(elements_.size()) ; ++i) { + if (elements_[i].type != INSET) + continue; + Element & bef = elements_[before(i)]; + int spc = class_spacing(bef.mclass, elements_[i].mclass, mi.base); + bef.after = spc / 2; + // this is better than spc / 2 to avoid rounding problems + elements_[i].before = spc - spc / 2; + } + // Do not lose spacing allocated to extremities + if (!elements_.empty()) { + elements_[after(0)].before += elements_.front().after; + elements_[before(elements_.size() - 1)].after += elements_.back().before; + } +} + + +int MathRow::before(int i) const +{ + do + --i; + while (elements_[i].type != BEGIN + && elements_[i].type != INSET); + + return i; +} + + +int MathRow::after(int i) const +{ + do + ++i; + while (elements_[i].type != END + && elements_[i].type != INSET); + + return i; +} + + +void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const +{ + dim.asc = 0; + dim.wid = 0; + // In order to compute the dimension of macros and their + // arguments, it is necessary to keep track of them. + map<MathMacro const *, Dimension> dim_macros; + map<MathData const *, Dimension> dim_arrays; + CoordCache & coords = mi.base.bv->coordCache(); + for (Element const & e : elements_) { + Dimension d; + mi.base.macro_nesting = e.macro_nesting; + switch (e.type) { + case BEGIN: + case END: + break; + case INSET: + e.inset->metrics(mi, d); + d.wid += e.before + e.after; + coords.insets().add(e.inset, d); + break; + case BEG_MACRO: + e.macro->macro()->lock(); + // Add a macro to current list + dim_macros[e.macro] = Dimension(); + break; + case END_MACRO: + LATTEST(dim_macros.find(e.macro) != dim_macros.end()); + e.macro->macro()->unlock(); + // Cache the dimension of the macro and remove it from + // tracking map. + coords.insets().add(e.macro, dim_macros[e.macro]); + dim_macros.erase(e.macro); + break; + // This is basically like macros + case BEG_ARG: + if (e.macro) + e.macro->macro()->unlock(); + dim_arrays[e.ar] = Dimension(); + break; + case END_ARG: + LATTEST(dim_arrays.find(e.ar) != dim_arrays.end()); + if (e.macro) + e.macro->macro()->lock(); + coords.arrays().add(e.ar, dim_arrays[e.ar]); + dim_arrays.erase(e.ar); + break; + case BOX: + d = theFontMetrics(mi.base.font).dimension('I'); + d.wid += e.before + e.after; + break; + } + + if (!d.empty()) { + dim += d; + // Now add the dimension to current macros and arguments. + for (auto & dim_macro : dim_macros) + dim_macro.second += d; + for (auto & dim_array : dim_arrays) + dim_array.second += d; + } + + if (e.compl_text.empty()) + continue; + FontInfo font = mi.base.font; + augmentFont(font, "mathnormal"); + dim.wid += mathed_string_width(font, e.compl_text); + } + LATTEST(dim_macros.empty() && dim_arrays.empty()); +} + + +void MathRow::draw(PainterInfo & pi, int x, int const y) const +{ + CoordCache & coords = pi.base.bv->coordCache(); + for (Element const & e : elements_) { + pi.base.macro_nesting = e.macro_nesting; + switch (e.type) { + case INSET: { + // This is hackish: the math inset does not know that space + // has been added before and after it; we alter its dimension + // while it is drawing, because it relies on this value. + Dimension const d = coords.insets().dim(e.inset); + Dimension d2 = d; + d2.wid -= e.before + e.after; + coords.insets().add(e.inset, d2); + e.inset->drawSelection(pi, x + e.before, y); + e.inset->draw(pi, x + e.before, y); + coords.insets().add(e.inset, x, y); + coords.insets().add(e.inset, d); + x += d.wid; + break; + } + case BEG_MACRO: + coords.insets().add(e.macro, x, y); + break; + case BEG_ARG: + coords.arrays().add(e.ar, x, y); + // if the macro is being edited, then the painter is in + // monochrome mode. + if (e.macro->editMetrics(pi.base.bv)) + pi.pain.leaveMonochromeMode(); + break; + case END_ARG: + if (e.macro->editMetrics(pi.base.bv)) + pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend); + break; + case BOX: { + Dimension const d = theFontMetrics(pi.base.font).dimension('I'); + // the box is not visible in non-editable context (except for grey macro boxes). + if (e.macro_nesting == 0 || e.color == Color_mathmacroblend) + pi.pain.rectangle(x + e.before, y - d.ascent(), + d.width(), d.height(), e.color); + x += d.wid; + break; + } + case BEGIN: + case END: + case END_MACRO: + break; + } + + if (e.compl_text.empty()) + continue; + FontInfo f = pi.base.font; + augmentFont(f, "mathnormal"); + + // draw the unique and the non-unique completion part + // Note: this is not time-critical as it is + // only done once per screen. + docstring const s1 = e.compl_text.substr(0, e.compl_unique_to); + docstring const s2 = e.compl_text.substr(e.compl_unique_to); + + if (!s1.empty()) { + f.setColor(Color_inlinecompletion); + pi.pain.text(x, y, s1, f); + x += mathed_string_width(f, s1); + } + if (!s2.empty()) { + f.setColor(Color_nonunique_inlinecompletion); + pi.pain.text(x, y, s2, f); + x += mathed_string_width(f, s2); + } + } +} + + +int MathRow::kerning(BufferView const * bv) const +{ + if (elements_.empty()) + return 0; + InsetMath const * inset = elements_[before(elements_.size() - 1)].inset; + return inset ? inset->kerning(bv) : 0; +} + + +ostream & operator<<(ostream & os, MathRow::Element const & e) +{ + switch (e.type) { + case MathRow::BEGIN: + os << "{"; + break; + case MathRow::END: + os << "}"; + break; + case MathRow::INSET: + os << "<" << e.before << "-" + << to_utf8(class_to_string(e.mclass)) + << "-" << e.after << ">"; + break; + case MathRow::BEG_MACRO: + os << "\\" << to_utf8(e.macro->name()) << "["; + break; + case MathRow::END_MACRO: + os << "]"; + break; + case MathRow::BEG_ARG: + os << "#("; + break; + case MathRow::END_ARG: + os << ")"; + break; + case MathRow::BOX: + os << "@"; + break; + } + return os; +} + + +ostream & operator<<(ostream & os, MathRow const & mrow) +{ + for (MathRow::Element const & e : mrow) + os << e << " "; + return os; +} + +} // namespace lyx diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h new file mode 100644 index 0000000000..3d31e1cbcb --- /dev/null +++ b/src/mathed/MathRow.h @@ -0,0 +1,145 @@ +// -*- C++ -*- +/** + * \file MathRow.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef MATH_ROW_H +#define MATH_ROW_H + +#include "MathClass.h" + +#include "ColorCode.h" + +#include "support/docstring.h" + +#include <vector> + +namespace lyx { + +class BufferView; +class Dimension; +class MetricsInfo; +class PainterInfo; + +class InsetMath; +class MathData; +class MathMacro; + +/* + * While for editing purpose it is important that macros are counted + * as a single element, this is not the case for display. To get the + * spacing correct, it is necessary to dissolve all the macros that + * can be, along with their arguments. Then one obtains a + * representation of the MathData contents as a string of insets and + * then spacing can be done properly. + * + * This is the purpose of the MathRow class. + */ +class MathRow +{ +public: + // What row elements can be + enum Type { + INSET, // this element is a plain inset + BEG_MACRO, // a macro begins here + END_MACRO, // a macro ends here + BEG_ARG, // a macro argument begins here + END_ARG, // a macro argument ends here + BEGIN, // dummy element before row + END, // dummy element after row + BOX // an empty box + }; + + // An elements, together with its spacing + struct Element + { + /// + Element(Type t, MetricsInfo & mi); + + /// Classifies the contents of the object + Type type; + /// count wether the current mathdata is nested in macro(s) + int macro_nesting; + + /// When type is INSET + /// the math inset + InsetMath const * inset; + /// the class of the inset + MathClass mclass; + /// the spacing around the inset + int before, after; + // Non empty when there is a completion to draw + docstring compl_text; + // the number of characters forming the unique part. + size_t compl_unique_to; + + /// When type is BEG_MACRO, END_MACRO, BEG_ARG, END_ARG + /// the math macro + MathMacro const * macro; + + // type is BEG_ARG, END_ARG + MathData const * ar; + + // type is BOX + ColorCode color; + }; + + /// + MathRow() {}; + /// + typedef std::vector<Element> Elements; + /// + typedef Elements::iterator iterator; + /// + typedef Elements::const_iterator const_iterator; + /// + iterator begin() { return elements_.begin(); } + /// + iterator end() { return elements_.end(); } + /// + const_iterator begin() const { return elements_.begin(); } + /// + const_iterator end() const { return elements_.end(); } + // + void push_back(Element const & e) { elements_.push_back(e); } + // + Element & back() { return elements_.back(); } + + // create the math row by unwinding all macros in the MathData and + // compute the spacings. + MathRow(MetricsInfo & mi, MathData const * ar); + + // + void metrics(MetricsInfo & mi, Dimension & dim) const; + // + void draw(PainterInfo & pi, int const x, int const y) const; + + /// superscript kerning + int kerning(BufferView const *) const; + +private: + // Index of the first inset element before position i + int before(int i) const; + // Index of the first inset element after position i + int after(int i) const; + + /// + Elements elements_; +}; + +/// +std::ostream & operator<<(std::ostream & os, MathRow::Element const & elt); + +/// +std::ostream & operator<<(std::ostream & os, MathRow const & mrow); + + +} // namespace lyx + +#endif diff --git a/src/mathed/MathSupport.cpp b/src/mathed/MathSupport.cpp index 0c610b1e69..3d708406d3 100644 --- a/src/mathed/MathSupport.cpp +++ b/src/mathed/MathSupport.cpp @@ -528,6 +528,9 @@ int mathed_font_em(FontInfo const & font) * punctuation, and is put around inner objects, except where these * are followed by a close or preceded by an open symbol, and except * if the other object is a large operator or a binary relation. + * + * See the file MathClass.cpp for a formal implementation of the rules + * above. */ int mathed_thinmuskip(FontInfo font) @@ -671,13 +674,6 @@ void mathedSymbolDim(MetricsInfo & mi, Dimension & dim, latexkeys const * sym) std::string const font = italic_upcase_greek ? "cmm" : sym->inset; Changer dummy = mi.base.changeFontSet(font); mathed_string_dim(mi.base.font, sym->draw, dim); - // seperate things a bit - if (sym->extra == "mathbin") - dim.wid += 2 * mathed_medmuskip(mi.base.font); - else if (sym->extra == "mathrel") - dim.wid += 2 * mathed_thickmuskip(mi.base.font); - else if (sym->extra == "mathpunct") - dim.wid += mathed_thinmuskip(mi.base.font); } @@ -693,10 +689,6 @@ void mathedSymbolDraw(PainterInfo & pi, int x, int y, latexkeys const * sym) sym->extra == "mathalpha" && pi.base.fontname == "mathit"; std::string const font = italic_upcase_greek ? "cmm" : sym->inset; - if (sym->extra == "mathbin") - x += mathed_medmuskip(pi.base.font); - else if (sym->extra == "mathrel") - x += mathed_thickmuskip(pi.base.font); Changer dummy = pi.base.changeFontSet(font); pi.draw(x, y, sym->draw);