--- /dev/null
+/**
+ * \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
--- /dev/null
+// -*- 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