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