]> git.lyx.org Git - features.git/blob - src/mathed/InsetMathFrac.cpp
Introduce a return value for mathmlize(). We will need this to be able
[features.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 "MathExtern.h"
21 #include "MathStream.h"
22 #include "MathSupport.h"
23 #include "MetricsInfo.h"
24 #include "TextPainter.h"
25
26 #include "frontends/Painter.h"
27
28 using namespace std;
29
30 namespace lyx {
31
32 /////////////////////////////////////////////////////////////////////
33 //
34 // InsetMathFracBase
35 //
36 /////////////////////////////////////////////////////////////////////
37
38
39 InsetMathFracBase::InsetMathFracBase(Buffer * buf, idx_type ncells)
40         : InsetMathNest(buf, ncells)
41 {}
42
43
44 bool InsetMathFracBase::idxUpDown(Cursor & cur, bool up) const
45 {
46         // If we only have one cell, target = 0, otherwise
47         // target = up ? 0 : 1, since upper cell has idx 0
48         InsetMath::idx_type target = nargs() > 1 ? !up : 0;
49         if (cur.idx() == target)
50                 return false;
51         cur.idx() = target;
52         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
53         return true;
54 }
55
56
57
58 /////////////////////////////////////////////////////////////////////
59 //
60 // InsetMathFrac
61 //
62 /////////////////////////////////////////////////////////////////////
63
64
65 InsetMathFrac::InsetMathFrac(Buffer * buf, Kind kind, InsetMath::idx_type ncells)
66         : InsetMathFracBase(buf, ncells), kind_(kind)
67 {}
68
69
70 Inset * InsetMathFrac::clone() const
71 {
72         return new InsetMathFrac(*this);
73 }
74
75
76 InsetMathFrac * InsetMathFrac::asFracInset()
77 {
78         return kind_ == ATOP ? 0 : this;
79 }
80
81
82 InsetMathFrac const * InsetMathFrac::asFracInset() const
83 {
84         return kind_ == ATOP ? 0 : this;
85 }
86
87
88 bool InsetMathFrac::idxForward(Cursor & cur) const
89 {
90         InsetMath::idx_type target = 0;
91         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
92                 if (nargs() == 3)
93                         target = 0;
94                 else if (nargs() == 2)
95                         target = 1;
96         } else
97                 return false;
98         if (cur.idx() == target)
99                 return false;
100         cur.idx() = target;
101         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
102         return true;
103 }
104
105
106 bool InsetMathFrac::idxBackward(Cursor & cur) const
107 {
108         InsetMath::idx_type target = 0;
109         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
110                 if (nargs() == 3)
111                         target = 2;
112                 else if (nargs() == 2)
113                         target = 0;
114         } else
115                 return false;
116         if (cur.idx() == target)
117                 return false;
118         cur.idx() = target;
119         cur.pos() = cell(target).x2pos(&cur.bv(), cur.x_target());
120         return true;
121 }
122
123
124 void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
125 {
126         Dimension dim0, dim1, dim2;
127
128         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
129                 if (nargs() == 1) {
130                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
131                         cell(0).metrics(mi, dim0);
132                         dim.wid = dim0.width()+ 3;
133                         dim.asc = dim0.asc;
134                         dim.des = dim0.des;
135                 } else if (nargs() == 2) {
136                         cell(0).metrics(mi, dim0);
137                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
138                         cell(1).metrics(mi, dim1);
139                         dim.wid = dim0.width() + dim1.wid + 5;
140                         dim.asc = max(dim0.asc, dim1.asc);
141                         dim.des = max(dim0.des, dim1.des);
142                 } else {
143                         cell(2).metrics(mi, dim2);
144                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
145                         FracChanger dummy(mi.base);
146                         cell(0).metrics(mi, dim0);
147                         cell(1).metrics(mi, dim1);
148                         dim.wid = dim0.width() + dim1.wid + dim2.wid + 10;
149                         dim.asc = max(dim2.asc, dim0.height() + 5);
150                         dim.des = max(dim2.des, dim1.height() - 5);
151                 }
152         } else {
153                 // general cell metrics used for \frac
154                 FracChanger dummy(mi.base);
155                 cell(0).metrics(mi, dim0);
156                 cell(1).metrics(mi, dim1);
157                 if (nargs() == 3)
158                         cell(2).metrics(mi, dim2);
159                 // metrics for special fraction types
160                 if (kind_ == NICEFRAC) {
161                         dim.wid = dim0.width() + dim1.wid + 5;
162                         dim.asc = dim0.height() + 5;
163                         dim.des = dim1.height() - 5;
164                 } else if (kind_ == UNITFRAC) {
165                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
166                         dim.wid = dim0.width() + dim1.wid + 5;
167                         dim.asc = dim0.height() + 5;
168                         dim.des = dim1.height() - 5;
169                 } else {
170                         if (kind_ == CFRAC || kind_ == CFRACLEFT
171                         || kind_ == CFRACRIGHT || kind_ == DFRAC) {
172                                 // \cfrac and \dfrac are always in display size
173                                 StyleChanger dummy2(mi.base, LM_ST_DISPLAY);
174                                 cell(0).metrics(mi, dim0);
175                                 cell(1).metrics(mi, dim1);
176                         } else if (kind_ == TFRAC) {
177                                 // tfrac is in always in text size
178                                 StyleChanger dummy2(mi.base, LM_ST_SCRIPT);
179                                 cell(0).metrics(mi, dim0);
180                                 cell(1).metrics(mi, dim1);
181                         }
182                         dim.wid = max(dim0.wid, dim1.wid) + 2;
183                         dim.asc = dim0.height() + 2 + 5;
184                         dim.des = dim1.height() + 2 - 5;
185                 }
186         }
187         metricsMarkers(dim);
188 }
189
190
191 void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
192 {
193         setPosCache(pi, x, y);
194         Dimension const dim = dimension(*pi.base.bv);
195         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
196         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
197                 if (nargs() == 1) {
198                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
199                         cell(0).draw(pi, x + 1, y);
200                 } else if (nargs() == 2) {
201                         cell(0).draw(pi, x + 1, y);
202                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
203                         cell(1).draw(pi, x + dim0.width() + 5, y);
204                 } else {
205                         cell(2).draw(pi, x + 1, y);
206                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
207                         FracChanger dummy(pi.base);
208                         Dimension const dim1 = cell(1).dimension(*pi.base.bv);
209                         Dimension const dim2 = cell(2).dimension(*pi.base.bv);
210                         int xx = x + dim2.wid + 5;
211                         cell(0).draw(pi, xx + 2, 
212                                          y - dim0.des - 5);
213                         cell(1).draw(pi, xx  + dim0.width() + 5, 
214                                          y + dim1.asc / 2);
215                 }
216         } else {
217                 FracChanger dummy(pi.base);
218                 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
219                 int m = x + dim.wid / 2;
220                 if (kind_ == NICEFRAC) {
221                         cell(0).draw(pi, x + 2,
222                                         y - dim0.des - 5);
223                         cell(1).draw(pi, x + dim0.width() + 5,
224                                         y + dim1.asc / 2);
225                 } else if (kind_ == UNITFRAC) {
226                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
227                         cell(0).draw(pi, x + 2, y - dim0.des - 5);
228                         cell(1).draw(pi, x + dim0.width() + 5, y + dim1.asc / 2);
229                 } else if (kind_ == FRAC || kind_ == ATOP || kind_ == OVER) {
230                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
231                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
232                 } else if (kind_ == TFRAC) {
233                         // tfrac is in always in text size
234                         StyleChanger dummy2(pi.base, LM_ST_SCRIPT);
235                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
236                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
237                 } else {
238                         // \cfrac and \dfrac are always in display size
239                         StyleChanger dummy2(pi.base, LM_ST_DISPLAY);
240                         if (kind_ == CFRAC || kind_ == DFRAC)
241                                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
242                         else if (kind_ == CFRACLEFT)
243                                 cell(0).draw(pi, x + 2, y - dim0.des - 2 - 5);
244                         else if (kind_ == CFRACRIGHT)
245                                 cell(0).draw(pi, x + dim.wid - dim0.wid - 2,
246                                         y - dim0.des - 2 - 5);
247                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
248                 }
249         }
250         if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
251                 // Diag line:
252                 int xx = x;
253                 if (nargs() == 3)
254                         xx += cell(2).dimension(*pi.base.bv).wid + 5;
255                 pi.pain.line(xx + dim0.wid,
256                                 y + dim.des - 2,
257                                 xx + dim0.wid + 5,
258                                 y - dim.asc + 2, Color_math);
259         }
260         if (kind_ == FRAC || kind_ == CFRAC || kind_ == CFRACLEFT
261                 || kind_ == CFRACRIGHT || kind_ == DFRAC
262                 || kind_ == TFRAC || kind_ == OVER)
263                 pi.pain.line(x + 1, y - 5,
264                                 x + dim.wid - 2, y - 5, Color_math);
265         drawMarkers(pi, x, y);
266 }
267
268
269 void InsetMathFrac::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
270 {
271         Dimension dim0, dim1;
272         cell(0).metricsT(mi, dim0);
273         cell(1).metricsT(mi, dim1);
274         dim.wid = max(dim0.width(), dim1.wid);
275         dim.asc = dim0.height() + 1;
276         dim.des = dim1.height();
277 }
278
279
280 void InsetMathFrac::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
281 {
282         // FIXME: BROKEN!
283         /*
284         Dimension dim;
285         int m = x + dim.width() / 2;
286         cell(0).drawT(pain, m - dim0.width() / 2, y - dim0.des - 1);
287         cell(1).drawT(pain, m - dim1.wid / 2, y + dim1.asc);
288         // ASCII art: ignore niceties
289         if (kind_ == FRAC || kind_ == OVER || kind_ == NICEFRAC || kind_ == UNITFRAC)
290                 pain.horizontalLine(x, y, dim.width());
291         */
292 }
293
294
295 void InsetMathFrac::write(WriteStream & os) const
296 {
297         MathEnsurer ensurer(os);
298         switch (kind_) {
299         case ATOP:
300                 // \\atop is only for compatibility, \\binom is the
301                 // LaTeX2e successor
302                 os << '{' << cell(0) << "\\atop " << cell(1) << '}';
303                 break;
304         case OVER:
305                 // \\over is only for compatibility, normalize this to \\frac
306                 os << "\\frac{" << cell(0) << "}{" << cell(1) << '}';
307                 break;
308         case FRAC:
309         case DFRAC:
310         case TFRAC:
311         case NICEFRAC:
312         case CFRAC:
313         case UNITFRAC:
314                 if (nargs() == 2)
315                         InsetMathNest::write(os);
316                 else
317                         os << "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
318                 break;
319         case UNIT:
320                 if (nargs() == 2)
321                         os << "\\unit[" << cell(0) << "]{" << cell(1) << '}';
322                 else
323                         os << "\\unit{" << cell(0) << '}';
324                 break;
325         case CFRACLEFT:
326                 os << "\\cfrac[l]{" << cell(0) << "}{" << cell(1) << '}';
327                 break;
328         case CFRACRIGHT:
329                 os << "\\cfrac[r]{" << cell(0) << "}{" << cell(1) << '}';
330                 break;
331         }
332 }
333
334
335 docstring InsetMathFrac::name() const
336 {
337         switch (kind_) {
338         case FRAC:
339                 return from_ascii("frac");
340         case CFRAC:
341         case CFRACLEFT:
342         case CFRACRIGHT:
343                 return from_ascii("cfrac");
344         case DFRAC:
345                 return from_ascii("dfrac");
346         case TFRAC:
347                 return from_ascii("tfrac");
348         case OVER:
349                 return from_ascii("over");
350         case NICEFRAC:
351                 return from_ascii("nicefrac");
352         case UNITFRAC:
353                 return from_ascii("unitfrac");
354         case UNIT:
355                 return from_ascii("unit");
356         case ATOP:
357                 return from_ascii("atop");
358         }
359         // shut up stupid compiler
360         return docstring();
361 }
362
363
364 bool InsetMathFrac::extraBraces() const
365 {
366         return kind_ == ATOP || kind_ == OVER;
367 }
368
369
370 void InsetMathFrac::maple(MapleStream & os) const
371 {
372         os << '(' << cell(0) << ")/(" << cell(1) << ')';
373 }
374
375
376 void InsetMathFrac::mathematica(MathematicaStream & os) const
377 {
378         os << '(' << cell(0) << ")/(" << cell(1) << ')';
379 }
380
381
382 void InsetMathFrac::octave(OctaveStream & os) const
383 {
384         os << '(' << cell(0) << ")/(" << cell(1) << ')';
385 }
386
387
388 docstring InsetMathFrac::mathmlize(MathStream & os) const
389 {
390         os << MTag("mfrac") << MTag("mrow");
391         docstring rv = lyx::mathmlize(cell(0), os);
392         os << ETag("mrow") << MTag("mrow");
393         rv += lyx::mathmlize(cell(1), os);
394         os << ETag("mrow") << ETag("mfrac");
395         return rv;
396 }
397
398
399 void InsetMathFrac::validate(LaTeXFeatures & features) const
400 {
401         if (kind_ == NICEFRAC || kind_ == UNITFRAC || kind_ == UNIT)
402                 features.require("units");
403         if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
404                 || kind_ == DFRAC || kind_ == TFRAC)
405                 features.require("amsmath");
406         InsetMathNest::validate(features);
407 }
408
409
410 /////////////////////////////////////////////////////////////////////
411 //
412 // InsetMathBinom
413 //
414 /////////////////////////////////////////////////////////////////////
415
416
417 InsetMathBinom::InsetMathBinom(Buffer * buf, Kind kind)
418         : InsetMathFracBase(buf), kind_(kind)
419 {}
420
421
422 Inset * InsetMathBinom::clone() const
423 {
424         return new InsetMathBinom(*this);
425 }
426
427
428 int InsetMathBinom::dw(int height) const
429 {
430         int w = height / 5;
431         if (w > 15)
432                 w = 15;
433         if (w < 6)
434                 w = 6;
435         return w;
436 }
437
438
439 void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
440 {
441         Dimension dim0, dim1;
442
443         // FIXME: for an unknown reason the cells must be set directly
444         // after the StyleChanger and cannot be set after the if case
445         if (kind_ == DBINOM) {
446                 StyleChanger dummy(mi.base, LM_ST_DISPLAY);
447                 cell(0).metrics(mi, dim0);
448                 cell(1).metrics(mi, dim1);
449         } else if (kind_ == TBINOM) {
450                 StyleChanger dummy(mi.base, LM_ST_SCRIPT);
451                 cell(0).metrics(mi, dim0);
452                 cell(1).metrics(mi, dim1);
453         } else {
454                 FracChanger dummy(mi.base);
455                 cell(0).metrics(mi, dim0);
456                 cell(1).metrics(mi, dim1);
457         }
458         dim.asc = dim0.height() + 4 + 5;
459         dim.des = dim1.height() + 4 - 5;
460         dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
461         metricsMarkers2(dim);
462 }
463
464
465 void InsetMathBinom::draw(PainterInfo & pi, int x, int y) const
466 {
467         Dimension const dim = dimension(*pi.base.bv);
468         Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
469         Dimension const & dim1 = cell(1).dimension(*pi.base.bv);
470         // define the binom brackets
471         docstring const bra = kind_ == BRACE ? from_ascii("{") :
472                 kind_ == BRACK ? from_ascii("[") : from_ascii("(");
473         docstring const ket = kind_ == BRACE ? from_ascii("}") :
474                 kind_ == BRACK ? from_ascii("]") : from_ascii(")");
475
476         int m = x + dim.width() / 2;
477         // FIXME: for an unknown reason the cells must be drawn directly
478         // after the StyleChanger and cannot be drawn after the if case
479         if (kind_ == DBINOM) {
480                 StyleChanger dummy(pi.base, LM_ST_DISPLAY);
481                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
482                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
483         } else if (kind_ == TBINOM) {
484                 StyleChanger dummy(pi.base, LM_ST_SCRIPT);
485                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
486                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
487         } else {
488                 FracChanger dummy2(pi.base);
489                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
490                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
491         }
492         // draw the brackets and the marker
493         mathed_draw_deco(pi, x, y - dim.ascent(), dw(dim.height()),
494                 dim.height(), bra);
495         mathed_draw_deco(pi, x + dim.width() - dw(dim.height()),
496                 y - dim.ascent(), dw(dim.height()), dim.height(), ket);
497         drawMarkers2(pi, x, y);
498 }
499
500
501 bool InsetMathBinom::extraBraces() const
502 {
503         return kind_ == CHOOSE || kind_ == BRACE || kind_ == BRACK;
504 }
505
506
507 void InsetMathBinom::write(WriteStream & os) const
508 {
509         MathEnsurer ensurer(os);
510         switch (kind_) {
511         case BINOM:
512                 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
513                 break;
514         case DBINOM:
515                 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
516                 break;
517         case TBINOM:
518                 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
519                 break;
520         case CHOOSE:
521                 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
522                 break;
523         case BRACE:
524                 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
525                 break;
526         case BRACK:
527                 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
528                 break;
529         }
530 }
531
532
533 void InsetMathBinom::normalize(NormalStream & os) const
534 {
535         os << "[binom " << cell(0) << ' ' << cell(1) << ']';
536 }
537
538
539 docstring InsetMathBinom::mathmlize(MathStream & os) const
540 {
541         // FIXME This all needs fixing
542         switch (kind_) {
543         case BINOM:
544                 os << MTag("mbinom") << cell(0) << cell(1) << ETag("mbinom");
545                 break;
546         case TBINOM:
547                 os << MTag("mtbinom") << cell(0) << cell(1) << ETag("mtbinom");
548                 break;
549         case DBINOM:
550         default:
551                 os << MTag("mdbinom") << cell(0) << cell(1) << ETag("mdbinom");
552                 break;
553         }
554         return docstring();
555 }
556
557
558 void InsetMathBinom::validate(LaTeXFeatures & features) const
559 {
560         if (kind_ == BINOM)
561                 features.require("binom");
562         if (kind_ == DBINOM || kind_ == TBINOM)
563                 features.require("amsmath");
564         InsetMathNest::validate(features);
565 }
566
567
568 } // namespace lyx