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 "InsetMathSideset.h"
16 #include "InsetMathSymbol.h"
18 #include "MathStream.h"
19 #include "MathSupport.h"
21 #include "BufferView.h"
23 #include "DispatchResult.h"
24 #include "FuncRequest.h"
25 #include "FuncStatus.h"
26 #include "LaTeXFeatures.h"
27 #include "MetricsInfo.h"
29 #include "support/debug.h"
30 #include "support/lassert.h"
37 /// x spacing between the nucleus and the scripts
44 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr)
45 : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
50 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr,
52 : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
59 Inset * InsetMathSideset::clone() const
61 return new InsetMathSideset(*this);
65 int InsetMathSideset::dybt(BufferView const & bv, int asc, int des, bool top) const
67 bool isCharBox = nuc().empty() ? false : isAlphaSymbol(nuc().back());
69 if (scriptl_ && scriptr_)
70 dasc = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
72 dasc = bl().dimension(bv).ascent();
74 dasc = br().dimension(bv).ascent();
75 int slevel = nuc().slevel();
76 int ascdrop = dasc - slevel;
77 int desdrop = isCharBox ? 0 : des + nuc().sshift();
78 int mindes = nuc().mindes();
79 des = max(desdrop, ascdrop);
80 des = max(mindes, des);
81 int minasc = nuc().minasc();
83 if (!isCharBox && (scriptl_ || scriptr_)) {
84 if (scriptl_ && scriptr_)
85 ascdrop = asc - min(tl().mindes(), tr().mindes());
87 ascdrop = asc - tl().mindes();
89 ascdrop = asc - tr().mindes();
93 udes = bl().dimension(bv).descent();
95 udes = max(udes, br().dimension(bv).descent());
96 asc = udes + nuc().sshift();
97 asc = max(ascdrop, asc);
98 asc = max(minasc, asc);
99 int del = asc - udes - dasc;
100 if (del + des <= 2) {
101 int newdes = 2 - del;
102 del = slevel - asc + udes;
107 des = max(des, newdes);
109 return top ? asc : des;
113 int InsetMathSideset::dyb(BufferView const & bv) const
117 int des = dybt(bv, na, nd, false);
122 int InsetMathSideset::dyt(BufferView const & bv) const
126 int asc = dybt(bv, na, nd, true);
131 int InsetMathSideset::dxr(BufferView const & bv) const
133 return dxn(bv) + nwid(bv) + dx;
137 int InsetMathSideset::dxn(BufferView const & bv) const
139 Dimension const dimb = bl().dimension(bv);
140 Dimension const dimt = tl().dimension(bv);
141 return max(dimb.width(), dimt.width()) + dx;
145 int InsetMathSideset::nwid(BufferView const & bv) const
147 return nuc().dimension(bv).width();
151 int InsetMathSideset::nasc(BufferView const & bv) const
153 return nuc().dimension(bv).ascent();
157 int InsetMathSideset::ndes(BufferView const & bv) const
159 return nuc().dimension(bv).descent();
163 int InsetMathSideset::nker(BufferView const * bv) const
165 int const kerning = nuc().kerning(bv);
166 return max(kerning, 0);
170 void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const
172 Changer dummy2 = mi.base.changeEnsureMath();
178 nuc().metrics(mi, dimn);
180 bl().metrics(mi, dimbl);
184 br().metrics(mi, dimbr);
187 Changer dummy = mi.base.changeScript();
189 bl().metrics(mi, dimbl);
190 tl().metrics(mi, dimtl);
193 br().metrics(mi, dimbr);
194 tr().metrics(mi, dimtr);
197 BufferView const & bv = *mi.base.bv;
198 // FIXME: data copying... not very efficient.
200 dim.wid = nwid(bv) + nker(mi.base.bv) + 2 * dx;
201 dim.wid += max(dimbl.width(), dimtl.width());
202 dim.wid += max(dimbr.width(), dimtr.width());
204 int asc = dyt(bv) + max(dimtl.ascent(), dimtr.ascent());
205 dim.asc = max(na, asc);
207 int des = dyb(bv) + max(dimbl.descent(), dimbr.descent());
208 dim.des = max(nd, des);
212 void InsetMathSideset::draw(PainterInfo & pi, int x, int y) const
214 Changer dummy2 = pi.base.changeEnsureMath();
215 BufferView const & bv = *pi.base.bv;
216 nuc().draw(pi, x + dxn(bv), y);
218 bl().draw(pi, x , y);
220 br().draw(pi, x + dxr(bv), y);
221 Changer dummy = pi.base.changeScript();
223 bl().draw(pi, x , y + dyb(bv));
224 tl().draw(pi, x , y - dyt(bv));
227 br().draw(pi, x + dxr(bv), y + dyb(bv));
228 tr().draw(pi, x + dxr(bv), y - dyt(bv));
233 void InsetMathSideset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
235 bl().metricsT(mi, dim);
236 tl().metricsT(mi, dim);
237 br().metricsT(mi, dim);
238 tr().metricsT(mi, dim);
239 nuc().metricsT(mi, dim);
243 void InsetMathSideset::drawT(TextPainter & pain, int x, int y) const
246 nuc().drawT(pain, x + 1, y);
247 bl().drawT(pain, x + 1, y + 1 /*dy0()*/);
248 tl().drawT(pain, x + 1, y - 1 /*dy1()*/);
249 br().drawT(pain, x + 1, y + 1 /*dy0()*/);
250 tr().drawT(pain, x + 1, y - 1 /*dy1()*/);
255 bool InsetMathSideset::idxForward(Cursor & cur) const
257 if (!scriptl_ && cur.idx() == 1) {
261 } else if (!scriptr_ && cur.idx() == 0) {
263 cur.idx() = 2 + scriptl_;
270 bool InsetMathSideset::idxBackward(Cursor & cur) const
272 if (!scriptr_ && cur.idx() == (scriptl_ ? 3 : 2)) {
276 } else if (!scriptl_ && cur.idx() == 0) {
285 bool InsetMathSideset::idxUpDown(Cursor & cur, bool up) const
288 if (cur.idx() == 0) {
289 // go up/down only if in the last position
290 // or in the first position
291 if ((scriptr_ && cur.pos() == cur.lastpos()) ||
292 (scriptl_ && cur.pos() == 0)) {
294 cur.idx() = up ? 2 : 1;
296 cur.idx() = (up ? 3 : 2) + scriptl_;
304 if ((scriptl_ && cur.idx() == 2) ||
305 (scriptr_ && cur.idx() == (scriptl_ ? 4 : 3))) {
306 // can't go further up
309 // otherwise go to first or last position in the nucleus
314 cur.pos() = cur.lastpos();
319 if ((scriptl_ && cur.idx() == 1) ||
320 (scriptr_ && cur.idx() == (scriptl_ ? 3 : 2))) {
321 // can't go further down
324 // otherwise go to first or last position in the nucleus
329 cur.pos() = cur.lastpos();
337 void InsetMathSideset::write(TeXMathStream & os) const
339 MathEnsurer ensurer(os);
345 os << "_{" << bl() << '}';
347 os << "^{" << tl() << '}';
353 os << "_{" << br() << '}';
355 os << "^{" << tr() << '}';
361 if (lock_ && !os.latex())
366 void InsetMathSideset::normalize(NormalStream & os) const
372 if (scriptl_ && !tl().empty())
382 if (scriptr_ && !tr().empty())
388 void InsetMathSideset::mathmlize(MathMLStream & ms) const
390 // FIXME This is only accurate if both scriptl_ and scriptr_ are true
392 ms << MTag("mrow") << bl() << ETag("mrow");
393 if (scriptl_ || scriptr_) {
394 ms << MTag("mmultiscripts");
397 ms << "<" << from_ascii(ms.namespacedTag("mrow")) << " />";
399 ms << MTag("mrow") << nuc() << ETag("mrow");
401 if (br().empty() || !scriptr_)
402 ms << "<" << from_ascii(ms.namespacedTag("none")) << " />";
404 ms << MTag("mrow") << br() << ETag("mrow");
405 if (tr().empty() || !scriptr_)
406 ms << "<" << from_ascii(ms.namespacedTag("none")) << " />";
408 ms << MTag("mrow") << tr() << ETag("mrow");
410 if (bl().empty() || !scriptl_)
411 ms << "<" << from_ascii(ms.namespacedTag("none")) << " />";
413 ms << MTag("mrow") << bl() << ETag("mrow");
414 if (tl().empty() || !scriptl_)
415 ms << "<" << from_ascii(ms.namespacedTag("none")) << " />";
417 ms << MTag("mrow") << tl() << ETag("mrow");
419 ms << ETag("mmultiscripts");
422 ms << MTag("mrow") << br() << ETag("mrow");
426 void InsetMathSideset::htmlize(HtmlStream & os) const
428 // FIXME This is only accurate if both scriptl_ and scriptr_ are true
429 bool const havebl = scriptl_ && !bl().empty();
430 bool const havetl = scriptl_ && !tl().empty();
431 bool const havebr = scriptr_ && !br().empty();
432 bool const havetr = scriptr_ && !tr().empty();
434 if (!scriptl_ && !bl().empty())
437 if (havebl && havetl)
438 os << MTag("span", "class='scripts'")
439 << MTag("span") << tl() << ETag("span")
440 << MTag("span") << bl() << ETag("span")
443 os << MTag("sub", "class='math'") << bl() << ETag("sub");
445 os << MTag("sup", "class='math'") << tl() << ETag("sup");
450 if (havebr && havetr)
451 os << MTag("span", "class='scripts'")
452 << MTag("span") << tr() << ETag("span")
453 << MTag("span") << br() << ETag("span")
456 os << MTag("sub", "class='math'") << br() << ETag("sub");
458 os << MTag("sup", "class='math'") << tr() << ETag("sup");
460 if (!scriptr_ && !br().empty())
465 void InsetMathSideset::infoize(odocstream & os) const
471 // the idea for dual scripts came from the eLyXer code
472 void InsetMathSideset::validate(LaTeXFeatures & features) const
474 if (features.runparams().math_flavor == OutputParams::MathAsHTML)
475 features.addCSSSnippet(
476 "span.scripts{display: inline-block; vertical-align: middle; text-align:center; font-size: 75%;}\n"
477 "span.scripts span {display: block;}\n"
478 "sub.math{font-size: 75%;}\n"
479 "sup.math{font-size: 75%;}");
480 features.require("amsmath");
481 InsetMathNest::validate(features);