]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathFrac.cpp
Use convertDelimToXMLEscape with matrix delimiters, too.
[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 "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"
24
25 #include "frontends/Painter.h"
26
27 #include "support/lassert.h"
28
29 using namespace std;
30
31
32 namespace lyx {
33
34 /////////////////////////////////////////////////////////////////////
35 //
36 // InsetMathFracBase
37 //
38 /////////////////////////////////////////////////////////////////////
39
40
41 InsetMathFracBase::InsetMathFracBase(Buffer * buf, idx_type ncells)
42         : InsetMathNest(buf, ncells)
43 {}
44
45
46 bool InsetMathFracBase::idxUpDown(Cursor & cur, bool up) const
47 {
48         // If we only have one cell, target = 0, otherwise
49         // target = up ? 0 : 1, since upper cell has idx 0
50         InsetMath::idx_type target = nargs() > 1 ? !up : 0;
51         if (cur.idx() == target)
52                 return false;
53         cur.idx() = target;
54         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
55         return true;
56 }
57
58
59
60 /////////////////////////////////////////////////////////////////////
61 //
62 // InsetMathFrac
63 //
64 /////////////////////////////////////////////////////////////////////
65
66
67 InsetMathFrac::InsetMathFrac(Buffer * buf, Kind kind, InsetMath::idx_type ncells)
68         : InsetMathFracBase(buf, ncells), kind_(kind)
69 {}
70
71
72 Inset * InsetMathFrac::clone() const
73 {
74         return new InsetMathFrac(*this);
75 }
76
77
78 InsetMathFrac * InsetMathFrac::asFracInset()
79 {
80         return kind_ == ATOP ? 0 : this;
81 }
82
83
84 InsetMathFrac const * InsetMathFrac::asFracInset() const
85 {
86         return kind_ == ATOP ? 0 : this;
87 }
88
89
90 bool InsetMathFrac::idxForward(Cursor & cur) const
91 {
92         InsetMath::idx_type target = 0;
93         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
94                 if (nargs() == 3)
95                         target = 0;
96                 else if (nargs() == 2)
97                         target = 1;
98         } else
99                 return false;
100         if (cur.idx() == target)
101                 return false;
102         cur.idx() = target;
103         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
104         return true;
105 }
106
107
108 bool InsetMathFrac::idxBackward(Cursor & cur) const
109 {
110         InsetMath::idx_type target = 0;
111         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
112                 if (nargs() == 3)
113                         target = 2;
114                 else if (nargs() == 2)
115                         target = 0;
116         } else
117                 return false;
118         if (cur.idx() == target)
119                 return false;
120         cur.idx() = target;
121         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
122         return true;
123 }
124
125
126 void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
127 {
128         Dimension dim0, dim1, dim2;
129
130         // This could be simplified, including avoiding useless recalculation of
131         // cell metrics
132         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
133                 if (nargs() == 1) {
134                         Changer dummy = mi.base.font.changeShape(UP_SHAPE);
135                         cell(0).metrics(mi, dim0);
136                         dim.wid = dim0.width()+ 3;
137                         dim.asc = dim0.asc;
138                         dim.des = dim0.des;
139                 } else if (nargs() == 2) {
140                         cell(0).metrics(mi, dim0);
141                         Changer dummy = mi.base.font.changeShape(UP_SHAPE);
142                         cell(1).metrics(mi, dim1);
143                         dim.wid = dim0.width() + dim1.wid + 5;
144                         dim.asc = max(dim0.asc, dim1.asc);
145                         dim.des = max(dim0.des, dim1.des);
146                 } else {
147                         cell(2).metrics(mi, dim2);
148                         Changer dummy = mi.base.font.changeShape(UP_SHAPE);
149                         Changer dummy2 = mi.base.changeFrac();
150                         cell(0).metrics(mi, dim0);
151                         cell(1).metrics(mi, dim1);
152                         dim.wid = dim0.width() + dim1.wid + dim2.wid + 10;
153                         dim.asc = max(dim2.asc, dim0.height() + 5);
154                         dim.des = max(dim2.des, dim1.height() - 5);
155                 }
156         } else {
157                 // general cell metrics used for \frac
158                 Changer dummy = mi.base.changeFrac();
159                 cell(0).metrics(mi, dim0);
160                 cell(1).metrics(mi, dim1);
161                 if (nargs() == 3)
162                         cell(2).metrics(mi, dim2);
163                 // metrics for special fraction types
164                 if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
165                         Changer dummy2 = mi.base.font.changeShape(UP_SHAPE, kind_ == UNITFRAC);
166                         dim.wid = dim0.width() + dim1.wid + 5;
167                         dim.asc = dim0.height() + 5;
168                         dim.des = dim1.height() - 5;
169                 } else {
170                         if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
171                             || kind_ == DFRAC || kind_ == TFRAC) {
172                                 // \cfrac and \dfrac are always in display size
173                                 // \tfrac is in always in text size
174                                 Changer dummy2 = mi.base.changeStyle((kind_ == TFRAC)
175                                                                      ? LM_ST_SCRIPT
176                                                                      : LM_ST_DISPLAY);
177                                 cell(0).metrics(mi, dim0);
178                                 cell(1).metrics(mi, dim1);
179                         }
180                         dim.wid = max(dim0.wid, dim1.wid) + 2;
181                         dim.asc = dim0.height() + 2 + 5;
182                         dim.des = dim1.height() + 2 - 5;
183                 }
184         }
185         metricsMarkers(dim);
186 }
187
188
189 void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
190 {
191         setPosCache(pi, x, y);
192         Dimension const dim = dimension(*pi.base.bv);
193         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
194         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
195                 if (nargs() == 1) {
196                         Changer dummy = pi.base.font.changeShape(UP_SHAPE);
197                         cell(0).draw(pi, x + 1, y);
198                 } else if (nargs() == 2) {
199                         cell(0).draw(pi, x + 1, y);
200                         Changer dummy = pi.base.font.changeShape(UP_SHAPE);
201                         cell(1).draw(pi, x + dim0.width() + 5, y);
202                 } else {
203                         cell(2).draw(pi, x + 1, y);
204                         Changer dummy = pi.base.font.changeShape(UP_SHAPE);
205                         Changer dummy2 = pi.base.changeFrac();
206                         Dimension const dim1 = cell(1).dimension(*pi.base.bv);
207                         Dimension const dim2 = cell(2).dimension(*pi.base.bv);
208                         int xx = x + dim2.wid + 5;
209                         cell(0).draw(pi, xx + 2, 
210                                          y - dim0.des - 5);
211                         cell(1).draw(pi, xx  + dim0.width() + 5, 
212                                          y + dim1.asc / 2);
213                 }
214         } else {
215                 Changer dummy = pi.base.changeFrac();
216                 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
217                 int m = x + dim.wid / 2;
218                 if (kind_ == NICEFRAC) {
219                         cell(0).draw(pi, x + 2,
220                                         y - dim0.des - 5);
221                         cell(1).draw(pi, x + dim0.width() + 5,
222                                         y + dim1.asc / 2);
223                 } else if (kind_ == UNITFRAC) {
224                         Changer dummy2 = pi.base.font.changeShape(UP_SHAPE);
225                         cell(0).draw(pi, x + 2, y - dim0.des - 5);
226                         cell(1).draw(pi, x + dim0.width() + 5, y + dim1.asc / 2);
227                 } else if (kind_ == FRAC || kind_ == ATOP || kind_ == OVER
228                            || kind_ == TFRAC) {
229                         // tfrac is in always in text size
230                         Changer dummy2 = pi.base.changeStyle(LM_ST_SCRIPT, kind_ == TFRAC);
231                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
232                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
233                 } else {
234                         // \cfrac and \dfrac are always in display size
235                         Changer dummy2 = pi.base.changeStyle(LM_ST_DISPLAY);
236                         if (kind_ == CFRAC || kind_ == DFRAC)
237                                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
238                         else if (kind_ == CFRACLEFT)
239                                 cell(0).draw(pi, x + 2, y - dim0.des - 2 - 5);
240                         else if (kind_ == CFRACRIGHT)
241                                 cell(0).draw(pi, x + dim.wid - dim0.wid - 2,
242                                         y - dim0.des - 2 - 5);
243                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
244                 }
245         }
246         if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
247                 // Diag line:
248                 int xx = x;
249                 if (nargs() == 3)
250                         xx += cell(2).dimension(*pi.base.bv).wid + 5;
251                 pi.pain.line(xx + dim0.wid,
252                                 y + dim.des - 2,
253                                 xx + dim0.wid + 5,
254                                 y - dim.asc + 2, pi.base.font.color());
255         }
256         if (kind_ == FRAC || kind_ == CFRAC || kind_ == CFRACLEFT
257                 || kind_ == CFRACRIGHT || kind_ == DFRAC
258                 || kind_ == TFRAC || kind_ == OVER)
259                 pi.pain.line(x + 1, y - 5,
260                                 x + dim.wid - 2, y - 5, pi.base.font.color());
261         drawMarkers(pi, x, y);
262 }
263
264
265 void InsetMathFrac::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
266 {
267         Dimension dim0, dim1;
268         cell(0).metricsT(mi, dim0);
269         cell(1).metricsT(mi, dim1);
270         dim.wid = max(dim0.width(), dim1.wid);
271         dim.asc = dim0.height() + 1;
272         dim.des = dim1.height();
273 }
274
275
276 void InsetMathFrac::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
277 {
278         // FIXME: BROKEN!
279         /*
280         Dimension dim;
281         int m = x + dim.width() / 2;
282         cell(0).drawT(pain, m - dim0.width() / 2, y - dim0.des - 1);
283         cell(1).drawT(pain, m - dim1.wid / 2, y + dim1.asc);
284         // ASCII art: ignore niceties
285         if (kind_ == FRAC || kind_ == OVER || kind_ == NICEFRAC || kind_ == UNITFRAC)
286                 pain.horizontalLine(x, y, dim.width());
287         */
288 }
289
290
291 void InsetMathFrac::write(WriteStream & os) const
292 {
293         MathEnsurer ensurer(os);
294         switch (kind_) {
295         case ATOP:
296                 // \\atop is only for compatibility, \\binom is the
297                 // LaTeX2e successor
298                 os << '{' << cell(0) << "\\atop " << cell(1) << '}';
299                 break;
300         case OVER:
301                 // \\over is only for compatibility, normalize this to \\frac
302                 os << "\\frac{" << cell(0) << "}{" << cell(1) << '}';
303                 break;
304         case FRAC:
305         case DFRAC:
306         case TFRAC:
307         case NICEFRAC:
308         case CFRAC:
309         case UNITFRAC:
310                 if (nargs() == 2)
311                         InsetMathNest::write(os);
312                 else
313                         os << "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
314                 break;
315         case UNIT:
316                 if (nargs() == 2)
317                         os << "\\unit[" << cell(0) << "]{" << cell(1) << '}';
318                 else
319                         os << "\\unit{" << cell(0) << '}';
320                 break;
321         case CFRACLEFT:
322                 os << "\\cfrac[l]{" << cell(0) << "}{" << cell(1) << '}';
323                 break;
324         case CFRACRIGHT:
325                 os << "\\cfrac[r]{" << cell(0) << "}{" << cell(1) << '}';
326                 break;
327         }
328 }
329
330
331 docstring InsetMathFrac::name() const
332 {
333         switch (kind_) {
334         case FRAC:
335                 return from_ascii("frac");
336         case CFRAC:
337         case CFRACLEFT:
338         case CFRACRIGHT:
339                 return from_ascii("cfrac");
340         case DFRAC:
341                 return from_ascii("dfrac");
342         case TFRAC:
343                 return from_ascii("tfrac");
344         case OVER:
345                 return from_ascii("over");
346         case NICEFRAC:
347                 return from_ascii("nicefrac");
348         case UNITFRAC:
349                 return from_ascii("unitfrac");
350         case UNIT:
351                 return from_ascii("unit");
352         case ATOP:
353                 return from_ascii("atop");
354         }
355         // shut up stupid compiler
356         return docstring();
357 }
358
359
360 bool InsetMathFrac::extraBraces() const
361 {
362         return kind_ == ATOP || kind_ == OVER;
363 }
364
365
366 void InsetMathFrac::maple(MapleStream & os) const
367 {
368         if (nargs() != 2) {
369                 // Someone who knows about maple should fix this.
370                 LASSERT(false, return);
371         }
372         os << '(' << cell(0) << ")/(" << cell(1) << ')';
373 }
374
375
376 void InsetMathFrac::mathematica(MathematicaStream & os) const
377 {
378         if (nargs() != 2) {
379                 // Someone who knows about mathematica should fix this.
380                 LASSERT(false, return);
381         }
382         os << '(' << cell(0) << ")/(" << cell(1) << ')';
383 }
384
385
386 void InsetMathFrac::octave(OctaveStream & os) const
387 {
388         if (nargs() != 2) {
389                 // Someone who knows about octave should fix this.
390                 LASSERT(false, return);
391         }
392         os << '(' << cell(0) << ")/(" << cell(1) << ')';
393 }
394
395
396 void InsetMathFrac::mathmlize(MathStream & os) const
397 {
398         switch (kind_) {
399         case ATOP:
400                 os << MTag("mfrac", "linethickeness='0'")
401                    << MTag("mrow") << cell(0) << ETag("mrow")
402                          << MTag("mrow") << cell(1) << ETag("mrow")
403                          << ETag("mfrac");
404                 break;
405
406         // we do not presently distinguish these
407         case OVER:
408         case FRAC:
409         case DFRAC:
410         case TFRAC:
411         case CFRAC:
412         case CFRACLEFT:
413         case CFRACRIGHT:
414                 os << MTag("mfrac")
415                    << MTag("mrow") << cell(0) << ETag("mrow")
416                          << MTag("mrow") << cell(1) << ETag("mrow")
417                          << ETag("mfrac");
418                 break;
419
420         case NICEFRAC:
421                 os << MTag("mfrac", "bevelled='true'")
422                    << MTag("mrow") << cell(0) << ETag("mrow")
423                          << MTag("mrow") << cell(1) << ETag("mrow")
424                          << ETag("mfrac");
425                 break;
426
427         case UNITFRAC:
428                 if (nargs() == 3)
429                         os << cell(2);
430                 os << MTag("mfrac", "bevelled='true'")
431                    << MTag("mrow") << cell(0) << ETag("mrow")
432                          << MTag("mrow") << cell(1) << ETag("mrow")
433                          << ETag("mfrac");
434                 break;
435
436         case UNIT:
437                 // FIXME This is not right, because we still output mi, etc,
438                 // when we output the cell. So we need to prevent that somehow.
439                 if (nargs() == 2)
440                         os << cell(0) 
441                            << MTag("mstyle mathvariant='normal'") 
442                            << cell(1) 
443                            << ETag("mstyle");
444                 else
445                         os << MTag("mstyle mathvariant='normal'") 
446                            << cell(0)
447                            << ETag("mstyle");
448         }
449 }
450
451
452 void InsetMathFrac::htmlize(HtmlStream & os) const
453 {
454         switch (kind_) {
455         case ATOP:
456                 os << MTag("span", "class='frac'")
457                          << MTag("span", "class='numer'") << cell(0) << ETag("span")
458                          << MTag("span", "class='numer'") << cell(1) << ETag("span")
459                          << ETag("span");
460                 break;
461
462         // we do not presently distinguish these
463         case OVER:
464         case FRAC:
465         case DFRAC:
466         case TFRAC:
467         case CFRAC:
468         case CFRACLEFT:
469         case CFRACRIGHT:
470                 os << MTag("span", "class='frac'")
471                          << MTag("span", "class='numer'") << cell(0) << ETag("span")
472                          << MTag("span", "class='denom'") << cell(1) << ETag("span")
473                          << ETag("span");
474                 break;
475
476         case NICEFRAC:
477                 os << cell(0) << '/' << cell(1);
478                 break;
479
480         case UNITFRAC:
481                 if (nargs() == 3)
482                         os << cell(2) << ' ';
483                 os << cell(0) << '/' << cell(1);
484                 break;
485
486         case UNIT:
487                 // FIXME This is not right, because we still output i, etc,
488                 // when we output the cell. So we need to prevent that somehow.
489                 if (nargs() == 2)
490                         os << cell(0) 
491                            << MTag("span") 
492                            << cell(1) 
493                            << ETag("span");
494                 else
495                         os << MTag("span") 
496                            << cell(0)
497                            << ETag("span");
498         }
499 }
500
501
502 void InsetMathFrac::validate(LaTeXFeatures & features) const
503 {
504         if (kind_ == NICEFRAC || kind_ == UNITFRAC || kind_ == UNIT)
505                 features.require("units");
506         if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
507                   || kind_ == DFRAC || kind_ == TFRAC)
508                 features.require("amsmath");
509         if (features.runparams().math_flavor == OutputParams::MathAsHTML)
510                 // CSS adapted from eLyXer
511                 features.addCSSSnippet(
512                         "span.frac{display: inline-block; vertical-align: middle; text-align:center;}\n"
513                         "span.numer{display: block;}\n"
514                         "span.denom{display: block; border-top: thin solid #000040;}");
515         InsetMathNest::validate(features);
516 }
517
518
519 /////////////////////////////////////////////////////////////////////
520 //
521 // InsetMathBinom
522 //
523 /////////////////////////////////////////////////////////////////////
524
525
526 InsetMathBinom::InsetMathBinom(Buffer * buf, Kind kind)
527         : InsetMathFracBase(buf), kind_(kind)
528 {}
529
530
531 Inset * InsetMathBinom::clone() const
532 {
533         return new InsetMathBinom(*this);
534 }
535
536
537 int InsetMathBinom::dw(int height) const
538 {
539         int w = height / 5;
540         if (w > 15)
541                 w = 15;
542         if (w < 6)
543                 w = 6;
544         return w;
545 }
546
547
548 void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
549 {
550         Dimension dim0, dim1;
551         Changer dummy =
552                 (kind_ == DBINOM) ? mi.base.changeStyle(LM_ST_DISPLAY) :
553                 (kind_ == TBINOM) ? mi.base.changeStyle(LM_ST_SCRIPT) :
554                                     mi.base.changeFrac();
555         cell(0).metrics(mi, dim0);
556         cell(1).metrics(mi, dim1);
557         dim.asc = dim0.height() + 4 + 5;
558         dim.des = dim1.height() + 4 - 5;
559         dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
560         metricsMarkers2(dim);
561 }
562
563
564 void InsetMathBinom::draw(PainterInfo & pi, int x, int y) const
565 {
566         Dimension const dim = dimension(*pi.base.bv);
567         Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
568         Dimension const & dim1 = cell(1).dimension(*pi.base.bv);
569         // define the binom brackets
570         docstring const bra = kind_ == BRACE ? from_ascii("{") :
571                 kind_ == BRACK ? from_ascii("[") : from_ascii("(");
572         docstring const ket = kind_ == BRACE ? from_ascii("}") :
573                 kind_ == BRACK ? from_ascii("]") : from_ascii(")");
574
575         int m = x + dim.width() / 2;
576         {
577                 Changer dummy =
578                         (kind_ == DBINOM) ? pi.base.changeStyle(LM_ST_DISPLAY) :
579                         (kind_ == TBINOM) ? pi.base.changeStyle(LM_ST_SCRIPT) :
580                                             pi.base.changeFrac();
581                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
582                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
583         }
584         // draw the brackets and the marker
585         mathed_draw_deco(pi, x, y - dim.ascent(), dw(dim.height()),
586                 dim.height(), bra);
587         mathed_draw_deco(pi, x + dim.width() - dw(dim.height()),
588                 y - dim.ascent(), dw(dim.height()), dim.height(), ket);
589         drawMarkers2(pi, x, y);
590 }
591
592
593 bool InsetMathBinom::extraBraces() const
594 {
595         return kind_ == CHOOSE || kind_ == BRACE || kind_ == BRACK;
596 }
597
598
599 void InsetMathBinom::write(WriteStream & os) const
600 {
601         MathEnsurer ensurer(os);
602         switch (kind_) {
603         case BINOM:
604                 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
605                 break;
606         case DBINOM:
607                 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
608                 break;
609         case TBINOM:
610                 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
611                 break;
612         case CHOOSE:
613                 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
614                 break;
615         case BRACE:
616                 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
617                 break;
618         case BRACK:
619                 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
620                 break;
621         }
622 }
623
624
625 void InsetMathBinom::normalize(NormalStream & os) const
626 {
627         os << "[binom " << cell(0) << ' ' << cell(1) << ']';
628 }
629
630
631 void InsetMathBinom::mathmlize(MathStream & os) const
632 {
633         char ldelim = ' ';
634         char rdelim = ' ';
635         switch (kind_) {
636         case BINOM:
637         case TBINOM:
638         case DBINOM:
639         case CHOOSE:
640                 ldelim = '(';
641                 rdelim = ')';
642                 break;
643         case BRACE:
644                 ldelim = '{';
645                 rdelim = '}';
646                 break;
647         case BRACK:
648                 ldelim = '[';
649                 rdelim = ']';
650                 break;
651         }
652         os << "<mo fence='true' stretchy='true' form='prefix'>" << ldelim << "</mo>"
653            << "<mfrac linethickness='0'>"
654            << cell(0) << cell(1)
655            << "</mfrac>"
656            << "<mo fence='true' stretchy='true' form='postfix'>" << rdelim << "</mo>";
657 }
658
659
660 void InsetMathBinom::htmlize(HtmlStream & os) const
661 {
662         char ldelim = ' ';
663         char rdelim = ' ';
664         switch (kind_) {
665         case BINOM:
666         case TBINOM:
667         case DBINOM:
668         case CHOOSE:
669                 ldelim = '(';
670                 rdelim = ')';
671                 break;
672         case BRACE:
673                 ldelim = '{';
674                 rdelim = '}';
675                 break;
676         case BRACK:
677                 ldelim = '[';
678                 rdelim = ']';
679                 break;
680         }
681         os << MTag("span", "class='binomdelim'") << ldelim << ETag("span") << '\n'
682            << MTag("span", "class='binom'") << '\n'
683            << MTag("span") << cell(0) << ETag("span") << '\n'
684            << MTag("span") << cell(1) << ETag("span") << '\n'
685            << ETag("span") << '\n'
686                  << MTag("span", "class='binomdelim'") << rdelim << ETag("span") << '\n';
687 }
688
689
690 void InsetMathBinom::validate(LaTeXFeatures & features) const
691 {
692         if (features.runparams().isLaTeX()) {
693                 if (kind_ == BINOM)
694                         features.require("binom");
695                 if (kind_ == DBINOM || kind_ == TBINOM)
696                         features.require("amsmath");
697         } else if (features.runparams().math_flavor == OutputParams::MathAsHTML)
698                 features.addCSSSnippet(
699                         "span.binom{display: inline-block; vertical-align: bottom; text-align:center;}\n"
700                         "span.binom span{display: block;}\n"
701                         "span.binomdelim{font-size: 2em;}");
702         InsetMathNest::validate(features);
703 }
704
705
706 } // namespace lyx