]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathFrac.cpp
Fix XHTML output for fractions and fix a crash reported on the list
[lyx.git] / src / mathed / InsetMathFrac.cpp
1 /**
2  * \file InsetMathFracBase.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  * \author Uwe Stöhr
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "InsetMathFrac.h"
16
17 #include "Cursor.h"
18 #include "LaTeXFeatures.h"
19 #include "MathData.h"
20 #include "MathStream.h"
21 #include "MathSupport.h"
22 #include "MetricsInfo.h"
23 #include "TextPainter.h"
24
25 #include "frontends/Painter.h"
26
27 using namespace std;
28
29 namespace lyx {
30
31 /////////////////////////////////////////////////////////////////////
32 //
33 // InsetMathFracBase
34 //
35 /////////////////////////////////////////////////////////////////////
36
37
38 InsetMathFracBase::InsetMathFracBase(Buffer * buf, idx_type ncells)
39         : InsetMathNest(buf, ncells)
40 {}
41
42
43 bool InsetMathFracBase::idxUpDown(Cursor & cur, bool up) const
44 {
45         // If we only have one cell, target = 0, otherwise
46         // target = up ? 0 : 1, since upper cell has idx 0
47         InsetMath::idx_type target = nargs() > 1 ? !up : 0;
48         if (cur.idx() == target)
49                 return false;
50         cur.idx() = target;
51         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
52         return true;
53 }
54
55
56
57 /////////////////////////////////////////////////////////////////////
58 //
59 // InsetMathFrac
60 //
61 /////////////////////////////////////////////////////////////////////
62
63
64 InsetMathFrac::InsetMathFrac(Buffer * buf, Kind kind, InsetMath::idx_type ncells)
65         : InsetMathFracBase(buf, ncells), kind_(kind)
66 {}
67
68
69 Inset * InsetMathFrac::clone() const
70 {
71         return new InsetMathFrac(*this);
72 }
73
74
75 InsetMathFrac * InsetMathFrac::asFracInset()
76 {
77         return kind_ == ATOP ? 0 : this;
78 }
79
80
81 InsetMathFrac const * InsetMathFrac::asFracInset() const
82 {
83         return kind_ == ATOP ? 0 : this;
84 }
85
86
87 bool InsetMathFrac::idxForward(Cursor & cur) const
88 {
89         InsetMath::idx_type target = 0;
90         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
91                 if (nargs() == 3)
92                         target = 0;
93                 else if (nargs() == 2)
94                         target = 1;
95         } else
96                 return false;
97         if (cur.idx() == target)
98                 return false;
99         cur.idx() = target;
100         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
101         return true;
102 }
103
104
105 bool InsetMathFrac::idxBackward(Cursor & cur) const
106 {
107         InsetMath::idx_type target = 0;
108         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
109                 if (nargs() == 3)
110                         target = 2;
111                 else if (nargs() == 2)
112                         target = 0;
113         } else
114                 return false;
115         if (cur.idx() == target)
116                 return false;
117         cur.idx() = target;
118         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
119         return true;
120 }
121
122
123 void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
124 {
125         Dimension dim0, dim1, dim2;
126
127         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
128                 if (nargs() == 1) {
129                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
130                         cell(0).metrics(mi, dim0);
131                         dim.wid = dim0.width()+ 3;
132                         dim.asc = dim0.asc;
133                         dim.des = dim0.des;
134                 } else if (nargs() == 2) {
135                         cell(0).metrics(mi, dim0);
136                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
137                         cell(1).metrics(mi, dim1);
138                         dim.wid = dim0.width() + dim1.wid + 5;
139                         dim.asc = max(dim0.asc, dim1.asc);
140                         dim.des = max(dim0.des, dim1.des);
141                 } else {
142                         cell(2).metrics(mi, dim2);
143                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
144                         FracChanger dummy(mi.base);
145                         cell(0).metrics(mi, dim0);
146                         cell(1).metrics(mi, dim1);
147                         dim.wid = dim0.width() + dim1.wid + dim2.wid + 10;
148                         dim.asc = max(dim2.asc, dim0.height() + 5);
149                         dim.des = max(dim2.des, dim1.height() - 5);
150                 }
151         } else {
152                 // general cell metrics used for \frac
153                 FracChanger dummy(mi.base);
154                 cell(0).metrics(mi, dim0);
155                 cell(1).metrics(mi, dim1);
156                 if (nargs() == 3)
157                         cell(2).metrics(mi, dim2);
158                 // metrics for special fraction types
159                 if (kind_ == NICEFRAC) {
160                         dim.wid = dim0.width() + dim1.wid + 5;
161                         dim.asc = dim0.height() + 5;
162                         dim.des = dim1.height() - 5;
163                 } else if (kind_ == UNITFRAC) {
164                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
165                         dim.wid = dim0.width() + dim1.wid + 5;
166                         dim.asc = dim0.height() + 5;
167                         dim.des = dim1.height() - 5;
168                 } else {
169                         if (kind_ == CFRAC || kind_ == CFRACLEFT
170                                   || kind_ == CFRACRIGHT || kind_ == DFRAC) {
171                                 // \cfrac and \dfrac are always in display size
172                                 StyleChanger dummy2(mi.base, LM_ST_DISPLAY);
173                                 cell(0).metrics(mi, dim0);
174                                 cell(1).metrics(mi, dim1);
175                         } else if (kind_ == TFRAC) {
176                                 // tfrac is in always in text size
177                                 StyleChanger dummy2(mi.base, LM_ST_SCRIPT);
178                                 cell(0).metrics(mi, dim0);
179                                 cell(1).metrics(mi, dim1);
180                         }
181                         dim.wid = max(dim0.wid, dim1.wid) + 2;
182                         dim.asc = dim0.height() + 2 + 5;
183                         dim.des = dim1.height() + 2 - 5;
184                 }
185         }
186         metricsMarkers(dim);
187 }
188
189
190 void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
191 {
192         setPosCache(pi, x, y);
193         Dimension const dim = dimension(*pi.base.bv);
194         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
195         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
196                 if (nargs() == 1) {
197                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
198                         cell(0).draw(pi, x + 1, y);
199                 } else if (nargs() == 2) {
200                         cell(0).draw(pi, x + 1, y);
201                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
202                         cell(1).draw(pi, x + dim0.width() + 5, y);
203                 } else {
204                         cell(2).draw(pi, x + 1, y);
205                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
206                         FracChanger dummy(pi.base);
207                         Dimension const dim1 = cell(1).dimension(*pi.base.bv);
208                         Dimension const dim2 = cell(2).dimension(*pi.base.bv);
209                         int xx = x + dim2.wid + 5;
210                         cell(0).draw(pi, xx + 2, 
211                                          y - dim0.des - 5);
212                         cell(1).draw(pi, xx  + dim0.width() + 5, 
213                                          y + dim1.asc / 2);
214                 }
215         } else {
216                 FracChanger dummy(pi.base);
217                 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
218                 int m = x + dim.wid / 2;
219                 if (kind_ == NICEFRAC) {
220                         cell(0).draw(pi, x + 2,
221                                         y - dim0.des - 5);
222                         cell(1).draw(pi, x + dim0.width() + 5,
223                                         y + dim1.asc / 2);
224                 } else if (kind_ == UNITFRAC) {
225                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
226                         cell(0).draw(pi, x + 2, y - dim0.des - 5);
227                         cell(1).draw(pi, x + dim0.width() + 5, y + dim1.asc / 2);
228                 } else if (kind_ == FRAC || kind_ == ATOP || kind_ == OVER) {
229                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
230                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
231                 } else if (kind_ == TFRAC) {
232                         // tfrac is in always in text size
233                         StyleChanger dummy2(pi.base, LM_ST_SCRIPT);
234                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
235                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
236                 } else {
237                         // \cfrac and \dfrac are always in display size
238                         StyleChanger dummy2(pi.base, LM_ST_DISPLAY);
239                         if (kind_ == CFRAC || kind_ == DFRAC)
240                                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
241                         else if (kind_ == CFRACLEFT)
242                                 cell(0).draw(pi, x + 2, y - dim0.des - 2 - 5);
243                         else if (kind_ == CFRACRIGHT)
244                                 cell(0).draw(pi, x + dim.wid - dim0.wid - 2,
245                                         y - dim0.des - 2 - 5);
246                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
247                 }
248         }
249         if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
250                 // Diag line:
251                 int xx = x;
252                 if (nargs() == 3)
253                         xx += cell(2).dimension(*pi.base.bv).wid + 5;
254                 pi.pain.line(xx + dim0.wid,
255                                 y + dim.des - 2,
256                                 xx + dim0.wid + 5,
257                                 y - dim.asc + 2, pi.base.font.color());
258         }
259         if (kind_ == FRAC || kind_ == CFRAC || kind_ == CFRACLEFT
260                 || kind_ == CFRACRIGHT || kind_ == DFRAC
261                 || kind_ == TFRAC || kind_ == OVER)
262                 pi.pain.line(x + 1, y - 5,
263                                 x + dim.wid - 2, y - 5, pi.base.font.color());
264         drawMarkers(pi, x, y);
265 }
266
267
268 void InsetMathFrac::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
269 {
270         Dimension dim0, dim1;
271         cell(0).metricsT(mi, dim0);
272         cell(1).metricsT(mi, dim1);
273         dim.wid = max(dim0.width(), dim1.wid);
274         dim.asc = dim0.height() + 1;
275         dim.des = dim1.height();
276 }
277
278
279 void InsetMathFrac::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
280 {
281         // FIXME: BROKEN!
282         /*
283         Dimension dim;
284         int m = x + dim.width() / 2;
285         cell(0).drawT(pain, m - dim0.width() / 2, y - dim0.des - 1);
286         cell(1).drawT(pain, m - dim1.wid / 2, y + dim1.asc);
287         // ASCII art: ignore niceties
288         if (kind_ == FRAC || kind_ == OVER || kind_ == NICEFRAC || kind_ == UNITFRAC)
289                 pain.horizontalLine(x, y, dim.width());
290         */
291 }
292
293
294 void InsetMathFrac::write(WriteStream & os) const
295 {
296         MathEnsurer ensurer(os);
297         switch (kind_) {
298         case ATOP:
299                 // \\atop is only for compatibility, \\binom is the
300                 // LaTeX2e successor
301                 os << '{' << cell(0) << "\\atop " << cell(1) << '}';
302                 break;
303         case OVER:
304                 // \\over is only for compatibility, normalize this to \\frac
305                 os << "\\frac{" << cell(0) << "}{" << cell(1) << '}';
306                 break;
307         case FRAC:
308         case DFRAC:
309         case TFRAC:
310         case NICEFRAC:
311         case CFRAC:
312         case UNITFRAC:
313                 if (nargs() == 2)
314                         InsetMathNest::write(os);
315                 else
316                         os << "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
317                 break;
318         case UNIT:
319                 if (nargs() == 2)
320                         os << "\\unit[" << cell(0) << "]{" << cell(1) << '}';
321                 else
322                         os << "\\unit{" << cell(0) << '}';
323                 break;
324         case CFRACLEFT:
325                 os << "\\cfrac[l]{" << cell(0) << "}{" << cell(1) << '}';
326                 break;
327         case CFRACRIGHT:
328                 os << "\\cfrac[r]{" << cell(0) << "}{" << cell(1) << '}';
329                 break;
330         }
331 }
332
333
334 docstring InsetMathFrac::name() const
335 {
336         switch (kind_) {
337         case FRAC:
338                 return from_ascii("frac");
339         case CFRAC:
340         case CFRACLEFT:
341         case CFRACRIGHT:
342                 return from_ascii("cfrac");
343         case DFRAC:
344                 return from_ascii("dfrac");
345         case TFRAC:
346                 return from_ascii("tfrac");
347         case OVER:
348                 return from_ascii("over");
349         case NICEFRAC:
350                 return from_ascii("nicefrac");
351         case UNITFRAC:
352                 return from_ascii("unitfrac");
353         case UNIT:
354                 return from_ascii("unit");
355         case ATOP:
356                 return from_ascii("atop");
357         }
358         // shut up stupid compiler
359         return docstring();
360 }
361
362
363 bool InsetMathFrac::extraBraces() const
364 {
365         return kind_ == ATOP || kind_ == OVER;
366 }
367
368
369 // FIXME This will crash on unitone and is wrong in other cases.
370 void InsetMathFrac::maple(MapleStream & os) const
371 {
372         os << '(' << cell(0) << ")/(" << cell(1) << ')';
373 }
374
375
376 // FIXME This will crash on unitone and is wrong in other cases.
377 void InsetMathFrac::mathematica(MathematicaStream & os) const
378 {
379         os << '(' << cell(0) << ")/(" << cell(1) << ')';
380 }
381
382
383 // FIXME This will crash on unitone and is wrong in other cases.
384 void InsetMathFrac::octave(OctaveStream & os) const
385 {
386         os << '(' << cell(0) << ")/(" << cell(1) << ')';
387 }
388
389
390 void InsetMathFrac::mathmlize(MathStream & os) const
391 {
392         switch (kind_) {
393         case ATOP:
394                 os << MTag("mfrac", "linethickeness='0'")
395                    << MTag("mrow") << cell(0) << ETag("mrow")
396                          << MTag("mrow") << cell(1) << ETag("mrow")
397                          << ETag("mfrac");
398                 break;
399
400         // we do not presently distinguish these
401         case OVER:
402         case FRAC:
403         case DFRAC:
404         case TFRAC:
405         case CFRAC:
406         case CFRACLEFT:
407         case CFRACRIGHT:
408                 os << MTag("mfrac")
409                    << MTag("mrow") << cell(0) << ETag("mrow")
410                          << MTag("mrow") << cell(1) << ETag("mrow")
411                          << ETag("mfrac");
412                 break;
413
414         case NICEFRAC:
415                 os << MTag("mfrac", "bevelled='true'")
416                    << MTag("mrow") << cell(0) << ETag("mrow")
417                          << MTag("mrow") << cell(1) << ETag("mrow")
418                          << ETag("mfrac");
419                 break;
420
421         case UNITFRAC:
422                 if (nargs() == 3)
423                         os << cell(2);
424                 os << MTag("mfrac", "bevelled='true'")
425                    << MTag("mrow") << cell(0) << ETag("mrow")
426                          << MTag("mrow") << cell(1) << ETag("mrow")
427                          << ETag("mfrac");
428                 break;
429
430         case UNIT:
431                 // FIXME This is not right, because we still output mi, etc,
432                 // when we output the cell. So we need to prevent that somehow.
433                 if (nargs() == 2)
434                         os << cell(0) 
435                            << MTag("mstyle mathvariant='normal'") 
436                            << cell(1) 
437                            << ETag("mstyle");
438                 else
439                         os << MTag("mstyle mathvariant='normal'") 
440                            << cell(0)
441                            << ETag("mstyle");
442         }
443 }
444
445
446 void InsetMathFrac::htmlize(HtmlStream & os) const
447 {
448         switch (kind_) {
449         case ATOP:
450                 os << MTag("span", "class='frac'")
451                          << MTag("span", "class='numer'") << cell(0) << ETag("span")
452                          << MTag("span", "class='numer'") << cell(1) << ETag("span")
453                          << ETag("span");
454                 break;
455
456         // we do not presently distinguish these
457         case OVER:
458         case FRAC:
459         case DFRAC:
460         case TFRAC:
461         case CFRAC:
462         case CFRACLEFT:
463         case CFRACRIGHT:
464                 os << MTag("span", "class='frac'")
465                          << MTag("span", "class='numer'") << cell(0) << ETag("span")
466                          << MTag("span", "class='denom'") << cell(1) << ETag("span")
467                          << ETag("span");
468                 break;
469
470         case NICEFRAC:
471                 os << cell(0) << '/' << cell(1);
472                 break;
473
474         case UNITFRAC:
475                 if (nargs() == 3)
476                         os << cell(2) << ' ';
477                 os << cell(0) << '/' << cell(1);
478                 break;
479
480         case UNIT:
481                 // FIXME This is not right, because we still output i, etc,
482                 // when we output the cell. So we need to prevent that somehow.
483                 if (nargs() == 2)
484                         os << cell(0) 
485                            << MTag("span") 
486                            << cell(1) 
487                            << ETag("span");
488                 else
489                         os << MTag("span") 
490                            << cell(0)
491                            << ETag("span");
492         }
493 }
494
495
496 void InsetMathFrac::validate(LaTeXFeatures & features) const
497 {
498         if (kind_ == NICEFRAC || kind_ == UNITFRAC || kind_ == UNIT)
499                 features.require("units");
500         if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
501                   || kind_ == DFRAC || kind_ == TFRAC)
502                 features.require("amsmath");
503         if (features.runparams().math_flavor == OutputParams::MathAsHTML)
504                 // CSS adapted from eLyXer
505                 features.addPreambleSnippet("<style type=\"text/css\">\n"
506                         "span.frac{display: inline-block; vertical-align: middle; text-align:center;}\n"
507                         "span.numer{display: block;}\n"
508                         "span.denom{display: block; border-top: thin solid #000040;}\n"
509                         "</style>");
510         InsetMathNest::validate(features);
511 }
512
513
514 /////////////////////////////////////////////////////////////////////
515 //
516 // InsetMathBinom
517 //
518 /////////////////////////////////////////////////////////////////////
519
520
521 InsetMathBinom::InsetMathBinom(Buffer * buf, Kind kind)
522         : InsetMathFracBase(buf), kind_(kind)
523 {}
524
525
526 Inset * InsetMathBinom::clone() const
527 {
528         return new InsetMathBinom(*this);
529 }
530
531
532 int InsetMathBinom::dw(int height) const
533 {
534         int w = height / 5;
535         if (w > 15)
536                 w = 15;
537         if (w < 6)
538                 w = 6;
539         return w;
540 }
541
542
543 void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
544 {
545         Dimension dim0, dim1;
546
547         // FIXME: for an unknown reason the cells must be set directly
548         // after the StyleChanger and cannot be set after the if case
549         if (kind_ == DBINOM) {
550                 StyleChanger dummy(mi.base, LM_ST_DISPLAY);
551                 cell(0).metrics(mi, dim0);
552                 cell(1).metrics(mi, dim1);
553         } else if (kind_ == TBINOM) {
554                 StyleChanger dummy(mi.base, LM_ST_SCRIPT);
555                 cell(0).metrics(mi, dim0);
556                 cell(1).metrics(mi, dim1);
557         } else {
558                 FracChanger dummy(mi.base);
559                 cell(0).metrics(mi, dim0);
560                 cell(1).metrics(mi, dim1);
561         }
562         dim.asc = dim0.height() + 4 + 5;
563         dim.des = dim1.height() + 4 - 5;
564         dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
565         metricsMarkers2(dim);
566 }
567
568
569 void InsetMathBinom::draw(PainterInfo & pi, int x, int y) const
570 {
571         Dimension const dim = dimension(*pi.base.bv);
572         Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
573         Dimension const & dim1 = cell(1).dimension(*pi.base.bv);
574         // define the binom brackets
575         docstring const bra = kind_ == BRACE ? from_ascii("{") :
576                 kind_ == BRACK ? from_ascii("[") : from_ascii("(");
577         docstring const ket = kind_ == BRACE ? from_ascii("}") :
578                 kind_ == BRACK ? from_ascii("]") : from_ascii(")");
579
580         int m = x + dim.width() / 2;
581         // FIXME: for an unknown reason the cells must be drawn directly
582         // after the StyleChanger and cannot be drawn after the if case
583         if (kind_ == DBINOM) {
584                 StyleChanger dummy(pi.base, LM_ST_DISPLAY);
585                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
586                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
587         } else if (kind_ == TBINOM) {
588                 StyleChanger dummy(pi.base, LM_ST_SCRIPT);
589                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
590                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
591         } else {
592                 FracChanger dummy2(pi.base);
593                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
594                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
595         }
596         // draw the brackets and the marker
597         mathed_draw_deco(pi, x, y - dim.ascent(), dw(dim.height()),
598                 dim.height(), bra);
599         mathed_draw_deco(pi, x + dim.width() - dw(dim.height()),
600                 y - dim.ascent(), dw(dim.height()), dim.height(), ket);
601         drawMarkers2(pi, x, y);
602 }
603
604
605 bool InsetMathBinom::extraBraces() const
606 {
607         return kind_ == CHOOSE || kind_ == BRACE || kind_ == BRACK;
608 }
609
610
611 void InsetMathBinom::write(WriteStream & os) const
612 {
613         MathEnsurer ensurer(os);
614         switch (kind_) {
615         case BINOM:
616                 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
617                 break;
618         case DBINOM:
619                 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
620                 break;
621         case TBINOM:
622                 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
623                 break;
624         case CHOOSE:
625                 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
626                 break;
627         case BRACE:
628                 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
629                 break;
630         case BRACK:
631                 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
632                 break;
633         }
634 }
635
636
637 void InsetMathBinom::normalize(NormalStream & os) const
638 {
639         os << "[binom " << cell(0) << ' ' << cell(1) << ']';
640 }
641
642
643 void InsetMathBinom::mathmlize(MathStream & os) const
644 {
645         char ldelim = ' ';
646         char rdelim = ' ';
647         switch (kind_) {
648         case BINOM:
649         case TBINOM:
650         case DBINOM:
651         case CHOOSE:
652                 ldelim = '(';
653                 rdelim = ')';
654                 break;
655         case BRACE:
656                 ldelim = '{';
657                 rdelim = '}';
658                 break;
659         case BRACK:
660                 ldelim = '[';
661                 rdelim = ']';
662                 break;
663         }
664         os << "<mo fence='true' stretchy='true' form='prefix'>" << ldelim << "</mo>"
665            << "<mfrac linethickness='0'>"
666            << cell(0) << cell(1)
667            << "</mfrac>"
668            << "<mo fence='true' stretchy='true' form='postfix'>" << rdelim << "</mo>";
669 }
670
671
672 void InsetMathBinom::htmlize(HtmlStream & os) const
673 {
674         char ldelim = ' ';
675         char rdelim = ' ';
676         switch (kind_) {
677         case BINOM:
678         case TBINOM:
679         case DBINOM:
680         case CHOOSE:
681                 ldelim = '(';
682                 rdelim = ')';
683                 break;
684         case BRACE:
685                 ldelim = '{';
686                 rdelim = '}';
687                 break;
688         case BRACK:
689                 ldelim = '[';
690                 rdelim = ']';
691                 break;
692         }
693         os << MTag("span", "class='binomdelim'") << ldelim << ETag("span") << '\n'
694            << MTag("span", "class='binom'") << '\n'
695            << MTag("span") << cell(0) << ETag("span") << '\n'
696            << MTag("span") << cell(1) << ETag("span") << '\n'
697            << ETag("span") << '\n'
698                  << MTag("span", "class='binomdelim'") << rdelim << ETag("span") << '\n';
699 }
700
701
702 void InsetMathBinom::validate(LaTeXFeatures & features) const
703 {
704         if (features.runparams().isLaTeX()) {
705                 if (kind_ == BINOM)
706                         features.require("binom");
707                 if (kind_ == DBINOM || kind_ == TBINOM)
708                         features.require("amsmath");
709         } else if (features.runparams().math_flavor == OutputParams::MathAsHTML)
710                 features.addPreambleSnippet("<style type=\"text/css\">\n"
711                         "span.binom{display: inline-block; vertical-align: bottom; text-align:center;}\n"
712                         "span.binom span{display: block;}\n"
713                         "span.binomdelim{font-size: 2em;}\n"
714                         "</style>");
715         InsetMathNest::validate(features);
716 }
717
718
719 } // namespace lyx