2 * \file InsetMathFracBase.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alejandro Aguilar Sierra
10 * Full author contact details are available in file CREDITS.
15 #include "InsetMathFrac.h"
18 #include "LaTeXFeatures.h"
20 #include "MathStream.h"
21 #include "MathSupport.h"
22 #include "MetricsInfo.h"
23 #include "TextPainter.h"
25 #include "frontends/Painter.h"
31 /////////////////////////////////////////////////////////////////////
35 /////////////////////////////////////////////////////////////////////
38 InsetMathFracBase::InsetMathFracBase(idx_type ncells)
39 : InsetMathNest(ncells)
43 bool InsetMathFracBase::idxUpDown(Cursor & cur, bool up) const
45 InsetMath::idx_type target = !up; // up ? 0 : 1, since upper cell has idx 0
46 if (cur.idx() == target)
49 cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
55 /////////////////////////////////////////////////////////////////////
59 /////////////////////////////////////////////////////////////////////
62 InsetMathFrac::InsetMathFrac(Kind kind, InsetMath::idx_type ncells)
63 : InsetMathFracBase(ncells), kind_(kind)
67 Inset * InsetMathFrac::clone() const
69 return new InsetMathFrac(*this);
73 InsetMathFrac * InsetMathFrac::asFracInset()
75 return kind_ == ATOP ? 0 : this;
79 InsetMathFrac const * InsetMathFrac::asFracInset() const
81 return kind_ == ATOP ? 0 : this;
85 bool InsetMathFrac::idxForward(Cursor & cur) const
87 InsetMath::idx_type target = 0;
88 if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)
89 || (kind_ == CFRAC && nargs() == 3)) {
92 else if (nargs() == 2)
96 if (cur.idx() == target)
99 cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
104 bool InsetMathFrac::idxBackward(Cursor & cur) const
106 InsetMath::idx_type target = 0;
107 if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)
108 || (kind_ == CFRAC && nargs() == 3)) {
111 else if (nargs() == 2)
115 if (cur.idx() == target)
118 cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
123 void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
125 Dimension dim0, dim1, dim2;
127 if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
129 ShapeChanger dummy2(mi.base.font, UP_SHAPE);
130 cell(0).metrics(mi, dim0);
131 dim.wid = dim0.width()+ 3;
134 } else if (nargs() == 2) {
135 cell(0).metrics(mi, dim0);
136 ShapeChanger dummy2(mi.base.font, UP_SHAPE);
137 cell(1).metrics(mi, dim1);
138 dim.wid = dim0.width() + dim1.wid + 5;
139 dim.asc = max(dim0.asc, dim1.asc);
140 dim.des = max(dim0.des, dim1.des);
142 cell(2).metrics(mi, dim2);
143 ShapeChanger dummy2(mi.base.font, UP_SHAPE);
144 FracChanger dummy(mi.base);
145 cell(0).metrics(mi, dim0);
146 cell(1).metrics(mi, dim1);
147 dim.wid = dim0.width() + dim1.wid + dim2.wid + 10;
148 dim.asc = max(dim2.asc, dim0.height() + 5);
149 dim.des = max(dim2.des, dim1.height() - 5);
152 FracChanger dummy(mi.base);
153 cell(0).metrics(mi, dim0);
154 cell(1).metrics(mi, dim1);
156 cell(2).metrics(mi, dim2);
158 if (kind_ == NICEFRAC) {
159 dim.wid = dim0.width() + dim1.wid + 5;
160 dim.asc = dim0.height() + 5;
161 dim.des = dim1.height() - 5;
162 } else if (kind_ == CFRAC) {
164 // cfrac is always in display size
165 StyleChanger dummy2(mi.base, LM_ST_DISPLAY);
166 cell(0).metrics(mi, dim0);
167 cell(1).metrics(mi, dim1);
168 dim.wid = max(dim0.wid, dim1.wid) + 2;
169 dim.asc = dim0.height() + 2 + 5;
170 dim.des = dim1.height() + 2 - 5;
172 // cfrac is always in display size
173 StyleChanger dummy2(mi.base, LM_ST_DISPLAY);
174 // use text font for the optional argument
175 FontSetChanger dummy3(mi.base, "textnormal");
176 cell(2).metrics(mi, dim2);
177 // return to math font
178 FontSetChanger dummy4(mi.base, "mathnormal");
179 cell(0).metrics(mi, dim0);
180 cell(1).metrics(mi, dim1);
181 dim.wid = 2 + max(dim0.wid, dim1.wid);
182 dim.asc = dim0.height() + 2 + 5;
183 dim.des = dim1.height() + 2 - 5;
185 } else if (kind_ == UNITFRAC) {
186 ShapeChanger dummy2(mi.base.font, UP_SHAPE);
187 dim.wid = dim0.width() + dim1.wid + 5;
188 dim.asc = dim0.height() + 5;
189 dim.des = dim1.height() - 5;
190 } else if (kind_ == DFRAC) {
191 // dfrac is in always in display size
192 StyleChanger dummy2(mi.base, LM_ST_DISPLAY);
193 cell(0).metrics(mi, dim0);
194 cell(1).metrics(mi, dim1);
195 dim.wid = max(dim0.wid, dim1.wid) + 2;
196 dim.asc = dim0.height() + 2 + 5;
197 dim.des = dim1.height() + 2 - 5;
198 } else if (kind_ == TFRAC) {
199 // tfrac is in always in text size
200 StyleChanger dummy2(mi.base, LM_ST_SCRIPT);
201 cell(0).metrics(mi, dim0);
202 cell(1).metrics(mi, dim1);
203 dim.wid = max(dim0.wid, dim1.wid) + 2;
204 dim.asc = dim0.height() + 2 + 5;
205 dim.des = dim1.height() + 2 - 5;
208 dim.wid = max(dim0.wid, dim1.wid) + 2;
209 dim.asc = dim0.height() + 2 + 5;
210 dim.des = dim1.height() + 2 - 5;
217 void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
219 setPosCache(pi, x, y);
220 Dimension const dim = dimension(*pi.base.bv);
221 Dimension const dim0 = cell(0).dimension(*pi.base.bv);
222 int m = x + dim.wid / 2;
223 if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
225 ShapeChanger dummy2(pi.base.font, UP_SHAPE);
226 cell(0).draw(pi, x + 1, y);
227 } else if (nargs() == 2) {
228 cell(0).draw(pi, x + 1, y);
229 ShapeChanger dummy2(pi.base.font, UP_SHAPE);
230 cell(1).draw(pi, x + dim0.width() + 5, y);
232 cell(2).draw(pi, x + 1, y);
233 ShapeChanger dummy2(pi.base.font, UP_SHAPE);
234 FracChanger dummy(pi.base);
235 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
236 Dimension const dim2 = cell(2).dimension(*pi.base.bv);
237 int xx = x + dim2.wid + 5;
238 cell(0).draw(pi, xx + 2,
240 cell(1).draw(pi, xx + dim0.width() + 5,
244 FracChanger dummy(pi.base);
245 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
246 if (kind_ == NICEFRAC) {
247 cell(0).draw(pi, x + 2,
249 cell(1).draw(pi, x + dim0.width() + 5,
251 } else if (kind_ == UNITFRAC) {
252 ShapeChanger dummy2(pi.base.font, UP_SHAPE);
253 cell(0).draw(pi, x + 2,
255 cell(1).draw(pi, x + dim0.width() + 5,
257 } else if (kind_ == CFRAC) {
259 // cfrac is always in display size
260 StyleChanger dummy2(pi.base, LM_ST_DISPLAY);
261 Dimension const dim0 = cell(0).dimension(*pi.base.bv);
262 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
263 int m = x + dim.wid / 2;
264 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
265 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
267 // cfrac is always in display size
268 StyleChanger dummy2(pi.base, LM_ST_DISPLAY);
269 // use text font for the optional argument
270 FontSetChanger dummy3(pi.base, "textnormal");
271 Dimension const dim2 = cell(2).dimension(*pi.base.bv);
272 int w = mathed_char_width(pi.base.font, '[');
273 drawStrBlack(pi, x, y, from_ascii("["));
275 cell(2).draw(pi, x + 1, y);
277 drawStrBlack(pi, x, y, from_ascii("]"));
279 // return to math font
280 FontSetChanger dummy4(pi.base, "mathnormal");
281 Dimension const dim0 = cell(0).dimension(*pi.base.bv);
282 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
283 int m = x + dim.wid / 2;
284 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
285 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
287 } else if (kind_ == DFRAC) {
288 // dfrac is in always in display size
289 StyleChanger dummy2(pi.base, LM_ST_DISPLAY);
290 //Dimension const dim = dimension(*pi.base.bv);
291 Dimension const dim0 = cell(0).dimension(*pi.base.bv);
292 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
293 int m = x + dim.wid / 2;
294 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
295 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
296 } else if (kind_ == TFRAC) {
297 // tfrac is in always in text size
298 StyleChanger dummy2(pi.base, LM_ST_SCRIPT);
299 Dimension const dim0 = cell(0).dimension(*pi.base.bv);
300 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
301 int m = x + dim.wid / 2;
302 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
303 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
306 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
307 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
310 if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
314 xx += cell(2).dimension(*pi.base.bv).wid + 5;
315 pi.pain.line(xx + dim0.wid,
318 y - dim.asc + 2, Color_math);
320 if (kind_ == FRAC || kind_ == CFRAC || kind_ == DFRAC
321 || kind_ == TFRAC || kind_ == OVER)
322 pi.pain.line(x + 1, y - 5,
323 x + dim.wid - 2, y - 5, Color_math);
324 drawMarkers(pi, x, y);
328 void InsetMathFrac::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
330 Dimension dim0, dim1;
331 cell(0).metricsT(mi, dim0);
332 cell(1).metricsT(mi, dim1);
333 dim.wid = max(dim0.width(), dim1.wid);
334 dim.asc = dim0.height() + 1;
335 dim.des = dim1.height();
339 void InsetMathFrac::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
344 int m = x + dim.width() / 2;
345 cell(0).drawT(pain, m - dim0.width() / 2, y - dim0.des - 1);
346 cell(1).drawT(pain, m - dim1.wid / 2, y + dim1.asc);
347 // ASCII art: ignore niceties
348 if (kind_ == FRAC || kind_ == OVER || kind_ == NICEFRAC || kind_ == UNITFRAC)
349 pain.horizontalLine(x, y, dim.width());
354 void InsetMathFrac::write(WriteStream & os) const
356 MathEnsurer ensurer(os);
359 os << '{' << cell(0) << "\\atop " << cell(1) << '}';
362 // \\over is only for compatibility, normalize this to \\frac
363 os << "\\frac{" << cell(0) << "}{" << cell(1) << '}';
371 InsetMathNest::write(os);
373 os << "\\cfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
377 InsetMathNest::write(os);
379 os << "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
383 os << "\\unit[" << cell(0) << "]{" << cell(1) << '}';
385 os << "\\unit{" << cell(0) << '}';
391 docstring InsetMathFrac::name() const
395 return from_ascii("frac");
397 return from_ascii("cfrac");
399 return from_ascii("dfrac");
401 return from_ascii("tfrac");
403 return from_ascii("over");
405 return from_ascii("nicefrac");
407 return from_ascii("unitfrac");
409 return from_ascii("unit");
411 return from_ascii("atop");
413 // shut up stupid compiler
418 bool InsetMathFrac::extraBraces() const
420 return kind_ == ATOP || kind_ == OVER;
424 void InsetMathFrac::maple(MapleStream & os) const
426 os << '(' << cell(0) << ")/(" << cell(1) << ')';
430 void InsetMathFrac::mathematica(MathematicaStream & os) const
432 os << '(' << cell(0) << ")/(" << cell(1) << ')';
436 void InsetMathFrac::octave(OctaveStream & os) const
438 os << '(' << cell(0) << ")/(" << cell(1) << ')';
442 void InsetMathFrac::mathmlize(MathStream & os) const
446 os << MTag("mfrac") << cell(0) << cell(1) << ETag("mfrac");
449 os << MTag("mdfrac") << cell(0) << cell(1) << ETag("mdfrac");
452 os << MTag("mtfrac") << cell(0) << cell(1) << ETag("mtfrac");
458 void InsetMathFrac::validate(LaTeXFeatures & features) const
460 if (kind_ == NICEFRAC || kind_ == UNITFRAC || kind_ == UNIT)
461 features.require("units");
462 if (kind_ == CFRAC || kind_ == DFRAC || kind_ == TFRAC)
463 features.require("amsmath");
464 InsetMathNest::validate(features);
468 /////////////////////////////////////////////////////////////////////
472 /////////////////////////////////////////////////////////////////////
475 InsetMathBinom::InsetMathBinom(Kind kind)
480 Inset * InsetMathBinom::clone() const
482 return new InsetMathBinom(*this);
486 int InsetMathBinom::dw(int height) const
497 void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
499 Dimension dim0, dim1;
501 // FIXME: for an unknown reason the cells must be set directly
502 // after the StyleChanger and cannot be set after the if case
503 if (kind_ == DBINOM) {
504 StyleChanger dummy(mi.base, LM_ST_DISPLAY);
505 cell(0).metrics(mi, dim0);
506 cell(1).metrics(mi, dim1);
507 } else if (kind_ == TBINOM) {
508 StyleChanger dummy(mi.base, LM_ST_SCRIPT);
509 cell(0).metrics(mi, dim0);
510 cell(1).metrics(mi, dim1);
512 FracChanger dummy(mi.base);
513 cell(0).metrics(mi, dim0);
514 cell(1).metrics(mi, dim1);
516 dim.asc = dim0.height() + 4 + 5;
517 dim.des = dim1.height() + 4 - 5;
518 dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
519 metricsMarkers2(dim);
523 void InsetMathBinom::draw(PainterInfo & pi, int x, int y) const
525 Dimension const dim = dimension(*pi.base.bv);
526 Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
527 Dimension const & dim1 = cell(1).dimension(*pi.base.bv);
528 // define the binom brackets
529 docstring const bra = kind_ == BRACE ? from_ascii("{") :
530 kind_ == BRACK ? from_ascii("[") : from_ascii("(");
531 docstring const ket = kind_ == BRACE ? from_ascii("}") :
532 kind_ == BRACK ? from_ascii("]") : from_ascii(")");
534 int m = x + dim.width() / 2;
535 // FIXME: for an unknown reason the cells must be drawn directly
536 // after the StyleChanger and cannot be drawn after the if case
537 if (kind_ == DBINOM) {
538 StyleChanger dummy(pi.base, LM_ST_DISPLAY);
539 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
540 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
541 } else if (kind_ == TBINOM) {
542 StyleChanger dummy(pi.base, LM_ST_SCRIPT);
543 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
544 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
546 FracChanger dummy2(pi.base);
547 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
548 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
550 // draw the brackets and the marker
551 mathed_draw_deco(pi, x, y - dim.ascent(), dw(dim.height()),
553 mathed_draw_deco(pi, x + dim.width() - dw(dim.height()),
554 y - dim.ascent(), dw(dim.height()), dim.height(), ket);
555 drawMarkers2(pi, x, y);
559 bool InsetMathBinom::extraBraces() const
561 return kind_ == CHOOSE || kind_ == BRACE || kind_ == BRACK;
565 void InsetMathBinom::write(WriteStream & os) const
567 MathEnsurer ensurer(os);
570 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
573 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
576 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
579 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
582 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
585 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
591 void InsetMathBinom::normalize(NormalStream & os) const
593 os << "[binom " << cell(0) << ' ' << cell(1) << ']';
597 void InsetMathBinom::mathmlize(MathStream & os) const
601 os << MTag("mbinom") << cell(0) << cell(1) << ETag("mbinom");
604 os << MTag("mdbinom") << cell(0) << cell(1) << ETag("mdbinom");
607 os << MTag("mtbinom") << cell(0) << cell(1) << ETag("mtbinom");
613 void InsetMathBinom::validate(LaTeXFeatures & features) const
616 features.require("binom");
617 if (kind_ == DBINOM || kind_ == TBINOM)
618 features.require("amsmath");
619 InsetMathNest::validate(features);