]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathFrac.cpp
InsetMathFrac: reimplement \cfrac with its optional argument the way Enrico proposed...
[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                 FracChanger dummy(mi.base);
151                 cell(0).metrics(mi, dim0);
152                 cell(1).metrics(mi, dim1);
153                 if (nargs() == 3)
154                         cell(2).metrics(mi, dim2);
155
156                 if (kind_ == NICEFRAC) {
157                         dim.wid = dim0.width() + dim1.wid + 5;
158                         dim.asc = dim0.height() + 5;
159                         dim.des = dim1.height() - 5;
160                 } else if (kind_ == CFRAC || kind_ == CFRACLEFT
161                         || kind_ == CFRACRIGHT) {
162                         // cfrac is always in display size
163                         StyleChanger dummy2(mi.base, LM_ST_DISPLAY);
164                         cell(0).metrics(mi, dim0);
165                         cell(1).metrics(mi, dim1);
166                         dim.wid = max(dim0.wid, dim1.wid) + 2;
167                         dim.asc = dim0.height() + 2 + 5;
168                         dim.des = dim1.height() + 2 - 5;
169                 } else if (kind_ == UNITFRAC) {
170                         ShapeChanger dummy2(mi.base.font, UP_SHAPE);
171                         dim.wid = dim0.width() + dim1.wid + 5;
172                         dim.asc = dim0.height() + 5;
173                         dim.des = dim1.height() - 5;
174                 } else if (kind_ == DFRAC) {
175                         // dfrac is in always in display size
176                         StyleChanger dummy2(mi.base, LM_ST_DISPLAY);
177                         cell(0).metrics(mi, dim0);
178                         cell(1).metrics(mi, dim1);
179                         dim.wid = max(dim0.wid, dim1.wid) + 2;
180                         dim.asc = dim0.height() + 2 + 5;
181                         dim.des = dim1.height() + 2 - 5;
182                 } else if (kind_ == TFRAC) {
183                         // tfrac is in always in text size
184                         StyleChanger dummy2(mi.base, LM_ST_SCRIPT);
185                         cell(0).metrics(mi, dim0);
186                         cell(1).metrics(mi, dim1);
187                         dim.wid = max(dim0.wid, dim1.wid) + 2;
188                         dim.asc = dim0.height() + 2 + 5;
189                         dim.des = dim1.height() + 2 - 5;
190                 } else {
191                         // FRAC
192                         dim.wid = max(dim0.wid, dim1.wid) + 2;
193                         dim.asc = dim0.height() + 2 + 5;
194                         dim.des = dim1.height() + 2 - 5;
195                 }
196         }
197         metricsMarkers(dim);
198 }
199
200
201 void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
202 {
203         setPosCache(pi, x, y);
204         Dimension const dim = dimension(*pi.base.bv);
205         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
206         int m = x + dim.wid / 2;
207         if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
208                 if (nargs() == 1) {
209                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
210                         cell(0).draw(pi, x + 1, y);
211                 } else if (nargs() == 2) {
212                         cell(0).draw(pi, x + 1, y);
213                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
214                         cell(1).draw(pi, x + dim0.width() + 5, y);
215                 } else {
216                         cell(2).draw(pi, x + 1, y);
217                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
218                         FracChanger dummy(pi.base);
219                         Dimension const dim1 = cell(1).dimension(*pi.base.bv);
220                         Dimension const dim2 = cell(2).dimension(*pi.base.bv);
221                         int xx = x + dim2.wid + 5;
222                         cell(0).draw(pi, xx + 2, 
223                                          y - dim0.des - 5);
224                         cell(1).draw(pi, xx  + dim0.width() + 5, 
225                                          y + dim1.asc / 2);
226                 }
227         } else {
228                 FracChanger dummy(pi.base);
229                 Dimension const dim1 = cell(1).dimension(*pi.base.bv);
230                 if (kind_ == NICEFRAC) {
231                         cell(0).draw(pi, x + 2,
232                                         y - dim0.des - 5);
233                         cell(1).draw(pi, x + dim0.width() + 5,
234                                         y + dim1.asc / 2);
235                 } else if (kind_ == UNITFRAC) {
236                         ShapeChanger dummy2(pi.base.font, UP_SHAPE);
237                         cell(0).draw(pi, x + 2,
238                                         y - dim0.des - 5);
239                         cell(1).draw(pi, x + dim0.width() + 5,
240                                         y + dim1.asc / 2);
241                 } else if (kind_ == CFRAC || kind_ == CFRACLEFT
242                         || kind_ == CFRACRIGHT) {
243                         // cfrac is always in display size
244                         StyleChanger dummy2(pi.base, LM_ST_DISPLAY);
245                         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
246                         Dimension const dim1 = cell(1).dimension(*pi.base.bv);
247                         int m = x + dim.wid / 2;
248                         if (kind_ == CFRAC)
249                                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
250                         else if (kind_ == CFRACLEFT)
251                                 cell(0).draw(pi, x + 2, y - dim0.des - 2 - 5);
252                         else if (kind_ == CFRACRIGHT)
253                                 cell(0).draw(pi, x + dim.wid - dim0.wid - 2,
254                                 y - dim0.des - 2 - 5);
255                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
256                 } else if (kind_ == DFRAC) {
257                         // dfrac is in always in display size
258                         StyleChanger dummy2(pi.base, LM_ST_DISPLAY);
259                         //Dimension const dim = dimension(*pi.base.bv);
260                         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
261                         Dimension const dim1 = cell(1).dimension(*pi.base.bv);
262                         int m = x + dim.wid / 2;
263                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
264                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
265                 } else if (kind_ == TFRAC) {
266                         // tfrac is in always in text size
267                         StyleChanger dummy2(pi.base, LM_ST_SCRIPT);
268                         Dimension const dim0 = cell(0).dimension(*pi.base.bv);
269                         Dimension const dim1 = cell(1).dimension(*pi.base.bv);
270                         int m = x + dim.wid / 2;
271                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
272                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
273                 } else {
274                         // FRAC
275                         cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
276                         cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
277                 }
278         }
279         if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
280                 // Diag line:
281                 int xx = x;
282                 if (nargs() == 3)
283                         xx += cell(2).dimension(*pi.base.bv).wid + 5;
284                 pi.pain.line(xx + dim0.wid,
285                                 y + dim.des - 2,
286                                 xx + dim0.wid + 5,
287                                 y - dim.asc + 2, Color_math);
288         }
289         if (kind_ == FRAC || kind_ == CFRAC || kind_ == CFRACLEFT
290                 || kind_ == CFRACRIGHT || kind_ == DFRAC
291                 || kind_ == TFRAC || kind_ == OVER)
292                 pi.pain.line(x + 1, y - 5,
293                                 x + dim.wid - 2, y - 5, Color_math);
294         drawMarkers(pi, x, y);
295 }
296
297
298 void InsetMathFrac::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
299 {
300         Dimension dim0, dim1;
301         cell(0).metricsT(mi, dim0);
302         cell(1).metricsT(mi, dim1);
303         dim.wid = max(dim0.width(), dim1.wid);
304         dim.asc = dim0.height() + 1;
305         dim.des = dim1.height();
306 }
307
308
309 void InsetMathFrac::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
310 {
311         // FIXME: BROKEN!
312         /*
313         Dimension dim;
314         int m = x + dim.width() / 2;
315         cell(0).drawT(pain, m - dim0.width() / 2, y - dim0.des - 1);
316         cell(1).drawT(pain, m - dim1.wid / 2, y + dim1.asc);
317         // ASCII art: ignore niceties
318         if (kind_ == FRAC || kind_ == OVER || kind_ == NICEFRAC || kind_ == UNITFRAC)
319                 pain.horizontalLine(x, y, dim.width());
320         */
321 }
322
323
324 void InsetMathFrac::write(WriteStream & os) const
325 {
326         MathEnsurer ensurer(os);
327         switch (kind_) {
328         case ATOP:
329                 // \\atop is only for compatibility, \\binom is the
330                 // LaTeX2e successor
331                 os << '{' << cell(0) << "\\atop " << cell(1) << '}';
332                 break;
333         case OVER:
334                 // \\over is only for compatibility, normalize this to \\frac
335                 os << "\\frac{" << cell(0) << "}{" << cell(1) << '}';
336                 break;
337         case FRAC:
338         case DFRAC:
339         case TFRAC:
340         case NICEFRAC:
341         case CFRAC:
342         case UNITFRAC:
343                 if (nargs() == 2)
344                         InsetMathNest::write(os);
345                 else
346                         os << "\\unitfrac[" << cell(2) << "]{" << cell(0) << "}{" << cell(1) << '}';
347                 break;
348         case UNIT:
349                 if (nargs() == 2)
350                         os << "\\unit[" << cell(0) << "]{" << cell(1) << '}';
351                 else
352                         os << "\\unit{" << cell(0) << '}';
353                 break;
354         case CFRACLEFT:
355                 os << "\\cfrac[l]{" << cell(0) << "}{" << cell(1) << '}';
356                 break;
357         case CFRACRIGHT:
358                 os << "\\cfrac[r]{" << cell(0) << "}{" << cell(1) << '}';
359                 break;
360         }
361 }
362
363
364 docstring InsetMathFrac::name() const
365 {
366         switch (kind_) {
367         case FRAC:
368                 return from_ascii("frac");
369         case CFRAC:
370         case CFRACLEFT:
371         case CFRACRIGHT:
372                 return from_ascii("cfrac");
373         case DFRAC:
374                 return from_ascii("dfrac");
375         case TFRAC:
376                 return from_ascii("tfrac");
377         case OVER:
378                 return from_ascii("over");
379         case NICEFRAC:
380                 return from_ascii("nicefrac");
381         case UNITFRAC:
382                 return from_ascii("unitfrac");
383         case UNIT:
384                 return from_ascii("unit");
385         case ATOP:
386                 return from_ascii("atop");
387         }
388         // shut up stupid compiler
389         return docstring();
390 }
391
392
393 bool InsetMathFrac::extraBraces() const
394 {
395         return kind_ == ATOP || kind_ == OVER;
396 }
397
398
399 void InsetMathFrac::maple(MapleStream & os) const
400 {
401         os << '(' << cell(0) << ")/(" << cell(1) << ')';
402 }
403
404
405 void InsetMathFrac::mathematica(MathematicaStream & os) const
406 {
407         os << '(' << cell(0) << ")/(" << cell(1) << ')';
408 }
409
410
411 void InsetMathFrac::octave(OctaveStream & os) const
412 {
413         os << '(' << cell(0) << ")/(" << cell(1) << ')';
414 }
415
416
417 void InsetMathFrac::mathmlize(MathStream & os) const
418 {
419         switch (kind_) {
420         case FRAC:
421                 os << MTag("mfrac") << cell(0) << cell(1) << ETag("mfrac");
422                 break;
423         case DFRAC:
424                 os << MTag("mdfrac") << cell(0) << cell(1) << ETag("mdfrac");
425                 break;
426         case TFRAC:
427                 os << MTag("mtfrac") << cell(0) << cell(1) << ETag("mtfrac");
428                 break;
429         }
430 }
431
432
433 void InsetMathFrac::validate(LaTeXFeatures & features) const
434 {
435         if (kind_ == NICEFRAC || kind_ == UNITFRAC || kind_ == UNIT)
436                 features.require("units");
437         if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
438                 || kind_ == DFRAC || kind_ == TFRAC)
439                 features.require("amsmath");
440         InsetMathNest::validate(features);
441 }
442
443
444 /////////////////////////////////////////////////////////////////////
445 //
446 // InsetMathBinom
447 //
448 /////////////////////////////////////////////////////////////////////
449
450
451 InsetMathBinom::InsetMathBinom(Kind kind)
452         : kind_(kind)
453 {}
454
455
456 Inset * InsetMathBinom::clone() const
457 {
458         return new InsetMathBinom(*this);
459 }
460
461
462 int InsetMathBinom::dw(int height) const
463 {
464         int w = height / 5;
465         if (w > 15)
466                 w = 15;
467         if (w < 6)
468                 w = 6;
469         return w;
470 }
471
472
473 void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
474 {
475         Dimension dim0, dim1;
476
477         // FIXME: for an unknown reason the cells must be set directly
478         // after the StyleChanger and cannot be set after the if case
479         if (kind_ == DBINOM) {
480                 StyleChanger dummy(mi.base, LM_ST_DISPLAY);
481                 cell(0).metrics(mi, dim0);
482                 cell(1).metrics(mi, dim1);
483         } else if (kind_ == TBINOM) {
484                 StyleChanger dummy(mi.base, LM_ST_SCRIPT);
485                 cell(0).metrics(mi, dim0);
486                 cell(1).metrics(mi, dim1);
487         } else {
488                 FracChanger dummy(mi.base);
489                 cell(0).metrics(mi, dim0);
490                 cell(1).metrics(mi, dim1);
491         }
492         dim.asc = dim0.height() + 4 + 5;
493         dim.des = dim1.height() + 4 - 5;
494         dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
495         metricsMarkers2(dim);
496 }
497
498
499 void InsetMathBinom::draw(PainterInfo & pi, int x, int y) const
500 {
501         Dimension const dim = dimension(*pi.base.bv);
502         Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
503         Dimension const & dim1 = cell(1).dimension(*pi.base.bv);
504         // define the binom brackets
505         docstring const bra = kind_ == BRACE ? from_ascii("{") :
506                 kind_ == BRACK ? from_ascii("[") : from_ascii("(");
507         docstring const ket = kind_ == BRACE ? from_ascii("}") :
508                 kind_ == BRACK ? from_ascii("]") : from_ascii(")");
509
510         int m = x + dim.width() / 2;
511         // FIXME: for an unknown reason the cells must be drawn directly
512         // after the StyleChanger and cannot be drawn after the if case
513         if (kind_ == DBINOM) {
514                 StyleChanger dummy(pi.base, LM_ST_DISPLAY);
515                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
516                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
517         } else if (kind_ == TBINOM) {
518                 StyleChanger dummy(pi.base, LM_ST_SCRIPT);
519                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
520                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
521         } else {
522                 FracChanger dummy2(pi.base);
523                 cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 3 - 5);
524                 cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 3 - 5);
525         }
526         // draw the brackets and the marker
527         mathed_draw_deco(pi, x, y - dim.ascent(), dw(dim.height()),
528                 dim.height(), bra);
529         mathed_draw_deco(pi, x + dim.width() - dw(dim.height()),
530                 y - dim.ascent(), dw(dim.height()), dim.height(), ket);
531         drawMarkers2(pi, x, y);
532 }
533
534
535 bool InsetMathBinom::extraBraces() const
536 {
537         return kind_ == CHOOSE || kind_ == BRACE || kind_ == BRACK;
538 }
539
540
541 void InsetMathBinom::write(WriteStream & os) const
542 {
543         MathEnsurer ensurer(os);
544         switch (kind_) {
545         case BINOM:
546                 os << "\\binom{" << cell(0) << "}{" << cell(1) << '}';
547                 break;
548         case DBINOM:
549                 os << "\\dbinom{" << cell(0) << "}{" << cell(1) << '}';
550                 break;
551         case TBINOM:
552                 os << "\\tbinom{" << cell(0) << "}{" << cell(1) << '}';
553                 break;
554         case CHOOSE:
555                 os << '{' << cell(0) << " \\choose " << cell(1) << '}';
556                 break;
557         case BRACE:
558                 os << '{' << cell(0) << " \\brace " << cell(1) << '}';
559                 break;
560         case BRACK:
561                 os << '{' << cell(0) << " \\brack " << cell(1) << '}';
562                 break;
563         }
564 }
565
566
567 void InsetMathBinom::normalize(NormalStream & os) const
568 {
569         os << "[binom " << cell(0) << ' ' << cell(1) << ']';
570 }
571
572
573 void InsetMathBinom::mathmlize(MathStream & os) const
574 {
575         switch (kind_) {
576         case BINOM:
577                 os << MTag("mbinom") << cell(0) << cell(1) << ETag("mbinom");
578                 break;
579         case DBINOM:
580                 os << MTag("mdbinom") << cell(0) << cell(1) << ETag("mdbinom");
581                 break;
582         case TBINOM:
583                 os << MTag("mtbinom") << cell(0) << cell(1) << ETag("mtbinom");
584                 break;
585         }
586 }
587
588
589 void InsetMathBinom::validate(LaTeXFeatures & features) const
590 {
591         if (kind_ == BINOM)
592                 features.require("binom");
593         if (kind_ == DBINOM || kind_ == TBINOM)
594                 features.require("amsmath");
595         InsetMathNest::validate(features);
596 }
597
598
599 } // namespace lyx