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