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