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(Buffer
* buf
, idx_type ncells
)
39 : InsetMathNest(buf
, 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(Buffer
* buf
, Kind kind
, InsetMath::idx_type ncells
)
63 : InsetMathFracBase(buf
, 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)) {
91 else if (nargs() == 2)
95 if (cur
.idx() == target
)
98 cur
.pos() = cell(target
).x2pos(&cur
.bv(), cur
.x_target());
103 bool InsetMathFrac::idxBackward(Cursor
& cur
) const
105 InsetMath::idx_type target
= 0;
106 if (kind_
== UNIT
|| (kind_
== UNITFRAC
&& nargs() == 3)) {
109 else if (nargs() == 2)
113 if (cur
.idx() == target
)
116 cur
.pos() = cell(target
).x2pos(&cur
.bv(), cur
.x_target());
121 void InsetMathFrac::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
123 Dimension dim0
, dim1
, dim2
;
125 if (kind_
== UNIT
|| (kind_
== UNITFRAC
&& nargs() == 3)) {
127 ShapeChanger
dummy2(mi
.base
.font
, UP_SHAPE
);
128 cell(0).metrics(mi
, dim0
);
129 dim
.wid
= dim0
.width()+ 3;
132 } else if (nargs() == 2) {
133 cell(0).metrics(mi
, dim0
);
134 ShapeChanger
dummy2(mi
.base
.font
, UP_SHAPE
);
135 cell(1).metrics(mi
, dim1
);
136 dim
.wid
= dim0
.width() + dim1
.wid
+ 5;
137 dim
.asc
= max(dim0
.asc
, dim1
.asc
);
138 dim
.des
= max(dim0
.des
, dim1
.des
);
140 cell(2).metrics(mi
, dim2
);
141 ShapeChanger
dummy2(mi
.base
.font
, UP_SHAPE
);
142 FracChanger
dummy(mi
.base
);
143 cell(0).metrics(mi
, dim0
);
144 cell(1).metrics(mi
, dim1
);
145 dim
.wid
= dim0
.width() + dim1
.wid
+ dim2
.wid
+ 10;
146 dim
.asc
= max(dim2
.asc
, dim0
.height() + 5);
147 dim
.des
= max(dim2
.des
, dim1
.height() - 5);
150 // general cell metrics used for \frac
151 FracChanger
dummy(mi
.base
);
152 cell(0).metrics(mi
, dim0
);
153 cell(1).metrics(mi
, dim1
);
155 cell(2).metrics(mi
, dim2
);
156 // metrics for special fraction types
157 if (kind_
== NICEFRAC
) {
158 dim
.wid
= dim0
.width() + dim1
.wid
+ 5;
159 dim
.asc
= dim0
.height() + 5;
160 dim
.des
= dim1
.height() - 5;
161 } else if (kind_
== UNITFRAC
) {
162 ShapeChanger
dummy2(mi
.base
.font
, UP_SHAPE
);
163 dim
.wid
= dim0
.width() + dim1
.wid
+ 5;
164 dim
.asc
= dim0
.height() + 5;
165 dim
.des
= dim1
.height() - 5;
167 if (kind_
== CFRAC
|| kind_
== CFRACLEFT
168 || kind_
== CFRACRIGHT
|| kind_
== DFRAC
) {
169 // \cfrac and \dfrac are always in display size
170 StyleChanger
dummy2(mi
.base
, LM_ST_DISPLAY
);
171 cell(0).metrics(mi
, dim0
);
172 cell(1).metrics(mi
, dim1
);
173 } else if (kind_
== TFRAC
) {
174 // tfrac is in always in text size
175 StyleChanger
dummy2(mi
.base
, LM_ST_SCRIPT
);
176 cell(0).metrics(mi
, dim0
);
177 cell(1).metrics(mi
, dim1
);
179 dim
.wid
= max(dim0
.wid
, dim1
.wid
) + 2;
180 dim
.asc
= dim0
.height() + 2 + 5;
181 dim
.des
= dim1
.height() + 2 - 5;
188 void InsetMathFrac::draw(PainterInfo
& pi
, int x
, int y
) const
190 setPosCache(pi
, x
, y
);
191 Dimension
const dim
= dimension(*pi
.base
.bv
);
192 Dimension
const dim0
= cell(0).dimension(*pi
.base
.bv
);
193 if (kind_
== UNIT
|| (kind_
== UNITFRAC
&& nargs() == 3)) {
195 ShapeChanger
dummy2(pi
.base
.font
, UP_SHAPE
);
196 cell(0).draw(pi
, x
+ 1, y
);
197 } else if (nargs() == 2) {
198 cell(0).draw(pi
, x
+ 1, y
);
199 ShapeChanger
dummy2(pi
.base
.font
, UP_SHAPE
);
200 cell(1).draw(pi
, x
+ dim0
.width() + 5, y
);
202 cell(2).draw(pi
, x
+ 1, y
);
203 ShapeChanger
dummy2(pi
.base
.font
, UP_SHAPE
);
204 FracChanger
dummy(pi
.base
);
205 Dimension
const dim1
= cell(1).dimension(*pi
.base
.bv
);
206 Dimension
const dim2
= cell(2).dimension(*pi
.base
.bv
);
207 int xx
= x
+ dim2
.wid
+ 5;
208 cell(0).draw(pi
, xx
+ 2,
210 cell(1).draw(pi
, xx
+ dim0
.width() + 5,
214 FracChanger
dummy(pi
.base
);
215 Dimension
const dim1
= cell(1).dimension(*pi
.base
.bv
);
216 int m
= x
+ dim
.wid
/ 2;
217 if (kind_
== NICEFRAC
) {
218 cell(0).draw(pi
, x
+ 2,
220 cell(1).draw(pi
, x
+ dim0
.width() + 5,
222 } else if (kind_
== UNITFRAC
) {
223 ShapeChanger
dummy2(pi
.base
.font
, UP_SHAPE
);
224 cell(0).draw(pi
, x
+ 2, y
- dim0
.des
- 5);
225 cell(1).draw(pi
, x
+ dim0
.width() + 5, y
+ dim1
.asc
/ 2);
226 } else if (kind_
== FRAC
|| kind_
== ATOP
|| kind_
== OVER
) {
227 cell(0).draw(pi
, m
- dim0
.wid
/ 2, y
- dim0
.des
- 2 - 5);
228 cell(1).draw(pi
, m
- dim1
.wid
/ 2, y
+ dim1
.asc
+ 2 - 5);
229 } else if (kind_
== TFRAC
) {
230 // tfrac is in always in text size
231 StyleChanger
dummy2(pi
.base
, LM_ST_SCRIPT
);
232 cell(0).draw(pi
, m
- dim0
.wid
/ 2, y
- dim0
.des
- 2 - 5);
233 cell(1).draw(pi
, m
- dim1
.wid
/ 2, y
+ dim1
.asc
+ 2 - 5);
235 // \cfrac and \dfrac are always in display size
236 StyleChanger
dummy2(pi
.base
, LM_ST_DISPLAY
);
237 if (kind_
== CFRAC
|| kind_
== DFRAC
)
238 cell(0).draw(pi
, m
- dim0
.wid
/ 2, y
- dim0
.des
- 2 - 5);
239 else if (kind_
== CFRACLEFT
)
240 cell(0).draw(pi
, x
+ 2, y
- dim0
.des
- 2 - 5);
241 else if (kind_
== CFRACRIGHT
)
242 cell(0).draw(pi
, x
+ dim
.wid
- dim0
.wid
- 2,
243 y
- dim0
.des
- 2 - 5);
244 cell(1).draw(pi
, m
- dim1
.wid
/ 2, y
+ dim1
.asc
+ 2 - 5);
247 if (kind_
== NICEFRAC
|| kind_
== UNITFRAC
) {
251 xx
+= cell(2).dimension(*pi
.base
.bv
).wid
+ 5;
252 pi
.pain
.line(xx
+ dim0
.wid
,
255 y
- dim
.asc
+ 2, Color_math
);
257 if (kind_
== FRAC
|| kind_
== CFRAC
|| kind_
== CFRACLEFT
258 || kind_
== CFRACRIGHT
|| kind_
== DFRAC
259 || kind_
== TFRAC
|| kind_
== OVER
)
260 pi
.pain
.line(x
+ 1, y
- 5,
261 x
+ dim
.wid
- 2, y
- 5, Color_math
);
262 drawMarkers(pi
, x
, y
);
266 void InsetMathFrac::metricsT(TextMetricsInfo
const & mi
, Dimension
& dim
) const
268 Dimension dim0
, dim1
;
269 cell(0).metricsT(mi
, dim0
);
270 cell(1).metricsT(mi
, dim1
);
271 dim
.wid
= max(dim0
.width(), dim1
.wid
);
272 dim
.asc
= dim0
.height() + 1;
273 dim
.des
= dim1
.height();
277 void InsetMathFrac::drawT(TextPainter
& /*pain*/, int /*x*/, int /*y*/) const
282 int m = x + dim.width() / 2;
283 cell(0).drawT(pain, m - dim0.width() / 2, y - dim0.des - 1);
284 cell(1).drawT(pain, m - dim1.wid / 2, y + dim1.asc);
285 // ASCII art: ignore niceties
286 if (kind_ == FRAC || kind_ == OVER || kind_ == NICEFRAC || kind_ == UNITFRAC)
287 pain.horizontalLine(x, y, dim.width());
292 void InsetMathFrac::write(WriteStream
& os
) const
294 MathEnsurer
ensurer(os
);
297 // \\atop is only for compatibility, \\binom is the
299 os
<< '{' << cell(0) << "\\atop " << cell(1) << '}';
302 // \\over is only for compatibility, normalize this to \\frac
303 os
<< "\\frac{" << cell(0) << "}{" << cell(1) << '}';
312 InsetMathNest::write(os
);
314 os
<< "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
318 os
<< "\\unit[" << cell(0) << "]{" << cell(1) << '}';
320 os
<< "\\unit{" << cell(0) << '}';
323 os
<< "\\cfrac[l]{" << cell(0) << "}{" << cell(1) << '}';
326 os
<< "\\cfrac[r]{" << cell(0) << "}{" << cell(1) << '}';
332 docstring
InsetMathFrac::name() const
336 return from_ascii("frac");
340 return from_ascii("cfrac");
342 return from_ascii("dfrac");
344 return from_ascii("tfrac");
346 return from_ascii("over");
348 return from_ascii("nicefrac");
350 return from_ascii("unitfrac");
352 return from_ascii("unit");
354 return from_ascii("atop");
356 // shut up stupid compiler
361 bool InsetMathFrac::extraBraces() const
363 return kind_
== ATOP
|| kind_
== OVER
;
367 void InsetMathFrac::maple(MapleStream
& os
) const
369 os
<< '(' << cell(0) << ")/(" << cell(1) << ')';
373 void InsetMathFrac::mathematica(MathematicaStream
& os
) const
375 os
<< '(' << cell(0) << ")/(" << cell(1) << ')';
379 void InsetMathFrac::octave(OctaveStream
& os
) const
381 os
<< '(' << cell(0) << ")/(" << cell(1) << ')';
385 void InsetMathFrac::mathmlize(MathStream
& os
) const
389 os
<< MTag("mdfrac") << cell(0) << cell(1) << ETag("mdfrac");
392 os
<< MTag("mtfrac") << cell(0) << cell(1) << ETag("mtfrac");
396 os
<< MTag("mfrac") << cell(0) << cell(1) << ETag("mfrac");
402 void InsetMathFrac::validate(LaTeXFeatures
& features
) const
404 if (kind_
== NICEFRAC
|| kind_
== UNITFRAC
|| kind_
== UNIT
)
405 features
.require("units");
406 if (kind_
== CFRAC
|| kind_
== CFRACLEFT
|| kind_
== CFRACRIGHT
407 || kind_
== DFRAC
|| kind_
== TFRAC
)
408 features
.require("amsmath");
409 InsetMathNest::validate(features
);
413 /////////////////////////////////////////////////////////////////////
417 /////////////////////////////////////////////////////////////////////
420 InsetMathBinom::InsetMathBinom(Buffer
* buf
, Kind kind
)
421 : InsetMathFracBase(buf
), kind_(kind
)
425 Inset
* InsetMathBinom::clone() const
427 return new InsetMathBinom(*this);
431 int InsetMathBinom::dw(int height
) const
442 void InsetMathBinom::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
444 Dimension dim0
, dim1
;
446 // FIXME: for an unknown reason the cells must be set directly
447 // after the StyleChanger and cannot be set after the if case
448 if (kind_
== DBINOM
) {
449 StyleChanger
dummy(mi
.base
, LM_ST_DISPLAY
);
450 cell(0).metrics(mi
, dim0
);
451 cell(1).metrics(mi
, dim1
);
452 } else if (kind_
== TBINOM
) {
453 StyleChanger
dummy(mi
.base
, LM_ST_SCRIPT
);
454 cell(0).metrics(mi
, dim0
);
455 cell(1).metrics(mi
, dim1
);
457 FracChanger
dummy(mi
.base
);
458 cell(0).metrics(mi
, dim0
);
459 cell(1).metrics(mi
, dim1
);
461 dim
.asc
= dim0
.height() + 4 + 5;
462 dim
.des
= dim1
.height() + 4 - 5;
463 dim
.wid
= max(dim0
.wid
, dim1
.wid
) + 2 * dw(dim
.height()) + 4;
464 metricsMarkers2(dim
);
468 void InsetMathBinom::draw(PainterInfo
& pi
, int x
, int y
) const
470 Dimension
const dim
= dimension(*pi
.base
.bv
);
471 Dimension
const & dim0
= cell(0).dimension(*pi
.base
.bv
);
472 Dimension
const & dim1
= cell(1).dimension(*pi
.base
.bv
);
473 // define the binom brackets
474 docstring
const bra
= kind_
== BRACE
? from_ascii("{") :
475 kind_
== BRACK
? from_ascii("[") : from_ascii("(");
476 docstring
const ket
= kind_
== BRACE
? from_ascii("}") :
477 kind_
== BRACK
? from_ascii("]") : from_ascii(")");
479 int m
= x
+ dim
.width() / 2;
480 // FIXME: for an unknown reason the cells must be drawn directly
481 // after the StyleChanger and cannot be drawn after the if case
482 if (kind_
== DBINOM
) {
483 StyleChanger
dummy(pi
.base
, LM_ST_DISPLAY
);
484 cell(0).draw(pi
, m
- dim0
.wid
/ 2, y
- dim0
.des
- 3 - 5);
485 cell(1).draw(pi
, m
- dim1
.wid
/ 2, y
+ dim1
.asc
+ 3 - 5);
486 } else if (kind_
== TBINOM
) {
487 StyleChanger
dummy(pi
.base
, LM_ST_SCRIPT
);
488 cell(0).draw(pi
, m
- dim0
.wid
/ 2, y
- dim0
.des
- 3 - 5);
489 cell(1).draw(pi
, m
- dim1
.wid
/ 2, y
+ dim1
.asc
+ 3 - 5);
491 FracChanger
dummy2(pi
.base
);
492 cell(0).draw(pi
, m
- dim0
.wid
/ 2, y
- dim0
.des
- 3 - 5);
493 cell(1).draw(pi
, m
- dim1
.wid
/ 2, y
+ dim1
.asc
+ 3 - 5);
495 // draw the brackets and the marker
496 mathed_draw_deco(pi
, x
, y
- dim
.ascent(), dw(dim
.height()),
498 mathed_draw_deco(pi
, x
+ dim
.width() - dw(dim
.height()),
499 y
- dim
.ascent(), dw(dim
.height()), dim
.height(), ket
);
500 drawMarkers2(pi
, x
, y
);
504 bool InsetMathBinom::extraBraces() const
506 return kind_
== CHOOSE
|| kind_
== BRACE
|| kind_
== BRACK
;
510 void InsetMathBinom::write(WriteStream
& os
) const
512 MathEnsurer
ensurer(os
);
515 os
<< "\\binom{" << cell(0) << "}{" << cell(1) << '}';
518 os
<< "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
521 os
<< "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
524 os
<< '{' << cell(0) << " \\choose " << cell(1) << '}';
527 os
<< '{' << cell(0) << " \\brace " << cell(1) << '}';
530 os
<< '{' << cell(0) << " \\brack " << cell(1) << '}';
536 void InsetMathBinom::normalize(NormalStream
& os
) const
538 os
<< "[binom " << cell(0) << ' ' << cell(1) << ']';
542 void InsetMathBinom::mathmlize(MathStream
& os
) const
546 os
<< MTag("mbinom") << cell(0) << cell(1) << ETag("mbinom");
549 os
<< MTag("mtbinom") << cell(0) << cell(1) << ETag("mtbinom");
553 os
<< MTag("mdbinom") << cell(0) << cell(1) << ETag("mdbinom");
559 void InsetMathBinom::validate(LaTeXFeatures
& features
) const
562 features
.require("binom");
563 if (kind_
== DBINOM
|| kind_
== TBINOM
)
564 features
.require("amsmath");
565 InsetMathNest::validate(features
);