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.
This commit is contained in:
Georg Baum 2013-02-17 11:08:58 +01:00
parent c0395333b2
commit 5261ae6a29
11 changed files with 583 additions and 28 deletions

View File

@ -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 \

BIN
lib/images/math/sideset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

View File

@ -183,6 +183,7 @@ ToolbarSet
Item "Insert sum" "math-insert \sum"
Item "Insert integral" "math-insert \int"
Item "Insert product" "math-insert \prod"
Item "Insert side scripts" "math-insert \sideset"
Separator
Item "Insert ( )" "math-delim ( )"
Item "Insert [ ]" "math-delim [ ]"

View File

@ -409,6 +409,7 @@ SOURCEFILESMATHED = \
mathed/InsetMathRef.cpp \
mathed/InsetMathRoot.cpp \
mathed/InsetMathScript.cpp \
mathed/InsetMathSideset.cpp \
mathed/InsetMathSize.cpp \
mathed/InsetMathSpace.cpp \
mathed/InsetMathSpecialChar.cpp \
@ -477,6 +478,7 @@ HEADERFILESMATHED = \
mathed/InsetMathRef.h \
mathed/InsetMathRoot.h \
mathed/InsetMathScript.h \
mathed/InsetMathSideset.h \
mathed/InsetMathSize.h \
mathed/InsetMathSpace.h \
mathed/InsetMathSpecialChar.h \

View File

@ -16,7 +16,6 @@
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "InsetMathBrace.h"
#include "InsetMathFont.h"
#include "InsetMathScript.h"
#include "InsetMathSymbol.h"
#include "LaTeXFeatures.h"
@ -28,7 +27,6 @@
#include "support/lassert.h"
#include <ostream>
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;

View File

@ -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 <config.h>
#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 << "<mrow />";
else
os << MTag("mrow") << nuc() << ETag("mrow");
if (br().empty())
os << "<none />";
else
os << MTag("mrow") << br() << ETag("mrow");
if (tr().empty())
os << "<none />";
else
os << MTag("mrow") << tr() << ETag("mrow");
if (bl().empty())
os << "<none />";
else
os << MTag("mrow") << bl() << ETag("mrow");
if (tl().empty())
os << "<none />";
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

View File

@ -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

View File

@ -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 == " ")

View File

@ -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;

View File

@ -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;

View File

@ -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