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