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 "BufferView.h"
15 #include "DispatchResult.h"
16 #include "FuncRequest.h"
17 #include "InsetMathFont.h"
18 #include "InsetMathScript.h"
19 #include "InsetMathSymbol.h"
21 #include "MathStream.h"
22 #include "MathSupport.h"
24 #include "support/debug.h"
26 #include "support/lassert.h"
35 InsetMathScript::InsetMathScript(Buffer
* buf
)
36 : InsetMathNest(buf
, 1), cell_1_is_up_(false), limits_(0)
40 InsetMathScript::InsetMathScript(Buffer
* buf
, bool up
)
41 : InsetMathNest(buf
, 2), cell_1_is_up_(up
), limits_(0)
45 InsetMathScript::InsetMathScript(Buffer
* buf
, MathAtom
const & at
, bool up
)
46 : InsetMathNest(buf
, 2), cell_1_is_up_(up
), limits_(0)
48 LASSERT(nargs() >= 1, /**/);
49 cell(0).push_back(at
);
53 Inset
* InsetMathScript::clone() const
55 return new InsetMathScript(*this);
59 InsetMathScript
const * InsetMathScript::asScriptInset() const
65 InsetMathScript
* InsetMathScript::asScriptInset()
71 bool InsetMathScript::idxFirst(Cursor
& cur
) const
79 bool InsetMathScript::idxLast(Cursor
& cur
) const
82 cur
.pos() = nuc().size();
87 MathData
const & InsetMathScript::down() const
91 LASSERT(nargs() > 1, /**/);
96 MathData
& InsetMathScript::down()
100 LASSERT(nargs() > 1, /**/);
105 MathData
const & InsetMathScript::up() const
107 LASSERT(nargs() > 1, /**/);
112 MathData
& InsetMathScript::up()
114 LASSERT(nargs() > 1, /**/);
119 void InsetMathScript::ensure(bool up
)
122 // just nucleus so far
123 cells_
.push_back(MathData());
125 } else if (nargs() == 2 && !has(up
)) {
127 cells_
.push_back(cell(1));
130 cells_
.push_back(MathData());
136 MathData
const & InsetMathScript::nuc() const
142 MathData
& InsetMathScript::nuc()
150 bool isAlphaSymbol(MathAtom
const & at
)
152 if (at
->asCharInset() ||
153 (at
->asSymbolInset() &&
154 at
->asSymbolInset()->isOrdAlpha()))
157 if (at
->asFontInset()) {
158 MathData
const & ar
= at
->asFontInset()->cell(0);
159 for (size_t i
= 0; i
< ar
.size(); ++i
) {
160 if (!(ar
[i
]->asCharInset() ||
161 (ar
[i
]->asSymbolInset() &&
162 ar
[i
]->asSymbolInset()->isOrdAlpha())))
173 int InsetMathScript::dy01(BufferView
const & bv
, int asc
, int des
, int what
) const
177 bool isCharBox
= nuc().size() ? isAlphaSymbol(nuc().back()) : false;
179 Dimension
const & dimdown
= down().dimension(bv
);
180 dasc
= dimdown
.ascent();
181 slevel
= nuc().slevel();
182 int ascdrop
= dasc
- slevel
;
183 int desdrop
= isCharBox
? 0 : des
+ nuc().sshift();
184 int mindes
= nuc().mindes();
185 des
= max(desdrop
, ascdrop
);
186 des
= max(mindes
, des
);
189 Dimension
const & dimup
= up().dimension(bv
);
190 int minasc
= nuc().minasc();
191 int ascdrop
= isCharBox
? 0 : asc
- up().mindes();
192 int udes
= dimup
.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(BufferView
const & bv
) const
218 int des
= down().dimension(bv
).ascent();
223 des
= dy01(bv
, na
, nd
, 0);
229 int InsetMathScript::dy1(BufferView
const & bv
) const
234 int asc
= up().dimension(bv
).descent();
239 asc
= dy01(bv
, na
, nd
, 1);
246 int InsetMathScript::dx0(BufferView
const & bv
) const
248 LASSERT(hasDown(), /**/);
249 Dimension
const dim
= dimension(bv
);
250 return hasLimits() ? (dim
.wid
- down().dimension(bv
).width()) / 2 : nwid(bv
);
254 int InsetMathScript::dx1(BufferView
const & bv
) const
256 LASSERT(hasUp(), /**/);
257 Dimension
const dim
= dimension(bv
);
258 return hasLimits() ? (dim
.wid
- up().dimension(bv
).width()) / 2 : nwid(bv
) + nker(&bv
);
262 int InsetMathScript::dxx(BufferView
const & bv
) const
264 Dimension
const dim
= dimension(bv
);
265 return hasLimits() ? (dim
.wid
- nwid(bv
)) / 2 : 0;
269 int InsetMathScript::nwid(BufferView
const & bv
) const
271 return nuc().size() ? nuc().dimension(bv
).width() : 2;
275 int InsetMathScript::nasc(BufferView
const & bv
) const
277 return nuc().size() ? nuc().dimension(bv
).ascent() : 5;
281 int InsetMathScript::ndes(BufferView
const & bv
) const
283 return nuc().size() ? nuc().dimension(bv
).descent() : 0;
287 int InsetMathScript::nker(BufferView
const * bv
) const
290 int kerning
= nuc().kerning(bv
);
291 return kerning
> 0 ? kerning
: 0;
297 void InsetMathScript::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
302 cell(0).metrics(mi
, dim0
);
303 ScriptChanger
dummy(mi
.base
);
305 cell(1).metrics(mi
, dim1
);
307 cell(2).metrics(mi
, dim2
);
310 BufferView
& bv
= *mi
.base
.bv
;
311 // FIXME: data copying... not very efficient.
315 dimup
= up().dimension(bv
);
317 dimdown
= down().dimension(bv
);
322 dim
.wid
= max(dim
.wid
, dimup
.width());
324 dim
.wid
= max(dim
.wid
, dimdown
.width());
327 dim
.wid
= max(dim
.wid
, nker(mi
.base
.bv
) + dimup
.width());
329 dim
.wid
= max(dim
.wid
, dimdown
.width());
334 int asc
= dy1(bv
) + dimup
.ascent();
335 dim
.asc
= max(na
, asc
);
340 int des
= dy0(bv
) + dimdown
.descent();
341 dim
.des
= max(nd
, des
);
348 void InsetMathScript::draw(PainterInfo
& pi
, int x
, int y
) const
350 BufferView
& bv
= *pi
.base
.bv
;
352 nuc().draw(pi
, x
+ dxx(bv
), y
);
354 nuc().setXY(bv
, x
+ dxx(bv
), y
);
356 pi
.draw(x
+ dxx(bv
), y
, char_type('.'));
358 ScriptChanger
dummy(pi
.base
);
360 up().draw(pi
, x
+ dx1(bv
), y
- dy1(bv
));
362 down().draw(pi
, x
+ dx0(bv
), y
+ dy0(bv
));
363 drawMarkers(pi
, x
, y
);
367 void InsetMathScript::metricsT(TextMetricsInfo
const & mi
, Dimension
& dim
) const
370 up().metricsT(mi
, dim
);
372 down().metricsT(mi
, dim
);
373 nuc().metricsT(mi
, dim
);
377 void InsetMathScript::drawT(TextPainter
& pain
, int x
, int y
) const
381 nuc().drawT(pain
, x
+ 1, y
);
383 up().drawT(pain
, x
+ 1, y
- 1 /*dy1()*/);
385 down().drawT(pain
, x
+ 1, y
+ 1 /*dy0()*/);
390 bool InsetMathScript::hasLimits() const
398 // we can only display limits if the nucleus wants some
401 if (!nuc().back()->isScriptable())
404 if (nuc().back()->asSymbolInset()) {
405 // \intop is an alias for \int\limits, \ointop == \oint\limits
406 if (nuc().back()->asSymbolInset()->name().find(from_ascii("intop")) != string::npos
)
408 // per default \int has limits beside the \int even in displayed formulas
409 if (nuc().back()->asSymbolInset()->name().find(from_ascii("int")) != string::npos
)
413 // assume "real" limits for everything else
418 void InsetMathScript::removeScript(bool up
)
421 if (up
== cell_1_is_up_
)
423 } else if (nargs() == 3) {
425 swap(cells_
[1], cells_
[2]);
426 cell_1_is_up_
= false;
428 cell_1_is_up_
= true;
435 bool InsetMathScript::has(bool up
) const
437 return idxOfScript(up
);
441 bool InsetMathScript::hasUp() const
443 //lyxerr << "1up: " << bool(cell_1_is_up_));
444 //lyxerr << "hasUp: " << bool(idxOfScript(true)));
445 return idxOfScript(true);
449 bool InsetMathScript::hasDown() const
451 //LYXERR0("1up: " << bool(cell_1_is_up_));
452 //LYXERR0("hasDown: " << bool(idxOfScript(false)));
453 return idxOfScript(false);
457 Inset::idx_type
InsetMathScript::idxOfScript(bool up
) const
462 return (cell_1_is_up_
== up
) ? 1 : 0;
465 LASSERT(false, /**/);
471 bool InsetMathScript::idxForward(Cursor
&) const
477 bool InsetMathScript::idxBackward(Cursor
&) const
483 bool InsetMathScript::idxUpDown(Cursor
& cur
, bool up
) const
486 if (cur
.idx() == 0) {
487 // don't go up/down if there is no cell in this direction
490 // go up/down only if in the last position
491 // or in the first position of something with displayed limits
492 if (cur
.pos() == cur
.lastpos() || (cur
.pos() == 0 && hasLimits())) {
493 cur
.idx() = idxOfScript(up
);
501 if (cur
.idx() == idxOfScript(true)) {
502 // can't go further up
505 // otherwise go to last position in the nucleus
507 cur
.pos() = cur
.lastpos();
512 if (cur
.idx() == idxOfScript(false)) {
513 // can't go further down
516 // otherwise go to last position in the nucleus
518 cur
.pos() = cur
.lastpos();
526 void InsetMathScript::write(WriteStream
& os
) const
528 MathEnsurer
ensurer(os
);
532 //if (nuc().back()->takesLimits()) {
540 LYXERR(Debug::MATHED
, "suppressing {} when writing");
545 if (hasDown() /*&& down().size()*/)
546 os
<< "_{" << down() << '}';
548 if (hasUp() /*&& up().size()*/)
549 os
<< "^{" << up() << '}';
551 if (lock_
&& !os
.latex())
556 void InsetMathScript::normalize(NormalStream
& os
) const
558 bool d
= hasDown() && down().size();
559 bool u
= hasUp() && up().size();
574 os
<< down() << ' ' << up() << ']';
582 void InsetMathScript::maple(MapleStream
& os
) const
586 if (hasDown() && down().size())
587 os
<< '[' << down() << ']';
588 if (hasUp() && up().size())
589 os
<< "^(" << up() << ')';
593 void InsetMathScript::mathematica(MathematicaStream
& os
) const
595 bool d
= hasDown() && down().size();
596 bool u
= hasUp() && up().size();
600 os
<< "Subscript[" << nuc();
606 os
<< "^(" << up() << ')';
610 os
<< ',' << down() << ']';
615 void InsetMathScript::mathmlize(MathStream
& os
) const
617 bool d
= hasDown() && down().size();
618 bool u
= hasUp() && up().size();
621 os
<< MTag("msubsup");
633 os
<< down() << up() << ETag("msubsup");
635 os
<< up() << ETag("msup");
637 os
<< down() << ETag("msub");
641 void InsetMathScript::octave(OctaveStream
& os
) const
645 if (hasDown() && down().size())
646 os
<< '[' << down() << ']';
647 if (hasUp() && up().size())
648 os
<< "^(" << up() << ')';
652 void InsetMathScript::infoize(odocstream
& os
) const
658 void InsetMathScript::infoize2(odocstream
& os
) const
661 os
<< from_ascii(limits_
== 1 ? ", Displayed limits" : ", Inlined limits");
665 bool InsetMathScript::notifyCursorLeaves(Cursor
const & old
, Cursor
& cur
)
667 InsetMathNest::notifyCursorLeaves(old
, cur
);
669 //LYXERR0("InsetMathScript::notifyCursorLeaves: 1 " << cur);
671 // Remove empty scripts if possible:
673 // The case of two scripts, but only one got empty (1 = super, 2 = sub).
674 // We keep the script inset, but remove the empty script.
675 if (nargs() > 2 && (!cell(1).empty() || !cell(2).empty())) {
676 if (cell(2).empty()) {
677 // must be a subscript...
678 old
.recordUndoInset();
680 cur
.updateFlags(cur
.disp_
.update() | Update::SinglePar
);
682 } else if (cell(1).empty()) {
683 // must be a superscript...
684 old
.recordUndoInset();
686 cur
.updateFlags(cur
.disp_
.update() | Update::SinglePar
);
690 // Now the two suicide cases:
691 // * we have only one script which is empty
692 // * we have two scripts which are both empty.
693 // The script inset is removed completely.
694 if ((nargs() == 2 && cell(1).empty())
695 || (nargs() == 3 && cell(1).empty() && cell(2).empty())) {
696 // Make undo step. We cannot use cur for this because
697 // it does not necessarily point to us anymore. But we
698 // should be on top of the cursor old.
699 Cursor insetCur
= old
;
700 int scriptSlice
= insetCur
.find(this);
701 LASSERT(scriptSlice
!= -1, /**/);
702 insetCur
.cutOff(scriptSlice
);
703 insetCur
.recordUndoInset();
705 // Let the script inset commit suicide. This is
706 // modelled on Cursor.pullArg(), but tries not to
707 // invoke notifyCursorLeaves again and does not touch
708 // cur (since the top slice will be deleted
710 MathData ar
= cell(0);
712 insetCur
.cell().erase(insetCur
.pos());
713 insetCur
.cell().insert(insetCur
.pos(), ar
);
716 cur
.updateFlags(cur
.disp_
.update() | Update::SinglePar
);
720 //LYXERR0("InsetMathScript::notifyCursorLeaves: 2 " << cur);
725 void InsetMathScript::doDispatch(Cursor
& cur
, FuncRequest
& cmd
)
727 //LYXERR("InsetMathScript: request: " << cmd);
729 if (cmd
.action
== LFUN_MATH_LIMITS
) {
730 if (!cmd
.argument().empty()) {
731 if (cmd
.argument() == "limits")
733 else if (cmd
.argument() == "nolimits")
737 } else if (limits_
== 0)
738 limits_
= hasLimits() ? -1 : 1;
744 InsetMathNest::doDispatch(cur
, cmd
);