From 5261ae6a29aef4d517e83580f52e532c91f85a06 Mon Sep 17 00:00:00 2001 From: Georg Baum Date: Sun, 17 Feb 2013 11:08:58 +0100 Subject: [PATCH] Fix bug #1424: Native \sideset inset The toolbar image is the one Uwe attached to the bug report. Note that \sideset works only for operators like \sum in the nucleus. LyX allows any content, so you might get a LaTeX error. I don't know how to prevent wrong content in the nucleus. --- lib/Makefile.am | 1 + lib/images/math/sideset.png | Bin 0 -> 139 bytes lib/ui/stdtoolbars.inc | 1 + src/Makefile.am | 2 + src/mathed/InsetMathScript.cpp | 27 --- src/mathed/InsetMathSideset.cpp | 415 ++++++++++++++++++++++++++++++++ src/mathed/InsetMathSideset.h | 113 +++++++++ src/mathed/MathFactory.cpp | 3 + src/mathed/MathParser.cpp | 23 ++ src/mathed/MathSupport.cpp | 24 +- src/mathed/MathSupport.h | 2 + 11 files changed, 583 insertions(+), 28 deletions(-) create mode 100644 lib/images/math/sideset.png create mode 100644 src/mathed/InsetMathSideset.cpp create mode 100644 src/mathed/InsetMathSideset.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 2b620d3140..a428b3d1a7 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1109,6 +1109,7 @@ dist_imagesmath_DATA = \ images/math/shortparallel.png \ images/math/shortrightarrow.png \ images/math/shortuparrow.png \ + images/math/sideset.png \ images/math/sigma.png \ images/math/sigma2.png \ images/math/sim.png \ diff --git a/lib/images/math/sideset.png b/lib/images/math/sideset.png new file mode 100644 index 0000000000000000000000000000000000000000..02a20fe4ac6406760db178b1490beb84010edc66 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^q98U08<2FHq?!Pv*pj^6T^Rm@;DWu&Cj&(|3p^r= z85p>QL70(Y)*K0-pth%rV~EA+ using namespace std; @@ -148,31 +146,6 @@ MathData & InsetMathScript::nuc() } -namespace { - -bool isAlphaSymbol(MathAtom const & at) -{ - if (at->asCharInset() || - (at->asSymbolInset() && - at->asSymbolInset()->isOrdAlpha())) - return true; - - if (at->asFontInset()) { - MathData const & ar = at->asFontInset()->cell(0); - for (size_t i = 0; i < ar.size(); ++i) { - if (!(ar[i]->asCharInset() || - (ar[i]->asSymbolInset() && - ar[i]->asSymbolInset()->isOrdAlpha()))) - return false; - } - return true; - } - return false; -} - -} // namespace anon - - int InsetMathScript::dy01(BufferView const & bv, int asc, int des, int what) const { int dasc = 0; diff --git a/src/mathed/InsetMathSideset.cpp b/src/mathed/InsetMathSideset.cpp new file mode 100644 index 0000000000..9d1e51bd1b --- /dev/null +++ b/src/mathed/InsetMathSideset.cpp @@ -0,0 +1,415 @@ +/** + * \file InsetMathSideset.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author André Pönitz + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "BufferView.h" +#include "Cursor.h" +#include "DispatchResult.h" +#include "FuncRequest.h" +#include "FuncStatus.h" +#include "InsetMathSideset.h" +#include "InsetMathSymbol.h" +#include "LaTeXFeatures.h" +#include "MathData.h" +#include "MathStream.h" +#include "MathSupport.h" + +#include "support/debug.h" + +#include "support/lassert.h" + + +using namespace std; + + +namespace { + /// x spacing between the nucleus and the scripts + int const dx = 2; +} + + +namespace lyx { + +InsetMathSideset::InsetMathSideset(Buffer * buf) + : InsetMathNest(buf, 5) +{} + + +InsetMathSideset::InsetMathSideset(Buffer * buf, MathAtom const & at) + : InsetMathNest(buf, 5) +{ + nuc().push_back(at); +} + + +Inset * InsetMathSideset::clone() const +{ + return new InsetMathSideset(*this); +} + + +bool InsetMathSideset::idxFirst(Cursor & cur) const +{ + cur.idx() = 0; + cur.pos() = 0; + return true; +} + + +bool InsetMathSideset::idxLast(Cursor & cur) const +{ + cur.idx() = 0; + cur.pos() = nuc().size(); + return true; +} + + +int InsetMathSideset::dybt(BufferView const & bv, int asc, int des, bool top) const +{ + bool isCharBox = nuc().empty() ? false : isAlphaSymbol(nuc().back()); + int dasc = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent()); + int slevel = nuc().slevel(); + int ascdrop = dasc - slevel; + int desdrop = isCharBox ? 0 : des + nuc().sshift(); + int mindes = nuc().mindes(); + des = max(desdrop, ascdrop); + des = max(mindes, des); + int minasc = nuc().minasc(); + ascdrop = isCharBox ? 0 : asc - min(tl().mindes(), tr().mindes()); + int udes = max(bl().dimension(bv).descent(), tr().dimension(bv).descent()); + asc = udes + nuc().sshift(); + asc = max(ascdrop, asc); + asc = max(minasc, asc); + int del = asc - udes - dasc; + if (del + des <= 2) { + int newdes = 2 - del; + del = slevel - asc + udes; + if (del > 0) { + asc += del; + newdes -= del; + } + des = max(des, newdes); + } + return top ? asc : des; +} + + +int InsetMathSideset::dyb(BufferView const & bv) const +{ + int nd = ndes(bv); + int des = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent()); + int na = nasc(bv); + des = dybt(bv, na, nd, false); + return des; +} + + +int InsetMathSideset::dyt(BufferView const & bv) const +{ + int na = nasc(bv); + int asc = max(tl().dimension(bv).descent(), tr().dimension(bv).descent()); + int nd = ndes(bv); + asc = dybt(bv, na, nd, true); + return asc; +} + + +int InsetMathSideset::dxr(BufferView const & bv) const +{ + return dxn(bv) + nwid(bv) + dx; +} + + +int InsetMathSideset::dxn(BufferView const & bv) const +{ + Dimension const dimb = bl().dimension(bv); + Dimension const dimt = tl().dimension(bv); + return max(dimb.width(), dimt.width()) + dx; +} + + +int InsetMathSideset::nwid(BufferView const & bv) const +{ + return nuc().dimension(bv).width(); +} + + +int InsetMathSideset::nasc(BufferView const & bv) const +{ + return nuc().dimension(bv).ascent(); +} + + +int InsetMathSideset::ndes(BufferView const & bv) const +{ + return nuc().dimension(bv).descent(); +} + + +int InsetMathSideset::nker(BufferView const * bv) const +{ + int const kerning = nuc().kerning(bv); + return max(kerning, 0); +} + + +void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const +{ + Dimension dimn; + Dimension dimbl; + Dimension dimtl; + Dimension dimbr; + Dimension dimtr; + nuc().metrics(mi, dimn); + ScriptChanger dummy(mi.base); + bl().metrics(mi, dimbl); + tl().metrics(mi, dimtl); + br().metrics(mi, dimbr); + tr().metrics(mi, dimtr); + + BufferView & bv = *mi.base.bv; + // FIXME: data copying... not very efficient. + + dim.wid = nwid(bv) + nker(mi.base.bv) + 2 * dx; + dim.wid += max(dimbl.width(), dimtl.width()); + dim.wid += max(dimbr.width(), dimtr.width()); + int na = nasc(bv); + int asc = dyt(bv) + max(dimtl.ascent(), dimtr.ascent()); + dim.asc = max(na, asc); + int nd = ndes(bv); + int des = dyb(bv) + max(dimbl.descent(), dimbr.descent()); + dim.des = max(nd, des); + metricsMarkers(dim); +} + + +void InsetMathSideset::draw(PainterInfo & pi, int x, int y) const +{ + BufferView & bv = *pi.base.bv; + nuc().draw(pi, x + dxn(bv), y); + ScriptChanger dummy(pi.base); + bl().draw(pi, x , y + dyb(bv)); + tl().draw(pi, x , y - dyt(bv)); + br().draw(pi, x + dxr(bv), y + dyb(bv)); + tr().draw(pi, x + dxr(bv), y - dyt(bv)); + drawMarkers(pi, x, y); +} + + +void InsetMathSideset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const +{ + bl().metricsT(mi, dim); + tl().metricsT(mi, dim); + br().metricsT(mi, dim); + tr().metricsT(mi, dim); + nuc().metricsT(mi, dim); +} + + +void InsetMathSideset::drawT(TextPainter & pain, int x, int y) const +{ + // FIXME: BROKEN + nuc().drawT(pain, x + 1, y); + bl().drawT(pain, x + 1, y + 1 /*dy0()*/); + tl().drawT(pain, x + 1, y - 1 /*dy1()*/); + br().drawT(pain, x + 1, y + 1 /*dy0()*/); + tr().drawT(pain, x + 1, y - 1 /*dy1()*/); +} + + + +bool InsetMathSideset::idxForward(Cursor &) const +{ + return false; +} + + +bool InsetMathSideset::idxBackward(Cursor &) const +{ + return false; +} + + +bool InsetMathSideset::idxUpDown(Cursor & cur, bool up) const +{ + // in nucleus? + if (cur.idx() == 0) { + // go up/down only if in the last position + // or in the first position + if (cur.pos() == cur.lastpos() || cur.pos() == 0) { + if (cur.pos() == 0) + cur.idx() = up ? 2 : 1; + else + cur.idx() = up ? 4 : 3; + cur.pos() = 0; + return true; + } + return false; + } + + // Are we 'up'? + if (cur.idx() == 2 || cur.idx() == 4) { + // can't go further up + if (up) + return false; + // otherwise go to first or last position in the nucleus + cur.idx() = 0; + if (cur.idx() == 2) + cur.pos() = 0; + else + cur.pos() = cur.lastpos(); + return true; + } + + // Are we 'down'? + if (cur.idx() == 1 || cur.idx() == 3) { + // can't go further down + if (!up) + return false; + // otherwise go to first or last position in the nucleus + cur.idx() = 0; + if (cur.idx() == 1) + cur.pos() = 0; + else + cur.pos() = cur.lastpos(); + return true; + } + + return false; +} + + +void InsetMathSideset::write(WriteStream & os) const +{ + MathEnsurer ensurer(os); + + os << "\\sideset"; + for (int i = 0; i < 2; ++i) { + os << '{'; + if (!cell(2*i+1).empty()) + os << "_{" << cell(2*i+1) << '}'; + if (!cell(2*i+2).empty()) + os << "^{" << cell(2*i+2) << '}'; + os << '}'; + } + os << '{' << nuc() << '}'; + + if (lock_ && !os.latex()) + os << "\\lyxlock "; +} + + +void InsetMathSideset::normalize(NormalStream & os) const +{ + os << "[sideset "; + + if (!bl().empty()) + os << bl() << ' '; + if (!tl().empty()) + os << tl() << ' '; + + if (!nuc().empty()) + os << nuc() << ' '; + else + os << "[par]"; + + if (!br().empty()) + os << br() << ' '; + if (!tr().empty()) + os << tr() << ' '; + os << ']'; +} + + +void InsetMathSideset::mathmlize(MathStream & os) const +{ + os << MTag("mmultiscripts"); + + if (nuc().empty()) + os << ""; + else + os << MTag("mrow") << nuc() << ETag("mrow"); + + if (br().empty()) + os << ""; + else + os << MTag("mrow") << br() << ETag("mrow"); + if (tr().empty()) + os << ""; + else + os << MTag("mrow") << tr() << ETag("mrow"); + + if (bl().empty()) + os << ""; + else + os << MTag("mrow") << bl() << ETag("mrow"); + if (tl().empty()) + os << ""; + else + os << MTag("mrow") << tl() << ETag("mrow"); + + os << ETag("mmultiscripts"); +} + + +void InsetMathSideset::htmlize(HtmlStream & os) const +{ + bool const havebl = !bl().empty(); + bool const havetl = !tl().empty(); + bool const havebr = !br().empty(); + bool const havetr = !tr().empty(); + + if (havebl && havetl) + os << MTag("span", "class='scripts'") + << MTag("span") << tl() << ETag("span") + << MTag("span") << bl() << ETag("span") + << ETag("span"); + else if (havebl) + os << MTag("sub", "class='math'") << bl() << ETag("sub"); + else if (havetl) + os << MTag("sup", "class='math'") << tl() << ETag("sup"); + + if (!nuc().empty()) + os << nuc(); + + if (havebr && havetr) + os << MTag("span", "class='scripts'") + << MTag("span") << tr() << ETag("span") + << MTag("span") << br() << ETag("span") + << ETag("span"); + else if (havebr) + os << MTag("sub", "class='math'") << br() << ETag("sub"); + else if (havetr) + os << MTag("sup", "class='math'") << tr() << ETag("sup"); +} + + +void InsetMathSideset::infoize(odocstream & os) const +{ + os << "Sideset"; +} + + +// the idea for dual scripts came from the eLyXer code +void InsetMathSideset::validate(LaTeXFeatures & features) const +{ + if (features.runparams().math_flavor == OutputParams::MathAsHTML) + features.addCSSSnippet( + "span.scripts{display: inline-block; vertical-align: middle; text-align:center; font-size: 75%;}\n" + "span.scripts span {display: block;}\n" + "sub.math{font-size: 75%;}\n" + "sup.math{font-size: 75%;}"); + features.require("amsmath"); + InsetMathNest::validate(features); +} + +} // namespace lyx diff --git a/src/mathed/InsetMathSideset.h b/src/mathed/InsetMathSideset.h new file mode 100644 index 0000000000..67f7eaa06f --- /dev/null +++ b/src/mathed/InsetMathSideset.h @@ -0,0 +1,113 @@ +// -*- C++ -*- +/** + * \file InsetMathSideset.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author André Pönitz + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef MATH_SIDESETINSET_H +#define MATH_SIDESETINSET_H + +#include "InsetMathNest.h" + + +namespace lyx { + + +/// An inset for amsmath \sideset. The 'nucleus' is always cell 0. +/// cell(1) is the bottom left index, cell(2) is the top left index, +/// cell(3) is the bottom right index, and cell(4) is top right index. +class InsetMathSideset : public InsetMathNest { +public: + /// + InsetMathSideset(Buffer * buf); + /// create inset with given nucleus + InsetMathSideset(Buffer * buf, MathAtom const & at); + /// + mode_type currentMode() const { return MATH_MODE; } + /// + void metrics(MetricsInfo & mi, Dimension & dim) const; + /// + void draw(PainterInfo & pi, int x, int y) const; + /// + void metricsT(TextMetricsInfo const & mi, Dimension & dim) const; + /// + void drawT(TextPainter & pi, int x, int y) const; + + /// move cursor backwards + bool idxBackward(Cursor & cur) const; + /// move cursor forward + bool idxForward(Cursor & cur) const; + /// move cursor up or down + bool idxUpDown(Cursor & cur, bool up) const; + /// Target pos when we enter the inset while moving forward + bool idxFirst(Cursor & cur) const; + /// Target pos when we enter the inset while moving backwards + bool idxLast(Cursor & cur) const; + + /// write LaTeX and Lyx code + void write(WriteStream & os) const; + /// write normalized content + void normalize(NormalStream &) const; + /// write content as MathML + void mathmlize(MathStream &) const; + /// write content as HTML + void htmlize(HtmlStream &) const; + + /// returns nucleus + MathData const & nuc() const { return cell(0); }; + /// returns nucleus + MathData & nuc() { return cell(0); }; + /// bottom left index + MathData const & bl() const { return cell(1); } + /// bottom left index + MathData & bl() { return cell(1); } + /// top left index + MathData const & tl() const { return cell(2); } + /// top left index + MathData & tl() { return cell(2); } + /// bottom right index + MathData const & br() const { return cell(3); } + /// bottom right index + MathData & br() { return cell(3); } + /// top right index + MathData const & tr() const { return cell(4); } + /// top right index + MathData & tr() { return cell(4); } + /// say that we have scripts + void infoize(odocstream & os) const; + /// + InsetCode lyxCode() const { return MATH_SCRIPT_CODE; } + /// + void validate(LaTeXFeatures &features) const; +private: + virtual Inset * clone() const; + /// returns x offset of nucleus + int dxn(BufferView const & bv) const; + /// returns width of nucleus if any + int nwid(BufferView const &) const; + /// returns y offset for either superscript or subscript + int dybt(BufferView const &, int asc, int des, bool top) const; + /// returns y offset for superscript + int dyt(BufferView const &) const; + /// returns y offset for subscript + int dyb(BufferView const &) const; + /// returns x offset for right subscript and superscript + int dxr(BufferView const & bv) const; + /// returns ascent of nucleus if any + int nasc(BufferView const &) const; + /// returns descent of nucleus if any + int ndes(BufferView const &) const; + /// returns subscript and superscript kerning of nucleus if any + int nker(BufferView const * bv) const; +}; + + +} // namespace lyx + +#endif diff --git a/src/mathed/MathFactory.cpp b/src/mathed/MathFactory.cpp index ac3ac98619..d2bd2e8cb3 100644 --- a/src/mathed/MathFactory.cpp +++ b/src/mathed/MathFactory.cpp @@ -32,6 +32,7 @@ #include "InsetMathPhantom.h" #include "InsetMathRef.h" #include "InsetMathRoot.h" +#include "InsetMathSideset.h" #include "InsetMathSize.h" #include "InsetMathSpace.h" #include "InsetMathSpecialChar.h" @@ -535,6 +536,8 @@ MathAtom createInsetMath(docstring const & s, Buffer * buf) return MathAtom(new InsetMathPhantom(buf, InsetMathPhantom::mathrlap)); if (s == "ensuremath") return MathAtom(new InsetMathEnsureMath(buf)); + if (s == "sideset") + return MathAtom(new InsetMathSideset(buf)); if (isSpecialChar(s)) return MathAtom(new InsetMathSpecialChar(s)); if (s == " ") diff --git a/src/mathed/MathParser.cpp b/src/mathed/MathParser.cpp index 3f20df5eb7..61e000ccc2 100644 --- a/src/mathed/MathParser.cpp +++ b/src/mathed/MathParser.cpp @@ -56,6 +56,7 @@ following hack as starting point to write some macros: #include "InsetMathRef.h" #include "InsetMathRoot.h" #include "InsetMathScript.h" +#include "InsetMathSideset.h" #include "InsetMathSpace.h" #include "InsetMathSplit.h" #include "InsetMathSqrt.h" @@ -1442,6 +1443,28 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode); } + else if (t.cs() == "sideset") { + // Here allowed formats are \sideset{_{bl}^{tl}}{_{br}^{tr}}{operator} + cell->push_back(MathAtom(new InsetMathSideset(buf))); + for (int i = 0; i < 2; ++i) { + MathData ar; + parse(ar, FLAG_ITEM, mode); + if (!ar.empty()) { + InsetMathScript * script = (ar.size() == 1) ? + ar[0].nucleus()->asScriptInset() : 0; + if (!script) { + error("found invalid sideset argument"); + break; + } + if (script->hasDown()) + cell->back().nucleus()->cell(2 * i + 1) = script->down(); + if (script->hasUp()) + cell->back().nucleus()->cell(2 * i + 2) = script->up(); + } + } + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + else if (t.cs() == "stackrel") { // Here allowed formats are \stackrel[subscript]{superscript}{operator} MathData ar; diff --git a/src/mathed/MathSupport.cpp b/src/mathed/MathSupport.cpp index 0a87fd69ba..6a815ed898 100644 --- a/src/mathed/MathSupport.cpp +++ b/src/mathed/MathSupport.cpp @@ -13,7 +13,8 @@ #include "MathSupport.h" -#include "InsetMath.h" +#include "InsetMathFont.h" +#include "InsetMathSymbol.h" #include "MathData.h" #include "MathParser.h" #include "MathStream.h" @@ -736,6 +737,27 @@ void augmentFont(FontInfo & font, docstring const & name) } +bool isAlphaSymbol(MathAtom const & at) +{ + if (at->asCharInset() || + (at->asSymbolInset() && + at->asSymbolInset()->isOrdAlpha())) + return true; + + if (at->asFontInset()) { + MathData const & ar = at->asFontInset()->cell(0); + for (size_t i = 0; i < ar.size(); ++i) { + if (!(ar[i]->asCharInset() || + (ar[i]->asSymbolInset() && + ar[i]->asSymbolInset()->isOrdAlpha()))) + return false; + } + return true; + } + return false; +} + + docstring asString(MathData const & ar) { odocstringstream os; diff --git a/src/mathed/MathSupport.h b/src/mathed/MathSupport.h index 968be725ef..f31606615f 100644 --- a/src/mathed/MathSupport.h +++ b/src/mathed/MathSupport.h @@ -56,6 +56,8 @@ bool isMathFont(docstring const & name); bool isTextFont(docstring const & name); +bool isAlphaSymbol(MathAtom const & at); + // converts single cell to string docstring asString(MathData const & ar); // converts single inset to string -- 2.39.2