2 * \file InsetMathScript.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "InsetMathScript.h"
15 #include "MathStream.h"
16 #include "MathSupport.h"
17 #include "InsetMathSymbol.h"
18 #include "InsetMathFont.h"
19 #include "dispatchresult.h"
22 #include "funcrequest.h"
25 #include <boost/assert.hpp>
37 InsetMathScript::InsetMathScript()
38 : InsetMathNest(1), cell_1_is_up_(false), limits_(0)
42 InsetMathScript::InsetMathScript(bool up)
43 : InsetMathNest(2), cell_1_is_up_(up), limits_(0)
47 InsetMathScript::InsetMathScript(MathAtom const & at, bool up)
48 : InsetMathNest(2), cell_1_is_up_(up), limits_(0)
50 BOOST_ASSERT(nargs() >= 1);
51 cell(0).push_back(at);
55 auto_ptr<InsetBase> InsetMathScript::doClone() const
57 return auto_ptr<InsetBase>(new InsetMathScript(*this));
61 InsetMathScript const * InsetMathScript::asScriptInset() const
67 InsetMathScript * InsetMathScript::asScriptInset()
73 bool InsetMathScript::idxFirst(LCursor & cur) const
81 bool InsetMathScript::idxLast(LCursor & cur) const
84 cur.pos() = nuc().size();
89 MathArray const & InsetMathScript::down() const
93 BOOST_ASSERT(nargs() > 1);
98 MathArray & InsetMathScript::down()
102 BOOST_ASSERT(nargs() > 1);
107 MathArray const & InsetMathScript::up() const
109 BOOST_ASSERT(nargs() > 1);
114 MathArray & InsetMathScript::up()
116 BOOST_ASSERT(nargs() > 1);
121 void InsetMathScript::ensure(bool up)
124 // just nucleus so far
125 cells_.push_back(MathArray());
127 } else if (nargs() == 2 && !has(up)) {
129 cells_.push_back(cell(1));
132 cells_.push_back(MathArray());
138 MathArray const & InsetMathScript::nuc() const
144 MathArray & InsetMathScript::nuc()
152 bool isAlphaSymbol(MathAtom const & at)
154 if (at->asCharInset() ||
155 (at->asSymbolInset() &&
156 at->asSymbolInset()->isOrdAlpha()))
159 if (at->asFontInset()) {
160 MathArray const & ar = at->asFontInset()->cell(0);
161 for (size_t i = 0; i < ar.size(); ++i) {
162 if (!(ar[i]->asCharInset() ||
163 (ar[i]->asSymbolInset() &&
164 ar[i]->asSymbolInset()->isOrdAlpha())))
175 int InsetMathScript::dy01(int asc, int des, int what) const
179 bool isCharBox = nuc().size() ? isAlphaSymbol(nuc().back()) : false;
181 dasc = down().ascent();
182 slevel = nuc().slevel();
183 int ascdrop = dasc - slevel;
184 int desdrop = isCharBox ? 0 : des + nuc().sshift();
185 int mindes = nuc().mindes();
186 des = max(desdrop, ascdrop);
187 des = max(mindes, des);
190 int minasc = nuc().minasc();
191 int ascdrop = isCharBox ? 0 : asc - up().mindes();
192 int udes = up().descent();
193 asc = udes + nuc().sshift();
194 asc = max(ascdrop, asc);
195 asc = max(minasc, asc);
197 int del = asc - udes - dasc;
198 if (del + des <= 2) {
199 int newdes = 2 - del;
200 del = slevel - asc + udes;
205 des = max(des, newdes);
209 return what ? asc : des;
213 int InsetMathScript::dy0() const
218 int des = down().ascent();
223 des = dy01(na, nd, 0);
229 int InsetMathScript::dy1() const
234 int asc = up().descent();
239 asc = dy01(na, nd, 1);
246 int InsetMathScript::dx0() const
248 BOOST_ASSERT(hasDown());
249 return hasLimits() ? (dim_.wid - down().width()) / 2 : nwid();
253 int InsetMathScript::dx1() const
255 BOOST_ASSERT(hasUp());
256 return hasLimits() ? (dim_.wid - up().width()) / 2 : nwid() + nker();
260 int InsetMathScript::dxx() const
262 return hasLimits() ? (dim_.wid - nwid()) / 2 : 0;
266 int InsetMathScript::nwid() const
268 return nuc().size() ? nuc().width() : 2;
272 int InsetMathScript::nasc() const
274 return nuc().size() ? nuc().ascent() : 5;
278 int InsetMathScript::ndes() const
280 return nuc().size() ? nuc().descent() : 0;
284 int InsetMathScript::nker() const
287 int kerning = nuc().kerning();
288 return kerning > 0 ? kerning : 0;
294 bool InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
297 ScriptChanger dummy(mi.base);
306 dim.wid = max(dim.wid, up().width());
308 dim.wid = max(dim.wid, down().width());
311 dim.wid = max(dim.wid, nker() + up().width());
313 dim.wid = max(dim.wid, down().width());
318 int asc = dy1() + up().ascent();
319 dim.asc = max(na, asc);
324 int des = dy0() + down().descent();
325 dim.des = max(nd, des);
336 void InsetMathScript::draw(PainterInfo & pi, int x, int y) const
339 nuc().draw(pi, x + dxx(), y);
341 nuc().setXY(*pi.base.bv, x + dxx(), y);
342 if (editing(pi.base.bv))
343 pi.draw(x + dxx(), y, char_type('.'));
345 ScriptChanger dummy(pi.base);
347 up().draw(pi, x + dx1(), y - dy1());
349 down().draw(pi, x + dx0(), y + dy0());
350 drawMarkers(pi, x, y);
354 void InsetMathScript::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
357 up().metricsT(mi, dim);
359 down().metricsT(mi, dim);
360 nuc().metricsT(mi, dim);
364 void InsetMathScript::drawT(TextPainter & pain, int x, int y) const
367 nuc().drawT(pain, x + dxx(), y);
369 up().drawT(pain, x + dx1(), y - dy1());
371 down().drawT(pain, x + dx0(), y + dy0());
376 bool InsetMathScript::hasLimits() const
384 // we can only display limits if the nucleus wants some
387 if (!nuc().back()->isScriptable())
390 if (nuc().back()->asSymbolInset()) {
391 // \intop is an alias for \int\limits, \ointop == \oint\limits
392 if (nuc().back()->asSymbolInset()->name().find(from_ascii("intop")) != string::npos)
394 // per default \int has limits beside the \int even in displayed formulas
395 if (nuc().back()->asSymbolInset()->name().find(from_ascii("int")) != string::npos)
399 // assume "real" limits for everything else
404 void InsetMathScript::removeScript(bool up)
407 if (up == cell_1_is_up_)
409 } else if (nargs() == 3) {
411 swap(cells_[1], cells_[2]);
412 cell_1_is_up_ = false;
414 cell_1_is_up_ = true;
421 bool InsetMathScript::has(bool up) const
423 return idxOfScript(up);
427 bool InsetMathScript::hasUp() const
429 //lyxerr << "1up: " << bool(cell_1_is_up_) << endl;
430 //lyxerr << "hasUp: " << bool(idxOfScript(true)) << endl;
431 return idxOfScript(true);
435 bool InsetMathScript::hasDown() const
437 //lyxerr << "1up: " << bool(cell_1_is_up_) << endl;
438 //lyxerr << "hasDown: " << bool(idxOfScript(false)) << endl;
439 return idxOfScript(false);
443 InsetBase::idx_type InsetMathScript::idxOfScript(bool up) const
448 return (cell_1_is_up_ == up) ? 1 : 0;
457 bool InsetMathScript::idxRight(LCursor &) const
463 bool InsetMathScript::idxLeft(LCursor &) const
469 bool InsetMathScript::idxUpDown(LCursor & cur, bool up) const
472 if (cur.idx() == 0) {
473 // don't go up/down if there is no cell in this direction
476 // go up/down only if in the last position
477 // or in the first position of something with displayed limits
478 if (cur.pos() == cur.lastpos() || (cur.pos() == 0 && hasLimits())) {
479 cur.idx() = idxOfScript(up);
487 if (has(up) && cur.idx() == idxOfScript(true)) {
488 // can't go further up
491 // otherwise go to last position in the nucleus
493 cur.pos() = cur.lastpos();
498 if (has(up) && cur.idx() == idxOfScript(false)) {
499 // can't go further down
502 // otherwise go to last position in the nucleus
504 cur.pos() = cur.lastpos();
512 void InsetMathScript::write(WriteStream & os) const
516 //if (nuc().back()->takesLimits()) {
524 LYXERR(Debug::MATHED) << "suppressing {} when writing"
530 if (hasDown() /*&& down().size()*/)
531 os << "_{" << down() << '}';
533 if (hasUp() /*&& up().size()*/)
534 os << "^{" << up() << '}';
536 if (lock_ && !os.latex())
541 void InsetMathScript::normalize(NormalStream & os) const
543 bool d = hasDown() && down().size();
544 bool u = hasUp() && up().size();
559 os << down() << ' ' << up() << ']';
567 void InsetMathScript::maple(MapleStream & os) const
571 if (hasDown() && down().size())
572 os << '[' << down() << ']';
573 if (hasUp() && up().size())
574 os << "^(" << up() << ')';
578 void InsetMathScript::mathematica(MathematicaStream & os) const
580 bool d = hasDown() && down().size();
581 bool u = hasUp() && up().size();
585 os << "Subscript[" << nuc();
591 os << "^(" << up() << ')';
595 os << ',' << down() << ']';
600 void InsetMathScript::mathmlize(MathStream & os) const
602 bool d = hasDown() && down().size();
603 bool u = hasUp() && up().size();
606 os << MTag("msubsup");
618 os << down() << up() << ETag("msubsup");
620 os << up() << ETag("msup");
622 os << down() << ETag("msub");
626 void InsetMathScript::octave(OctaveStream & os) const
630 if (hasDown() && down().size())
631 os << '[' << down() << ']';
632 if (hasUp() && up().size())
633 os << "^(" << up() << ')';
637 void InsetMathScript::infoize(odocstream & os) const
643 void InsetMathScript::infoize2(odocstream & os) const
646 os << (limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
650 bool InsetMathScript::notifyCursorLeaves(LCursor & cur)
652 InsetMathNest::notifyCursorLeaves(cur);
654 //lyxerr << "InsetMathScript::notifyCursorLeaves: 1 " << cur << endl;
656 // remove empty scripts if possible
658 // Case of two scripts. In this case, 1 = super, 2 = sub
659 if (cur.idx() == 2 && cell(2).empty()) {
660 // must be a subscript...
661 recordUndoInset(cur);
664 } else if (cur.idx() == 1 && cell(1).empty()) {
665 // must be a superscript...
666 recordUndoInset(cur);
670 } else if (nargs() > 1 && cur.idx() == 1 && cell(1).empty()) {
671 // could be either subscript or super script
672 recordUndoInset(cur);
673 removeScript(cell_1_is_up_);
674 // Let the script inset commit suicide. This is
675 // modelled on LCursor.pullArg(), but tries not to
676 // invoke notifyCursorLeaves again and does not touch
677 // cur (since the top slice will be deleted
679 MathArray ar = cell(0);
680 LCursor tmpcur = cur;
682 tmpcur.cell().erase(tmpcur.pos());
683 tmpcur.cell().insert(tmpcur.pos(), ar);
687 //lyxerr << "InsetMathScript::notifyCursorLeaves: 2 " << cur << endl;
692 void InsetMathScript::doDispatch(LCursor & cur, FuncRequest & cmd)
694 //lyxerr << "InsetMathScript: request: " << cmd << std::endl;
696 if (cmd.action == LFUN_MATH_LIMITS) {
697 if (!cmd.argument().empty()) {
698 if (cmd.argument() == "limits")
700 else if (cmd.argument() == "nolimits")
704 } else if (limits_ == 0)
705 limits_ = hasLimits() ? -1 : 1;
711 InsetMathNest::doDispatch(cur, cmd);