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