A bit more re-organization.
[lyx.git] / src / mathed / InsetMathFrac.cpp
blob7f5323b8d611548a3f38201fe7ca8f100ffabe36
1 /**
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
7 * \author André Pönitz
8 * \author Uwe Stöhr
10 * Full author contact details are available in file CREDITS.
13 #include <config.h>
15 #include "InsetMathFrac.h"
17 #include "Cursor.h"
18 #include "LaTeXFeatures.h"
19 #include "MathData.h"
20 #include "MathStream.h"
21 #include "MathSupport.h"
22 #include "MetricsInfo.h"
23 #include "TextPainter.h"
25 #include "frontends/Painter.h"
27 using namespace std;
29 namespace lyx {
31 /////////////////////////////////////////////////////////////////////
33 // InsetMathFracBase
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)
47 return false;
48 cur.idx() = target;
49 cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
50 return true;
55 /////////////////////////////////////////////////////////////////////
57 // InsetMathFrac
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)) {
89 if (nargs() == 3)
90 target = 0;
91 else if (nargs() == 2)
92 target = 1;
93 } else
94 return false;
95 if (cur.idx() == target)
96 return false;
97 cur.idx() = target;
98 cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
99 return true;
103 bool InsetMathFrac::idxBackward(Cursor & cur) const
105 InsetMath::idx_type target = 0;
106 if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
107 if (nargs() == 3)
108 target = 2;
109 else if (nargs() == 2)
110 target = 0;
111 } else
112 return false;
113 if (cur.idx() == target)
114 return false;
115 cur.idx() = target;
116 cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
117 return true;
121 void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
123 Dimension dim0, dim1, dim2;
125 if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
126 if (nargs() == 1) {
127 ShapeChanger dummy2(mi.base.font, UP_SHAPE);
128 cell(0).metrics(mi, dim0);
129 dim.wid = dim0.width()+ 3;
130 dim.asc = dim0.asc;
131 dim.des = dim0.des;
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);
139 } else {
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);
149 } else {
150 // general cell metrics used for \frac
151 FracChanger dummy(mi.base);
152 cell(0).metrics(mi, dim0);
153 cell(1).metrics(mi, dim1);
154 if (nargs() == 3)
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;
166 } else {
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;
184 metricsMarkers(dim);
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)) {
194 if (nargs() == 1) {
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);
201 } else {
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,
209 y - dim0.des - 5);
210 cell(1).draw(pi, xx + dim0.width() + 5,
211 y + dim1.asc / 2);
213 } else {
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,
219 y - dim0.des - 5);
220 cell(1).draw(pi, x + dim0.width() + 5,
221 y + dim1.asc / 2);
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);
234 } else {
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) {
248 // Diag line:
249 int xx = x;
250 if (nargs() == 3)
251 xx += cell(2).dimension(*pi.base.bv).wid + 5;
252 pi.pain.line(xx + dim0.wid,
253 y + dim.des - 2,
254 xx + dim0.wid + 5,
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
279 // FIXME: BROKEN!
281 Dimension dim;
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);
295 switch (kind_) {
296 case ATOP:
297 // \\atop is only for compatibility, \\binom is the
298 // LaTeX2e successor
299 os << '{' << cell(0) << "\\atop " << cell(1) << '}';
300 break;
301 case OVER:
302 // \\over is only for compatibility, normalize this to \\frac
303 os << "\\frac{" << cell(0) << "}{" << cell(1) << '}';
304 break;
305 case FRAC:
306 case DFRAC:
307 case TFRAC:
308 case NICEFRAC:
309 case CFRAC:
310 case UNITFRAC:
311 if (nargs() == 2)
312 InsetMathNest::write(os);
313 else
314 os << "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
315 break;
316 case UNIT:
317 if (nargs() == 2)
318 os << "\\unit[" << cell(0) << "]{" << cell(1) << '}';
319 else
320 os << "\\unit{" << cell(0) << '}';
321 break;
322 case CFRACLEFT:
323 os << "\\cfrac[l]{" << cell(0) << "}{" << cell(1) << '}';
324 break;
325 case CFRACRIGHT:
326 os << "\\cfrac[r]{" << cell(0) << "}{" << cell(1) << '}';
327 break;
332 docstring InsetMathFrac::name() const
334 switch (kind_) {
335 case FRAC:
336 return from_ascii("frac");
337 case CFRAC:
338 case CFRACLEFT:
339 case CFRACRIGHT:
340 return from_ascii("cfrac");
341 case DFRAC:
342 return from_ascii("dfrac");
343 case TFRAC:
344 return from_ascii("tfrac");
345 case OVER:
346 return from_ascii("over");
347 case NICEFRAC:
348 return from_ascii("nicefrac");
349 case UNITFRAC:
350 return from_ascii("unitfrac");
351 case UNIT:
352 return from_ascii("unit");
353 case ATOP:
354 return from_ascii("atop");
356 // shut up stupid compiler
357 return docstring();
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
387 switch (kind_) {
388 case DFRAC:
389 os << MTag("mdfrac") << cell(0) << cell(1) << ETag("mdfrac");
390 break;
391 case TFRAC:
392 os << MTag("mtfrac") << cell(0) << cell(1) << ETag("mtfrac");
393 break;
394 case FRAC:
395 default:
396 os << MTag("mfrac") << cell(0) << cell(1) << ETag("mfrac");
397 break;
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 /////////////////////////////////////////////////////////////////////
415 // InsetMathBinom
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
433 int w = height / 5;
434 if (w > 15)
435 w = 15;
436 if (w < 6)
437 w = 6;
438 return w;
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);
456 } else {
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);
490 } else {
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()),
497 dim.height(), bra);
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);
513 switch (kind_) {
514 case BINOM:
515 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
516 break;
517 case DBINOM:
518 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
519 break;
520 case TBINOM:
521 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
522 break;
523 case CHOOSE:
524 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
525 break;
526 case BRACE:
527 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
528 break;
529 case BRACK:
530 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
531 break;
536 void InsetMathBinom::normalize(NormalStream & os) const
538 os << "[binom " << cell(0) << ' ' << cell(1) << ']';
542 void InsetMathBinom::mathmlize(MathStream & os) const
544 switch (kind_) {
545 case BINOM:
546 os << MTag("mbinom") << cell(0) << cell(1) << ETag("mbinom");
547 break;
548 case TBINOM:
549 os << MTag("mtbinom") << cell(0) << cell(1) << ETag("mtbinom");
550 break;
551 case DBINOM:
552 default:
553 os << MTag("mdbinom") << cell(0) << cell(1) << ETag("mdbinom");
554 break;
559 void InsetMathBinom::validate(LaTeXFeatures & features) const
561 if (kind_ == BINOM)
562 features.require("binom");
563 if (kind_ == DBINOM || kind_ == TBINOM)
564 features.require("amsmath");
565 InsetMathNest::validate(features);
569 } // namespace lyx