]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathFrac.cpp
8a79214021a8b7a9aee425211ea09652210966e3
[lyx.git] / src / mathed / InsetMathFrac.cpp
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.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  * \author Uwe Stöhr
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "InsetMathFrac.h"
16
17 #include "MathData.h"
18 #include "MathParser.h"
19 #include "MathStream.h"
20 #include "MathSupport.h"
21
22 #include "Cursor.h"
23 #include "LaTeXFeatures.h"
24 #include "MetricsInfo.h"
25 #include "TextPainter.h"
26
27 #include "frontends/FontMetrics.h"
28 #include "frontends/Painter.h"
29
30 #include "support/lassert.h"
31
32 using namespace std;
33
34
35 namespace lyx {
36
37 /////////////////////////////////////////////////////////////////////
38 //
39 // InsetMathFracBase
40 //
41 /////////////////////////////////////////////////////////////////////
42
43
44 InsetMathFracBase::InsetMathFracBase(Buffer * buf, idx_type ncells)
45         : InsetMathNest(buf, ncells)
46 {}
47
48
49 bool InsetMathFracBase::idxUpDown(Cursor & cur, bool up) const
50 {
51         // If we only have one cell, target = 0, otherwise
52         // target = up ? 0 : 1, since upper cell has idx 0
53         idx_type target = nargs() > 1 ? !up : 0;
54         if (cur.idx() == target)
55                 return false;
56         cur.idx() = target;
57         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
58         return true;
59 }
60
61
62
63 /////////////////////////////////////////////////////////////////////
64 //
65 // InsetMathFrac
66 //
67 /////////////////////////////////////////////////////////////////////
68
69
70 InsetMathFrac::InsetMathFrac(Buffer * buf, Kind kind, idx_type ncells)
71         : InsetMathFracBase(buf, ncells), kind_(kind)
72 {}
73
74
75 Inset * InsetMathFrac::clone() const
76 {
77         return new InsetMathFrac(*this);
78 }
79
80
81 InsetMathFrac * InsetMathFrac::asFracInset()
82 {
83         return kind_ == ATOP ? nullptr : this;
84 }
85
86
87 InsetMathFrac const * InsetMathFrac::asFracInset() const
88 {
89         return kind_ == ATOP ? nullptr : this;
90 }
91
92
93 bool InsetMathFrac::idxForward(Cursor & cur) const
94 {
95         idx_type target = 0;
96         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
97                 if (nargs() == 3)
98                         target = 0;
99                 else if (nargs() == 2)
100                         target = 1;
101         } else
102                 return false;
103         if (cur.idx() == target)
104                 return false;
105         cur.idx() = target;
106         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
107         return true;
108 }
109
110
111 bool InsetMathFrac::idxBackward(Cursor & cur) const
112 {
113         idx_type target = 0;
114         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
115                 if (nargs() == 3)
116                         target = 2;
117                 else if (nargs() == 2)
118                         target = 0;
119         } else
120                 return false;
121         if (cur.idx() == target)
122                 return false;
123         cur.idx() = target;
124         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
125         return true;
126 }
127
128
129 MathClass InsetMathFrac::mathClass() const
130 {
131         // Generalized fractions are of inner class (see The TeXbook, p. 292)
132         // But stuff from the unit/nicefrac packages are not real fractions.
133         MathClass mc = MC_ORD;
134         switch (kind_) {
135         case ATOP:
136         case OVER:
137         case FRAC:
138         case DFRAC:
139         case TFRAC:
140         case CFRAC:
141         case CFRACLEFT:
142         case CFRACRIGHT:
143                 mc = MC_INNER;
144                 break;
145         case NICEFRAC:
146         case UNITFRAC:
147         case UNIT:
148                 break;
149         }
150         return mc;
151 }
152
153
154
155 namespace {
156
157 // align the top of M in the cell with the top of M in the surrounding font
158 int dy_for_nicefrac(MetricsBase & mb)
159 {
160         // this is according to nicefrac.sty
161         int big_m = theFontMetrics(mb.font).ascent('M');
162         Changer dummy = mb.changeScript();
163         int small_m = theFontMetrics(mb.font).ascent('M');
164         return big_m - small_m;
165 }
166
167
168 // symbol for nicefrac solidus
169 latexkeys const * slash_symbol()
170 {
171         return in_word_set(from_ascii("slash"));
172 }
173
174 } // namespace
175
176
177 void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
178 {
179         Dimension dim0, dim1, dim2;
180         Changer dummy3 = mi.base.changeEnsureMath();
181
182         switch (kind_) {
183         case UNIT: {
184                 // \unitone, \unittwo
185                 dim.wid = 0;
186                 int unit_cell = 0;
187                 // is there an extra cell holding the value being given a dimension?
188                 // (this is \unittwo)
189                 if (nargs() == 2) {
190                         cell(0).metrics(mi, dim1);
191                         dim.wid += dim1.wid + 4;
192                         unit_cell = 1;
193                 }
194                 Changer dummy = mi.base.font.changeShape(UP_SHAPE);
195                 cell(unit_cell).metrics(mi, dim0);
196                 dim.wid += dim0.width() + 1;
197                 dim.asc = max(dim0.asc, dim1.asc);
198                 dim.des = max(dim0.des, dim1.des);
199         }
200                 break;
201
202         case UNITFRAC:
203         case NICEFRAC: {
204                 // \unitfrac, \unitfracthree, \nicefrac
205                 dim.wid = 0;
206                 dim.asc = 0;
207                 dim.des = 0;
208                 int const dy = dy_for_nicefrac(mi.base);
209                 // is there an extra cell holding the value being given a dimension?
210                 // (this is \unitfracthree)
211                 if (kind_ == UNITFRAC && nargs() == 3) {
212                         cell(2).metrics(mi, dim2);
213                         dim.wid += dim2.wid + 4;
214                         dim.asc = dim2.asc;
215                         dim.des = dim2.des;
216                 }
217                 Changer dummy = (kind_ == UNITFRAC) ? mi.base.font.changeShape(UP_SHAPE)
218                         : noChange();
219                 Changer dummy2 = mi.base.changeScript();
220                 if (latexkeys const * slash = slash_symbol()) {
221                         Dimension dimslash;
222                         mathedSymbolDim(mi.base, dimslash, slash);
223                         dim.wid += dimslash.wid - mathed_mu(mi.base.font, 3.0);
224                         dim.asc = max(dim.asc, dimslash.asc);
225                         dim.des = max(dim.des, dimslash.des);
226                 }
227                 cell(0).metrics(mi, dim0);
228                 cell(1).metrics(mi, dim1);
229                 dim.wid += dim0.wid + dim1.wid + 2;
230                 dim.asc = max(max(dim.asc, dim0.asc + dy), dim1.asc);
231                 dim.des = max(max(dim.des, dim0.des - dy), dim1.des);
232         }
233                 break;
234
235         case FRAC:
236         case CFRAC:
237         case CFRACLEFT:
238         case CFRACRIGHT:
239         case DFRAC:
240         case TFRAC:
241         case OVER:
242         case ATOP: {
243                 int const dy = axis_height(mi.base);
244                 Changer dummy =
245                         // \tfrac is always in text size
246                         (kind_ == TFRAC) ? mi.base.font.changeStyle(SCRIPT_STYLE) :
247                         // \cfrac and \dfrac are always in display size
248                         (kind_ == CFRAC
249                          || kind_ == CFRACLEFT
250                          || kind_ == CFRACRIGHT
251                          || kind_ == DFRAC) ? mi.base.font.changeStyle(DISPLAY_STYLE) :
252                         // all others
253                                               mi.base.changeFrac();
254                 Changer dummy2 = mi.base.changeEnsureMath();
255                 cell(0).metrics(mi, dim0);
256                 cell(1).metrics(mi, dim1);
257                 dim.wid = max(dim0.wid, dim1.wid) + 2;
258                 dim.asc = dim0.height() + dy/2 + dy;
259                 int const t = mi.base.solidLineThickness();
260                 dim.des = max(0, dim1.height() + dy/2 - dy + t);
261         }
262         } //switch (kind_)
263 }
264
265
266 void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
267 {
268         Changer dummy3 = pi.base.changeEnsureMath();
269         Dimension const dim = dimension(*pi.base.bv);
270         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
271         switch (kind_) {
272         case UNIT: {
273                 // \unitone, \unittwo
274                 int xx = x;
275                 int unit_cell = 0;
276                 // is there an extra cell holding the value being given a dimension?
277                 // (this is \unittwo)
278                 if (nargs() == 2) {
279                         cell(0).draw(pi, x, y);
280                         xx += dim0.wid + 4;
281                         unit_cell = 1;
282                 }
283                 Changer dummy = pi.base.font.changeShape(UP_SHAPE);
284                 cell(unit_cell).draw(pi, xx, y);
285         }
286                 break;
287
288         case UNITFRAC:
289         case NICEFRAC: {
290                 // \unitfrac, \unitfracthree, \nicefrac
291                 int xx = x;
292                 int const dy = dy_for_nicefrac(pi.base);
293                 // is there an extra cell holding the value being given a dimension?
294                 // (this is \unitfracthree)
295                 if (kind_ == UNITFRAC && nargs() == 3) {
296                         cell(2).draw(pi, x, y);
297                         xx += cell(2).dimension(*pi.base.bv).wid + 4;
298                 }
299                 Changer dummy = (kind_ == UNITFRAC) ? pi.base.font.changeShape(UP_SHAPE)
300                         : noChange();
301                 // nice fraction
302                 Changer dummy2 = pi.base.changeScript();
303                 cell(0).draw(pi, xx + 1, y - dy);
304                 // reference LaTeX code from nicefrac.sty:
305                 //    \mkern-2mu/\mkern-1mu
306                 if (latexkeys const * slash = slash_symbol()) {
307                         int mkern = mathed_mu(pi.base.font, 2.0);
308                         mathedSymbolDraw(pi, xx + 1 + dim0.wid - mkern, y, slash);
309                         Dimension dimslash;
310                         mathedSymbolDim(pi.base, dimslash, slash);
311                         xx += dimslash.wid - mathed_mu(pi.base.font, 3.0);
312                 }
313                 cell(1).draw(pi, xx + 1 + dim0.wid, y);
314         }
315                 break;
316
317         case FRAC:
318         case CFRAC:
319         case CFRACLEFT:
320         case CFRACRIGHT:
321         case DFRAC:
322         case TFRAC:
323         case OVER:
324         case ATOP: {
325                 int const dy = axis_height(pi.base);
326                 Changer dummy =
327                         // \tfrac is always in text size
328                         (kind_ == TFRAC) ? pi.base.font.changeStyle(SCRIPT_STYLE) :
329                         // \cfrac and \dfrac are always in display size
330                         (kind_ == CFRAC
331                          || kind_ == CFRACLEFT
332                          || kind_ == CFRACRIGHT
333                          || kind_ == DFRAC) ? pi.base.font.changeStyle(DISPLAY_STYLE) :
334                         // all others
335                                               pi.base.changeFrac();
336                 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
337                 int const m = x + dim.wid / 2;
338                 int const xx =
339                         // align left
340                         (kind_ == CFRACLEFT) ? x + 1 :
341                         // align right
342                         (kind_ == CFRACRIGHT) ? x + dim.wid - dim0.wid - 2 :
343                         // center
344                                                 m - dim0.wid / 2;
345                 int const t = pi.base.solidLineThickness();
346                 // take dy/2 for the spacing around the horizontal line. This is
347                 // arbitrary. In LaTeX it is more complicated to ensure that displayed
348                 // fractions line up next to each other.
349                 // For more accurate implementation refer to the TeXbook, Appendix G,
350                 // rules 15a-e.
351                 cell(0).draw(pi, xx, y - dim0.des - dy/2 - dy);
352                 // center
353                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + dy/2 - dy + t);
354                 // horizontal line
355                 if (kind_ != ATOP)
356                         pi.pain.line(x, y - dy, x + dim.wid, y - dy,
357                                      pi.base.font.color(), pi.pain.line_solid, t);
358         }
359         } //switch (kind_)
360 }
361
362
363 void InsetMathFrac::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
364 {
365         Dimension dim0, dim1;
366         cell(0).metricsT(mi, dim0);
367         cell(1).metricsT(mi, dim1);
368         dim.wid = max(dim0.width(), dim1.wid);
369         dim.asc = dim0.height() + 1;
370         dim.des = dim1.height();
371 }
372
373
374 void InsetMathFrac::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
375 {
376         // FIXME: BROKEN!
377         /*
378         Dimension dim;
379         int m = x + dim.width() / 2;
380         cell(0).drawT(pain, m - dim0.width() / 2, y - dim0.des - 1);
381         cell(1).drawT(pain, m - dim1.wid / 2, y + dim1.asc);
382         // ASCII art: ignore niceties
383         if (kind_ == FRAC || kind_ == OVER || kind_ == NICEFRAC || kind_ == UNITFRAC)
384                 pain.horizontalLine(x, y, dim.width());
385         */
386 }
387
388
389 void InsetMathFrac::write(WriteStream & os) const
390 {
391         MathEnsurer ensurer(os);
392         switch (kind_) {
393         case ATOP:
394                 // \\atop is only for compatibility, \\binom is the
395                 // LaTeX2e successor
396                 os << '{' << cell(0) << "\\atop " << cell(1) << '}';
397                 break;
398         case OVER:
399                 // \\over is only for compatibility, normalize this to \\frac
400                 os << "\\frac{" << cell(0) << "}{" << cell(1) << '}';
401                 break;
402         case FRAC:
403         case DFRAC:
404         case TFRAC:
405         case NICEFRAC:
406         case CFRAC:
407         case UNITFRAC:
408                 if (nargs() == 2)
409                         InsetMathNest::write(os);
410                 else
411                         os << "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
412                 break;
413         case UNIT:
414                 if (nargs() == 2)
415                         os << "\\unit[" << cell(0) << "]{" << cell(1) << '}';
416                 else
417                         os << "\\unit{" << cell(0) << '}';
418                 break;
419         case CFRACLEFT:
420                 os << "\\cfrac[l]{" << cell(0) << "}{" << cell(1) << '}';
421                 break;
422         case CFRACRIGHT:
423                 os << "\\cfrac[r]{" << cell(0) << "}{" << cell(1) << '}';
424                 break;
425         }
426 }
427
428
429 docstring InsetMathFrac::name() const
430 {
431         switch (kind_) {
432         case FRAC:
433                 return from_ascii("frac");
434         case CFRAC:
435         case CFRACLEFT:
436         case CFRACRIGHT:
437                 return from_ascii("cfrac");
438         case DFRAC:
439                 return from_ascii("dfrac");
440         case TFRAC:
441                 return from_ascii("tfrac");
442         case OVER:
443                 return from_ascii("over");
444         case NICEFRAC:
445                 return from_ascii("nicefrac");
446         case UNITFRAC:
447                 return from_ascii("unitfrac");
448         case UNIT:
449                 return from_ascii("unit");
450         case ATOP:
451                 return from_ascii("atop");
452         }
453         // shut up stupid compiler
454         return docstring();
455 }
456
457
458 bool InsetMathFrac::extraBraces() const
459 {
460         return kind_ == ATOP || kind_ == OVER;
461 }
462
463
464 void InsetMathFrac::maple(MapleStream & os) const
465 {
466         if (nargs() != 2) {
467                 // Someone who knows about maple should fix this.
468                 LASSERT(false, return);
469         }
470         os << '(' << cell(0) << ")/(" << cell(1) << ')';
471 }
472
473
474 void InsetMathFrac::mathematica(MathematicaStream & os) const
475 {
476         if (nargs() != 2) {
477                 // Someone who knows about mathematica should fix this.
478                 LASSERT(false, return);
479         }
480         os << '(' << cell(0) << ")/(" << cell(1) << ')';
481 }
482
483
484 void InsetMathFrac::octave(OctaveStream & os) const
485 {
486         if (nargs() != 2) {
487                 // Someone who knows about octave should fix this.
488                 LASSERT(false, return);
489         }
490         os << '(' << cell(0) << ")/(" << cell(1) << ')';
491 }
492
493
494 void InsetMathFrac::mathmlize(MathStream & ms) const
495 {
496         switch (kind_) {
497         case ATOP:
498                 ms << MTag("mfrac", "linethickness='0'")
499                    << MTag("mrow") << cell(0) << ETag("mrow")
500                    << MTag("mrow") << cell(1) << ETag("mrow")
501                    << ETag("mfrac");
502                 break;
503
504         // we do not presently distinguish these
505         case OVER:
506         case FRAC:
507         case DFRAC:
508         case TFRAC:
509         case CFRAC:
510         case CFRACLEFT:
511         case CFRACRIGHT:
512                 ms << MTag("mfrac")
513                    << MTag("mrow") << cell(0) << ETag("mrow")
514                    << MTag("mrow") << cell(1) << ETag("mrow")
515                    << ETag("mfrac");
516                 break;
517
518         case NICEFRAC:
519                 ms << MTag("mfrac", "bevelled='true'")
520                    << MTag("mrow") << cell(0) << ETag("mrow")
521                    << MTag("mrow") << cell(1) << ETag("mrow")
522                    << ETag("mfrac");
523                 break;
524
525         case UNITFRAC:
526                 if (nargs() == 3)
527                         ms << cell(2);
528                 ms << MTag("mfrac", "bevelled='true'")
529                    << MTag("mrow") << cell(0) << ETag("mrow")
530                    << MTag("mrow") << cell(1) << ETag("mrow")
531                    << ETag("mfrac");
532                 break;
533
534         case UNIT:
535                 // FIXME This is not right, because we still output mi, etc,
536                 // when we output the cell. So we need to prevent that somehow.
537                 if (nargs() == 2)
538                         ms << cell(0)
539                            << MTag("mstyle mathvariant='normal'") 
540                            << cell(1) 
541                            << ETag("mstyle");
542                 else
543                         ms << MTag("mstyle mathvariant='normal'")
544                            << cell(0)
545                            << ETag("mstyle");
546         }
547 }
548
549
550 void InsetMathFrac::htmlize(HtmlStream & os) const
551 {
552         switch (kind_) {
553         case ATOP:
554                 os << MTag("span", "class='frac'")
555                          << MTag("span", "class='numer'") << cell(0) << ETag("span")
556                          << MTag("span", "class='numer'") << cell(1) << ETag("span")
557                          << ETag("span");
558                 break;
559
560         // we do not presently distinguish these
561         case OVER:
562         case FRAC:
563         case DFRAC:
564         case TFRAC:
565         case CFRAC:
566         case CFRACLEFT:
567         case CFRACRIGHT:
568                 os << MTag("span", "class='frac'")
569                          << MTag("span", "class='numer'") << cell(0) << ETag("span")
570                          << MTag("span", "class='denom'") << cell(1) << ETag("span")
571                          << ETag("span");
572                 break;
573
574         case NICEFRAC:
575                 os << cell(0) << '/' << cell(1);
576                 break;
577
578         case UNITFRAC:
579                 if (nargs() == 3)
580                         os << cell(2) << ' ';
581                 os << cell(0) << '/' << cell(1);
582                 break;
583
584         case UNIT:
585                 // FIXME This is not right, because we still output i, etc,
586                 // when we output the cell. So we need to prevent that somehow.
587                 if (nargs() == 2)
588                         os << cell(0)
589                            << MTag("span")
590                            << cell(1)
591                            << ETag("span");
592                 else
593                         os << MTag("span")
594                            << cell(0)
595                            << ETag("span");
596         }
597 }
598
599
600 void InsetMathFrac::validate(LaTeXFeatures & features) const
601 {
602         if (kind_ == NICEFRAC || kind_ == UNITFRAC || kind_ == UNIT)
603                 features.require("units");
604         if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
605                   || kind_ == DFRAC || kind_ == TFRAC)
606                 features.require("amsmath");
607         if (features.runparams().math_flavor == OutputParams::MathAsHTML)
608                 // CSS adapted from eLyXer
609                 features.addCSSSnippet(
610                         "span.frac{display: inline-block; vertical-align: middle; text-align:center;}\n"
611                         "span.numer{display: block;}\n"
612                         "span.denom{display: block; border-top: thin solid #000040;}");
613         InsetMathNest::validate(features);
614 }
615
616
617 /////////////////////////////////////////////////////////////////////
618 //
619 // InsetMathBinom
620 //
621 /////////////////////////////////////////////////////////////////////
622
623
624 InsetMathBinom::InsetMathBinom(Buffer * buf, Kind kind)
625         : InsetMathFracBase(buf), kind_(kind)
626 {}
627
628
629 Inset * InsetMathBinom::clone() const
630 {
631         return new InsetMathBinom(*this);
632 }
633
634
635 int InsetMathBinom::dw(int height) const
636 {
637         int w = height / 5;
638         if (w > 15)
639                 w = 15;
640         if (w < 6)
641                 w = 6;
642         return w;
643 }
644
645
646 void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
647 {
648         Changer dummy2 = mi.base.changeEnsureMath();
649         Dimension dim0, dim1;
650         int const dy = axis_height(mi.base);
651         Changer dummy =
652                 (kind_ == DBINOM) ? mi.base.font.changeStyle(DISPLAY_STYLE) :
653                 (kind_ == TBINOM) ? mi.base.font.changeStyle(SCRIPT_STYLE) :
654                                     mi.base.changeFrac();
655         cell(0).metrics(mi, dim0);
656         cell(1).metrics(mi, dim1);
657         dim.asc = dim0.height() + 1 + dy/2 + dy;
658         dim.des = max(0, dim1.height() + 1 + dy/2 - dy);
659         dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
660 }
661
662
663 void InsetMathBinom::draw(PainterInfo & pi, int x, int y) const
664 {
665         Changer dummy2 = pi.base.changeEnsureMath();
666         Dimension const dim = dimension(*pi.base.bv);
667         Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
668         Dimension const & dim1 = cell(1).dimension(*pi.base.bv);
669         int const dy = axis_height(pi.base);
670         // define the binom brackets
671         docstring const bra = kind_ == BRACE ? from_ascii("{") :
672                 kind_ == BRACK ? from_ascii("[") : from_ascii("(");
673         docstring const ket = kind_ == BRACE ? from_ascii("}") :
674                 kind_ == BRACK ? from_ascii("]") : from_ascii(")");
675
676         int m = x + dim.width() / 2;
677         {
678                 Changer dummy =
679                         (kind_ == DBINOM) ? pi.base.font.changeStyle(DISPLAY_STYLE) :
680                         (kind_ == TBINOM) ? pi.base.font.changeStyle(SCRIPT_STYLE) :
681                                             pi.base.changeFrac();
682                 // take dy both for the vertical alignment and for the spacing between
683                 // cells
684                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - dy/2 - dy);
685                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + dy/2 - dy);
686         }
687         // draw the brackets and the marker
688         mathed_draw_deco(pi, x, y - dim.ascent(), dw(dim.height()),
689                 dim.height(), bra);
690         mathed_draw_deco(pi, x + dim.width() - dw(dim.height()),
691                 y - dim.ascent(), dw(dim.height()), dim.height(), ket);
692 }
693
694
695 bool InsetMathBinom::extraBraces() const
696 {
697         return kind_ == CHOOSE || kind_ == BRACE || kind_ == BRACK;
698 }
699
700
701 void InsetMathBinom::write(WriteStream & os) const
702 {
703         MathEnsurer ensurer(os);
704         switch (kind_) {
705         case BINOM:
706                 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
707                 break;
708         case DBINOM:
709                 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
710                 break;
711         case TBINOM:
712                 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
713                 break;
714         case CHOOSE:
715                 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
716                 break;
717         case BRACE:
718                 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
719                 break;
720         case BRACK:
721                 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
722                 break;
723         }
724 }
725
726
727 void InsetMathBinom::normalize(NormalStream & os) const
728 {
729         os << "[binom " << cell(0) << ' ' << cell(1) << ']';
730 }
731
732
733 void InsetMathBinom::mathmlize(MathStream & ms) const
734 {
735         char ldelim = ' ';
736         char rdelim = ' ';
737         switch (kind_) {
738         case BINOM:
739         case TBINOM:
740         case DBINOM:
741         case CHOOSE:
742                 ldelim = '(';
743                 rdelim = ')';
744                 break;
745         case BRACE:
746                 ldelim = '{';
747                 rdelim = '}';
748                 break;
749         case BRACK:
750                 ldelim = '[';
751                 rdelim = ']';
752                 break;
753         }
754         ms << "<" << from_ascii(ms.namespacedTag("mo")) << " fence='true' stretchy='true' form='prefix'>"
755            << ldelim
756            << "</" << from_ascii(ms.namespacedTag("mo")) << ">"
757            << "<" << from_ascii(ms.namespacedTag("mfrac")) << " linethickness='0'>"
758            << cell(0) << cell(1)
759        << "</" << from_ascii(ms.namespacedTag("mfrac")) << ">"
760            << "<" << from_ascii(ms.namespacedTag("mo")) << " fence='true' stretchy='true' form='postfix'>"
761            << rdelim
762            << "</" << from_ascii(ms.namespacedTag("mo")) << ">";
763 }
764
765
766 void InsetMathBinom::htmlize(HtmlStream & os) const
767 {
768         char ldelim = ' ';
769         char rdelim = ' ';
770         switch (kind_) {
771         case BINOM:
772         case TBINOM:
773         case DBINOM:
774         case CHOOSE:
775                 ldelim = '(';
776                 rdelim = ')';
777                 break;
778         case BRACE:
779                 ldelim = '{';
780                 rdelim = '}';
781                 break;
782         case BRACK:
783                 ldelim = '[';
784                 rdelim = ']';
785                 break;
786         }
787         os << MTag("span", "class='binomdelim'") << ldelim << ETag("span") << '\n'
788            << MTag("span", "class='binom'") << '\n'
789            << MTag("span") << cell(0) << ETag("span") << '\n'
790            << MTag("span") << cell(1) << ETag("span") << '\n'
791            << ETag("span") << '\n'
792                  << MTag("span", "class='binomdelim'") << rdelim << ETag("span") << '\n';
793 }
794
795
796 void InsetMathBinom::validate(LaTeXFeatures & features) const
797 {
798         if (features.runparams().isLaTeX()) {
799                 if (kind_ == BINOM)
800                         features.require("binom");
801                 if (kind_ == DBINOM || kind_ == TBINOM)
802                         features.require("amsmath");
803         } else if (features.runparams().math_flavor == OutputParams::MathAsHTML)
804                 features.addCSSSnippet(
805                         "span.binom{display: inline-block; vertical-align: bottom; text-align:center;}\n"
806                         "span.binom span{display: block;}\n"
807                         "span.binomdelim{font-size: 2em;}");
808         InsetMathNest::validate(features);
809 }
810
811
812 } // namespace lyx