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