]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathFrac.cpp
Substack support for XHTML.
[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 void InsetMathFrac::maple(MapleStream & os) const
370 {
371         os << '(' << cell(0) << ")/(" << cell(1) << ')';
372 }
373
374
375 void InsetMathFrac::mathematica(MathematicaStream & os) const
376 {
377         os << '(' << cell(0) << ")/(" << cell(1) << ')';
378 }
379
380
381 void InsetMathFrac::octave(OctaveStream & os) const
382 {
383         os << '(' << cell(0) << ")/(" << cell(1) << ')';
384 }
385
386
387 void InsetMathFrac::mathmlize(MathStream & os) const
388 {
389         os << MTag("mfrac")
390            << MTag("mrow") << cell(0) << ETag("mrow")
391                  << MTag("mrow") << cell(1) << ETag("mrow")
392                  << ETag("mfrac");
393 }
394
395
396 void InsetMathFrac::htmlize(HtmlStream & os) const
397 {
398         os << MTag("span", "class='frac'")
399                  << MTag("span", "class='numer'") << cell(0) << ETag("span")
400                  << MTag("span", "class='denom'") << cell(1) << ETag("span")
401                  << ETag("span");
402 }
403
404
405 void InsetMathFrac::validate(LaTeXFeatures & features) const
406 {
407         if (kind_ == NICEFRAC || kind_ == UNITFRAC || kind_ == UNIT)
408                 features.require("units");
409         if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
410                   || kind_ == DFRAC || kind_ == TFRAC)
411                 features.require("amsmath");
412         if (features.runparams().math_flavor == OutputParams::MathAsHTML)
413                 // CSS adapted from eLyXer
414                 features.addPreambleSnippet("<style type=\"text/css\">\n"
415                         "span.frac{display: inline-block; vertical-align: middle; text-align:center;}\n"
416                         "span.numer{display: block;}\n"
417                         "span.denom{display: block; border-top: thin solid #000040;}\n"
418                         "</style>");
419         InsetMathNest::validate(features);
420 }
421
422
423 /////////////////////////////////////////////////////////////////////
424 //
425 // InsetMathBinom
426 //
427 /////////////////////////////////////////////////////////////////////
428
429
430 InsetMathBinom::InsetMathBinom(Buffer * buf, Kind kind)
431         : InsetMathFracBase(buf), kind_(kind)
432 {}
433
434
435 Inset * InsetMathBinom::clone() const
436 {
437         return new InsetMathBinom(*this);
438 }
439
440
441 int InsetMathBinom::dw(int height) const
442 {
443         int w = height / 5;
444         if (w > 15)
445                 w = 15;
446         if (w < 6)
447                 w = 6;
448         return w;
449 }
450
451
452 void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
453 {
454         Dimension dim0, dim1;
455
456         // FIXME: for an unknown reason the cells must be set directly
457         // after the StyleChanger and cannot be set after the if case
458         if (kind_ == DBINOM) {
459                 StyleChanger dummy(mi.base, LM_ST_DISPLAY);
460                 cell(0).metrics(mi, dim0);
461                 cell(1).metrics(mi, dim1);
462         } else if (kind_ == TBINOM) {
463                 StyleChanger dummy(mi.base, LM_ST_SCRIPT);
464                 cell(0).metrics(mi, dim0);
465                 cell(1).metrics(mi, dim1);
466         } else {
467                 FracChanger dummy(mi.base);
468                 cell(0).metrics(mi, dim0);
469                 cell(1).metrics(mi, dim1);
470         }
471         dim.asc = dim0.height() + 4 + 5;
472         dim.des = dim1.height() + 4 - 5;
473         dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
474         metricsMarkers2(dim);
475 }
476
477
478 void InsetMathBinom::draw(PainterInfo & pi, int x, int y) const
479 {
480         Dimension const dim = dimension(*pi.base.bv);
481         Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
482         Dimension const & dim1 = cell(1).dimension(*pi.base.bv);
483         // define the binom brackets
484         docstring const bra = kind_ == BRACE ? from_ascii("{") :
485                 kind_ == BRACK ? from_ascii("[") : from_ascii("(");
486         docstring const ket = kind_ == BRACE ? from_ascii("}") :
487                 kind_ == BRACK ? from_ascii("]") : from_ascii(")");
488
489         int m = x + dim.width() / 2;
490         // FIXME: for an unknown reason the cells must be drawn directly
491         // after the StyleChanger and cannot be drawn after the if case
492         if (kind_ == DBINOM) {
493                 StyleChanger dummy(pi.base, LM_ST_DISPLAY);
494                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
495                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
496         } else if (kind_ == TBINOM) {
497                 StyleChanger dummy(pi.base, LM_ST_SCRIPT);
498                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
499                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
500         } else {
501                 FracChanger dummy2(pi.base);
502                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
503                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
504         }
505         // draw the brackets and the marker
506         mathed_draw_deco(pi, x, y - dim.ascent(), dw(dim.height()),
507                 dim.height(), bra);
508         mathed_draw_deco(pi, x + dim.width() - dw(dim.height()),
509                 y - dim.ascent(), dw(dim.height()), dim.height(), ket);
510         drawMarkers2(pi, x, y);
511 }
512
513
514 bool InsetMathBinom::extraBraces() const
515 {
516         return kind_ == CHOOSE || kind_ == BRACE || kind_ == BRACK;
517 }
518
519
520 void InsetMathBinom::write(WriteStream & os) const
521 {
522         MathEnsurer ensurer(os);
523         switch (kind_) {
524         case BINOM:
525                 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
526                 break;
527         case DBINOM:
528                 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
529                 break;
530         case TBINOM:
531                 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
532                 break;
533         case CHOOSE:
534                 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
535                 break;
536         case BRACE:
537                 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
538                 break;
539         case BRACK:
540                 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
541                 break;
542         }
543 }
544
545
546 void InsetMathBinom::normalize(NormalStream & os) const
547 {
548         os << "[binom " << cell(0) << ' ' << cell(1) << ']';
549 }
550
551
552 void InsetMathBinom::mathmlize(MathStream & os) const
553 {
554         char ldelim = ' ';
555         char rdelim = ' ';
556         switch (kind_) {
557         case BINOM:
558         case TBINOM:
559         case DBINOM:
560         case CHOOSE:
561                 ldelim = '(';
562                 rdelim = ')';
563                 break;
564         case BRACE:
565                 ldelim = '{';
566                 rdelim = '}';
567                 break;
568         case BRACK:
569                 ldelim = '[';
570                 rdelim = ']';
571                 break;
572         }
573         os << "<mo fence='true' stretchy='true' form='prefix'>" << ldelim << "</mo>"
574            << "<mfrac linethickness='0'>"
575            << cell(0) << cell(1)
576            << "</mfrac>"
577            << "<mo fence='true' stretchy='true' form='postfix'>" << rdelim << "</mo>";
578 }
579
580
581 void InsetMathBinom::htmlize(HtmlStream & os) const
582 {
583         char ldelim = ' ';
584         char rdelim = ' ';
585         switch (kind_) {
586         case BINOM:
587         case TBINOM:
588         case DBINOM:
589         case CHOOSE:
590                 ldelim = '(';
591                 rdelim = ')';
592                 break;
593         case BRACE:
594                 ldelim = '{';
595                 rdelim = '}';
596                 break;
597         case BRACK:
598                 ldelim = '[';
599                 rdelim = ']';
600                 break;
601         }
602         os << MTag("span", "class='binomdelim'") << ldelim << ETag("span") << '\n'
603            << MTag("span", "class='binom'") << '\n'
604            << MTag("span") << cell(0) << ETag("span") << '\n'
605            << MTag("span") << cell(1) << ETag("span") << '\n'
606            << ETag("span") << '\n'
607                  << MTag("span", "class='binomdelim'") << rdelim << ETag("span") << '\n';
608 }
609
610
611 void InsetMathBinom::validate(LaTeXFeatures & features) const
612 {
613         if (features.runparams().isLaTeX()) {
614                 if (kind_ == BINOM)
615                         features.require("binom");
616                 if (kind_ == DBINOM || kind_ == TBINOM)
617                         features.require("amsmath");
618         } else if (features.runparams().math_flavor == OutputParams::MathAsHTML)
619                 features.addPreambleSnippet("<style type=\"text/css\">\n"
620                         "span.binom{display: inline-block; vertical-align: bottom; text-align:center;}\n"
621                         "span.binom span{display: block;}\n"
622                         "span.binomdelim{font-size: 2em;}\n"
623                         "</style>");
624         InsetMathNest::validate(features);
625 }
626
627
628 } // namespace lyx