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