2 * \file InsetMathSideset.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
14 #include "BufferView.h"
16 #include "DispatchResult.h"
17 #include "FuncRequest.h"
18 #include "FuncStatus.h"
19 #include "InsetMathSideset.h"
20 #include "InsetMathSymbol.h"
21 #include "LaTeXFeatures.h"
23 #include "MathStream.h"
24 #include "MathSupport.h"
26 #include "support/debug.h"
28 #include "support/lassert.h"
35 /// x spacing between the nucleus and the scripts
42 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr)
43 : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
48 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr,
50 : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
57 Inset * InsetMathSideset::clone() const
59 return new InsetMathSideset(*this);
63 bool InsetMathSideset::idxFirst(Cursor & cur) const
71 bool InsetMathSideset::idxLast(Cursor & cur) const
74 cur.pos() = nuc().size();
79 int InsetMathSideset::dybt(BufferView const & bv, int asc, int des, bool top) const
81 bool isCharBox = nuc().empty() ? false : isAlphaSymbol(nuc().back());
83 if (scriptl_ && scriptr_)
84 dasc = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
86 dasc = bl().dimension(bv).ascent();
88 dasc = br().dimension(bv).ascent();
89 int slevel = nuc().slevel();
90 int ascdrop = dasc - slevel;
91 int desdrop = isCharBox ? 0 : des + nuc().sshift();
92 int mindes = nuc().mindes();
93 des = max(desdrop, ascdrop);
94 des = max(mindes, des);
95 int minasc = nuc().minasc();
97 if (!isCharBox && (scriptl_ || scriptr_)) {
98 if (scriptl_ && scriptr_)
99 ascdrop = asc - min(tl().mindes(), tr().mindes());
101 ascdrop = asc - tl().mindes();
103 ascdrop = asc - tr().mindes();
107 udes = bl().dimension(bv).descent();
109 udes = max(udes, br().dimension(bv).descent());
110 asc = udes + nuc().sshift();
111 asc = max(ascdrop, asc);
112 asc = max(minasc, asc);
113 int del = asc - udes - dasc;
114 if (del + des <= 2) {
115 int newdes = 2 - del;
116 del = slevel - asc + udes;
121 des = max(des, newdes);
123 return top ? asc : des;
127 int InsetMathSideset::dyb(BufferView const & bv) const
131 if (scriptl_ && scriptr_)
132 des = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
134 des = bl().dimension(bv).ascent();
136 des = br().dimension(bv).ascent();
138 des = dybt(bv, na, nd, false);
143 int InsetMathSideset::dyt(BufferView const & bv) const
147 if (scriptl_ && scriptr_)
148 asc = max(tl().dimension(bv).descent(), tr().dimension(bv).descent());
150 asc = tl().dimension(bv).descent();
152 asc = tr().dimension(bv).descent();
154 asc = dybt(bv, na, nd, true);
159 int InsetMathSideset::dxr(BufferView const & bv) const
161 return dxn(bv) + nwid(bv) + dx;
165 int InsetMathSideset::dxn(BufferView const & bv) const
167 Dimension const dimb = bl().dimension(bv);
168 Dimension const dimt = tl().dimension(bv);
169 return max(dimb.width(), dimt.width()) + dx;
173 int InsetMathSideset::nwid(BufferView const & bv) const
175 return nuc().dimension(bv).width();
179 int InsetMathSideset::nasc(BufferView const & bv) const
181 return nuc().dimension(bv).ascent();
185 int InsetMathSideset::ndes(BufferView const & bv) const
187 return nuc().dimension(bv).descent();
191 int InsetMathSideset::nker(BufferView const * bv) const
193 int const kerning = nuc().kerning(bv);
194 return max(kerning, 0);
198 void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const
205 nuc().metrics(mi, dimn);
207 bl().metrics(mi, dimbl);
211 br().metrics(mi, dimbr);
214 ScriptChanger dummy(mi.base);
216 bl().metrics(mi, dimbl);
217 tl().metrics(mi, dimtl);
220 br().metrics(mi, dimbr);
221 tr().metrics(mi, dimtr);
224 BufferView & bv = *mi.base.bv;
225 // FIXME: data copying... not very efficient.
227 dim.wid = nwid(bv) + nker(mi.base.bv) + 2 * dx;
228 dim.wid += max(dimbl.width(), dimtl.width());
229 dim.wid += max(dimbr.width(), dimtr.width());
231 int asc = dyt(bv) + max(dimtl.ascent(), dimtr.ascent());
232 dim.asc = max(na, asc);
234 int des = dyb(bv) + max(dimbl.descent(), dimbr.descent());
235 dim.des = max(nd, des);
240 void InsetMathSideset::draw(PainterInfo & pi, int x, int y) const
242 BufferView & bv = *pi.base.bv;
243 nuc().draw(pi, x + dxn(bv), y);
245 bl().draw(pi, x , y);
247 br().draw(pi, x + dxr(bv), y);
248 ScriptChanger dummy(pi.base);
250 bl().draw(pi, x , y + dyb(bv));
251 tl().draw(pi, x , y - dyt(bv));
254 br().draw(pi, x + dxr(bv), y + dyb(bv));
255 tr().draw(pi, x + dxr(bv), y - dyt(bv));
257 drawMarkers(pi, x, y);
261 void InsetMathSideset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
263 bl().metricsT(mi, dim);
264 tl().metricsT(mi, dim);
265 br().metricsT(mi, dim);
266 tr().metricsT(mi, dim);
267 nuc().metricsT(mi, dim);
271 void InsetMathSideset::drawT(TextPainter & pain, int x, int y) const
274 nuc().drawT(pain, x + 1, y);
275 bl().drawT(pain, x + 1, y + 1 /*dy0()*/);
276 tl().drawT(pain, x + 1, y - 1 /*dy1()*/);
277 br().drawT(pain, x + 1, y + 1 /*dy0()*/);
278 tr().drawT(pain, x + 1, y - 1 /*dy1()*/);
283 bool InsetMathSideset::idxForward(Cursor & cur) const
285 if (!scriptl_ && cur.idx() == 1) {
289 } else if (!scriptr_ && cur.idx() == 0) {
291 cur.idx() = 2 + scriptl_;
298 bool InsetMathSideset::idxBackward(Cursor & cur) const
300 if (!scriptr_ && cur.idx() == (scriptl_ ? 3 : 2)) {
304 } else if (!scriptl_ && cur.idx() == 0) {
313 bool InsetMathSideset::idxUpDown(Cursor & cur, bool up) const
316 if (cur.idx() == 0) {
317 // go up/down only if in the last position
318 // or in the first position
319 if ((scriptr_ && cur.pos() == cur.lastpos()) ||
320 (scriptl_ && cur.pos() == 0)) {
322 cur.idx() = up ? 2 : 1;
324 cur.idx() = (up ? 3 : 2) + scriptl_;
332 if ((scriptl_ && cur.idx() == 2) ||
333 (scriptr_ && cur.idx() == (scriptl_ ? 4 : 3))) {
334 // can't go further up
337 // otherwise go to first or last position in the nucleus
342 cur.pos() = cur.lastpos();
347 if ((scriptl_ && cur.idx() == 1) ||
348 (scriptr_ && cur.idx() == (scriptl_ ? 3 : 2))) {
349 // can't go further down
352 // otherwise go to first or last position in the nucleus
357 cur.pos() = cur.lastpos();
365 void InsetMathSideset::write(WriteStream & os) const
367 MathEnsurer ensurer(os);
373 os << "_{" << bl() << '}';
375 os << "^{" << tl() << '}';
381 os << "_{" << br() << '}';
383 os << "^{" << tr() << '}';
389 if (lock_ && !os.latex())
394 void InsetMathSideset::normalize(NormalStream & os) const
400 if (scriptl_ && !tl().empty())
410 if (scriptr_ && !tr().empty())
416 void InsetMathSideset::mathmlize(MathStream & os) const
418 // FIXME This is only accurate if both scriptl_ and scriptr_ are true
420 os << MTag("mrow") << bl() << ETag("mrow");
421 if (scriptl_ || scriptr_) {
422 os << MTag("mmultiscripts");
427 os << MTag("mrow") << nuc() << ETag("mrow");
429 if (br().empty() || !scriptr_)
432 os << MTag("mrow") << br() << ETag("mrow");
433 if (tr().empty() || !scriptr_)
436 os << MTag("mrow") << tr() << ETag("mrow");
438 if (bl().empty() || !scriptl_)
441 os << MTag("mrow") << bl() << ETag("mrow");
442 if (tl().empty() || !scriptl_)
445 os << MTag("mrow") << tl() << ETag("mrow");
447 os << ETag("mmultiscripts");
450 os << MTag("mrow") << br() << ETag("mrow");
454 void InsetMathSideset::htmlize(HtmlStream & os) const
456 // FIXME This is only accurate if both scriptl_ and scriptr_ are true
457 bool const havebl = scriptl_ && !bl().empty();
458 bool const havetl = scriptl_ && !tl().empty();
459 bool const havebr = scriptr_ && !br().empty();
460 bool const havetr = scriptr_ && !tr().empty();
462 if (!scriptl_ && !bl().empty())
465 if (havebl && havetl)
466 os << MTag("span", "class='scripts'")
467 << MTag("span") << tl() << ETag("span")
468 << MTag("span") << bl() << ETag("span")
471 os << MTag("sub", "class='math'") << bl() << ETag("sub");
473 os << MTag("sup", "class='math'") << tl() << ETag("sup");
478 if (havebr && havetr)
479 os << MTag("span", "class='scripts'")
480 << MTag("span") << tr() << ETag("span")
481 << MTag("span") << br() << ETag("span")
484 os << MTag("sub", "class='math'") << br() << ETag("sub");
486 os << MTag("sup", "class='math'") << tr() << ETag("sup");
488 if (!scriptr_ && !br().empty())
493 void InsetMathSideset::infoize(odocstream & os) const
499 // the idea for dual scripts came from the eLyXer code
500 void InsetMathSideset::validate(LaTeXFeatures & features) const
502 if (features.runparams().math_flavor == OutputParams::MathAsHTML)
503 features.addCSSSnippet(
504 "span.scripts{display: inline-block; vertical-align: middle; text-align:center; font-size: 75%;}\n"
505 "span.scripts span {display: block;}\n"
506 "sub.math{font-size: 75%;}\n"
507 "sup.math{font-size: 75%;}");
508 features.require("amsmath");
509 InsetMathNest::validate(features);