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