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