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