]> git.lyx.org Git - features.git/blob - src/mathed/math_support.C
crude support for a few more AMS constructs
[features.git] / src / mathed / math_support.C
1 #include <config.h>
2
3 #include <map>
4
5 #include "math_support.h"
6 #include "lyxfont.h"
7 #include "frontends/font_loader.h"
8 #include "frontends/font_metrics.h"
9 #include "math_cursor.h"
10 #include "math_defs.h"
11 #include "math_inset.h"
12 #include "math_parser.h"
13 #include "frontends/Painter.h"
14 #include "debug.h"
15 #include "commandtags.h"
16
17 using std::map;
18 using std::endl;
19 using std::max;
20
21
22 ///
23 class Matrix {
24 public:
25         ///
26         Matrix(int, double, double);
27         ///
28         void transform(double &, double &);
29 private:
30         ///
31         double m_[2][2];
32 };
33
34
35 Matrix::Matrix(int code, double x, double y)
36 {
37         double const cs = (code & 1) ? 0 : (1 - code);
38         double const sn = (code & 1) ? (2 - code) : 0;
39         m_[0][0] =  cs * x;
40         m_[0][1] =  sn * x;
41         m_[1][0] = -sn * y;
42         m_[1][1] =  cs * y;
43 }
44
45
46 void Matrix::transform(double & x, double & y)
47 {
48         double xx = m_[0][0] * x + m_[0][1] * y;
49         double yy = m_[1][0] * x + m_[1][1] * y;
50         x = xx;
51         y = yy;
52 }
53
54
55 namespace {
56
57 LyXFont * MathFonts = 0;
58 bool font_available[LM_FONT_END];
59 bool font_available_initialized[LM_FONT_END];
60
61 enum MathFont {
62         FONT_IT,
63         FONT_SYMBOL,
64         FONT_SYMBOLI,
65         FONT_BF,
66         FONT_TT,
67         FONT_RM,
68         FONT_SF,
69         FONT_CMR,
70         FONT_CMSY,
71         FONT_CMM,
72         FONT_CMEX,
73         FONT_MSA,
74         FONT_MSB,
75         FONT_EUFRAK,
76         FONT_FAKEBB,
77         FONT_FAKECAL,
78         FONT_FAKEFRAK,
79         FONT_NUM
80 };
81
82 void mathed_init_fonts()
83 {
84         MathFonts = new LyXFont[FONT_NUM];
85
86         MathFonts[FONT_IT].setShape(LyXFont::ITALIC_SHAPE);
87
88         MathFonts[FONT_SYMBOL].setFamily(LyXFont::SYMBOL_FAMILY);
89
90         MathFonts[FONT_SYMBOLI].setFamily(LyXFont::SYMBOL_FAMILY);
91         MathFonts[FONT_SYMBOLI].setShape(LyXFont::ITALIC_SHAPE);
92
93         MathFonts[FONT_BF].setSeries(LyXFont::BOLD_SERIES);
94
95         MathFonts[FONT_TT].setFamily(LyXFont::TYPEWRITER_FAMILY);
96         MathFonts[FONT_RM].setFamily(LyXFont::ROMAN_FAMILY);
97         MathFonts[FONT_SF].setFamily(LyXFont::SANS_FAMILY);
98
99         MathFonts[FONT_CMR].setFamily(LyXFont::CMR_FAMILY);
100         MathFonts[FONT_CMSY].setFamily(LyXFont::CMSY_FAMILY);
101         MathFonts[FONT_CMM].setFamily(LyXFont::CMM_FAMILY);
102         MathFonts[FONT_CMEX].setFamily(LyXFont::CMEX_FAMILY);
103         MathFonts[FONT_MSA].setFamily(LyXFont::MSA_FAMILY);
104         MathFonts[FONT_MSB].setFamily(LyXFont::MSB_FAMILY);
105         MathFonts[FONT_EUFRAK].setFamily(LyXFont::EUFRAK_FAMILY);
106
107         MathFonts[FONT_FAKEBB].setFamily(LyXFont::TYPEWRITER_FAMILY);
108         MathFonts[FONT_FAKEBB].setSeries(LyXFont::BOLD_SERIES);
109
110         MathFonts[FONT_FAKECAL].setFamily(LyXFont::SANS_FAMILY);
111         MathFonts[FONT_FAKECAL].setShape(LyXFont::ITALIC_SHAPE);
112
113         MathFonts[FONT_FAKEFRAK].setFamily(LyXFont::SANS_FAMILY);
114         MathFonts[FONT_FAKEFRAK].setSeries(LyXFont::BOLD_SERIES);
115
116         for (int i = 0; i < LM_FONT_END; ++i)
117                 font_available_initialized[i] = false;
118 }
119
120
121 LyXFont const & whichFontBaseIntern(MathTextCodes type)
122 {
123         if (!MathFonts)
124                 mathed_init_fonts();
125
126         switch (type) {
127         case LM_TC_SYMB:
128         case LM_TC_BOLDSYMB:
129                 return MathFonts[FONT_SYMBOLI];
130
131         case LM_TC_VAR:
132         case LM_TC_IT:
133                 return MathFonts[FONT_IT];
134
135         case LM_TC_BF:
136                 return MathFonts[FONT_BF];
137
138         case LM_TC_BB:
139                 return MathFonts[FONT_MSB];
140
141         case LM_TC_CAL:
142                 return MathFonts[FONT_CMSY];
143
144         case LM_TC_TT:
145                 return MathFonts[FONT_TT];
146
147         case LM_TC_BOX:
148         case LM_TC_TEXTRM:
149         case LM_TC_CONST:
150         case LM_TC_TEX:
151         case LM_TC_RM:
152                 return MathFonts[FONT_RM];
153
154         case LM_TC_SF:
155                 return MathFonts[FONT_SF];
156
157         case LM_TC_CMR:
158                 return MathFonts[FONT_CMR];
159
160         case LM_TC_CMSY:
161                 return MathFonts[FONT_CMSY];
162
163         case LM_TC_CMM:
164                 return MathFonts[FONT_CMM];
165
166         case LM_TC_CMEX:
167                 return MathFonts[FONT_CMEX];
168
169         case LM_TC_MSA:
170                 return MathFonts[FONT_MSA];
171
172         case LM_TC_MSB:
173                 return MathFonts[FONT_MSB];
174
175         case LM_TC_EUFRAK:
176                 return MathFonts[FONT_EUFRAK];
177
178         default:
179                 break;
180         }
181         return MathFonts[1];
182 }
183
184
185 LyXFont const & whichFontBase(MathTextCodes type)
186 {
187         if (!MathFonts)
188                 mathed_init_fonts();
189
190         switch (type) {
191         case LM_TC_BB:
192                 if (math_font_available(LM_TC_MSB))
193                         return MathFonts[FONT_MSB];
194                 else
195                         return MathFonts[FONT_FAKEBB];
196
197         case LM_TC_CAL:
198                 if (math_font_available(LM_TC_CMSY))
199                         return MathFonts[FONT_CMSY];
200                 else
201                         return MathFonts[FONT_FAKECAL];
202
203         case LM_TC_EUFRAK:
204                 if (math_font_available(LM_TC_EUFRAK))
205                         return MathFonts[FONT_EUFRAK];
206                 else
207                         return MathFonts[FONT_FAKEFRAK];
208
209         default:
210                 break;
211         }
212         return whichFontBaseIntern(type);
213 }
214
215 } // namespace
216
217
218 void whichFont(LyXFont & f, MathTextCodes type, MathMetricsInfo const & size)
219 {
220         f = whichFontBase(type);
221         // use actual size
222         f.setSize(size.font.size());
223
224         switch (size.style) {
225         case LM_ST_DISPLAY:
226                 if (type == LM_TC_BOLDSYMB || type == LM_TC_CMEX) {
227                         f.incSize();
228                         f.incSize();
229                 }
230                 break;
231
232         case LM_ST_TEXT:
233                 break;
234
235         case LM_ST_SCRIPT:
236                 f.decSize();
237                 f.decSize();
238                 break;
239
240         case LM_ST_SCRIPTSCRIPT:
241                 f.decSize();
242                 f.decSize();
243                 f.decSize();
244                 break;
245
246         default:
247                 lyxerr << "Math Error: wrong font size: " << size.style << endl;
248                 break;
249         }
250
251         if (type != LM_TC_TEXTRM && type != LM_TC_BOX)
252                 f.setColor(LColor::math);
253
254         if (type == LM_TC_TEX)
255                 f.setColor(LColor::latex);
256 }
257
258
259 bool math_font_available(MathTextCodes type)
260 {
261         if (!font_available_initialized[type]) {
262                 font_available_initialized[type] = true;
263                 font_available[type] = fontloader.available(whichFontBaseIntern(type));
264                 if (!font_available[type])
265                         lyxerr[Debug::FONT] << "Math font " << type << " not available.\n";
266         }
267         return font_available[type];
268 }
269
270
271 namespace {
272
273 /*
274  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
275  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4= square polyline
276  */
277
278
279 double const parenthHigh[] = {
280         2, 13,
281         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
282         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
283         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
284         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
285         0.9840, 0.9986,
286         0
287 };
288
289
290 double const parenth[] = {
291         2, 13,
292         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
293         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
294         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
295         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
296         0.9930, 0.9919,
297         0
298 };
299
300
301 double const brace[] = {
302         2, 21,
303         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
304         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
305         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
306         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
307         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
308         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
309         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
310         0
311 };
312
313
314 double const arrow[] = {
315         4, 7,
316         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
317         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
318         0.9500, 0.7500,
319         3, 0.5000, 0.1500, 0.5000, 0.9500,
320         0
321 };
322
323
324 double const Arrow[] = {
325         4, 7,
326         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
327         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
328         0.9500, 0.7500,
329         3, 0.3500, 0.5000, 0.3500, 0.9500,
330         3, 0.6500, 0.5000, 0.6500, 0.9500,
331         0
332 };
333
334
335 double const udarrow[] = {
336         2, 3,
337         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
338         2, 3,
339         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
340         1, 0.5, 0.2,  0.5, 0.8,
341         0
342 };
343
344
345 double const Udarrow[] = {
346         2, 3,
347         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
348         2, 3,
349         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
350         1, 0.35, 0.2, 0.35, 0.8,
351         1, 0.65, 0.2, 0.65, 0.8,
352         0
353 };
354
355
356 double const brack[] = {
357         2, 4,
358         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
359         0
360 };
361
362
363 double const corner[] = {
364         2, 3,
365         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
366         0
367 };
368
369
370 double const angle[] = {
371         2, 3,
372         1, 0,  0.05, 0.5,  1, 1,
373         0
374 };
375
376
377 double const slash[] = {
378         1, 0.95, 0.05, 0.05, 0.95,
379         0
380 };
381
382
383 double const hline[] = {
384         1, 0.00, 0.5, 1.0, 0.5,
385         0
386 };
387
388
389 double const ddot[] = {
390         1, 0.2, 0.5,  0.3, 0.5,
391         1, 0.7, 0.5,  0.8, 0.5,
392         0
393 };
394
395
396 double const dddot[] = {
397         1, 0.1, 0.5,  0.2, 0.5,
398         1, 0.45, 0.5, 0.55, 0.5,
399         1, 0.8, 0.5,  0.9, 0.5,
400         0
401 };
402
403
404 double const hline3[] = {
405         1, 0.1,   0,  0.15,  0,
406         1, 0.475, 0,  0.525, 0,
407         1, 0.85,  0,  0.9,   0,
408         0
409 };
410
411
412 double const dline3[] = {
413         1, 0.1,   0.1,   0.15,  0.15,
414         1, 0.475, 0.475, 0.525, 0.525,
415         1, 0.85,  0.85,  0.9,   0.9,
416         0
417 };
418
419
420 double const hlinesmall[] = {
421         1, 0.4, 0.5, 0.6, 0.5,
422         0
423 };
424
425
426 double const vert[] = {
427         1, 0.5, 0.05,  0.5, 0.95,
428         0
429 };
430
431
432 double const  Vert[] = {
433         1, 0.3, 0.05,  0.3, 0.95,
434         1, 0.7, 0.05,  0.7, 0.95,
435         0
436 };
437
438
439 double const ring[] = {
440         2, 5,
441         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
442         0
443 };
444
445
446 double const tilde[] = {
447         2, 4,
448         0.05, 0.8,  0.25, 0.2,  0.75, 0.8,  0.95, 0.2,
449         0
450 };
451
452
453 struct deco_struct {
454         double const * data;
455         int angle;
456 };
457
458 struct named_deco_struct {
459         char const * name;
460         double const * data;
461         int angle;
462 };
463
464 named_deco_struct deco_table[] = {
465         // Decorations
466         {"widehat",        angle,      3 },
467         {"widetilde",      tilde,      0 },
468         {"underbar",       hline,      0 },
469         {"underline",      hline,      0 },
470         {"overline",       hline,      0 },
471         {"underbrace",     brace,      1 },
472         {"overbrace",      brace,      3 },
473         {"overleftarrow",  arrow,      1 },
474         {"overrightarrow", arrow,      3 },
475         {"overleftrightarrow", udarrow, 1 },
476         {"xleftarrow",     arrow,      1 },
477         {"xrightarrow",    arrow,      3 },
478         {"underleftarrow", arrow,      1 },
479         {"underrightarrow", arrow,     3 },
480         {"underleftrightarrow",udarrow, 1 },
481
482         // Delimiters
483         {"(",              parenth,    0 },
484         {")",              parenth,    2 },
485         {"{",              brace,      0 },
486         {"}",              brace,      2 },
487         {"[",              brack,      0 },
488         {"]",              brack,      2 },
489         {"|",              vert,       0 },
490         {"/",              slash,      0 },
491         {"vert",           vert,       0 },
492         {"Vert",           Vert,       0 },
493         {"'",              slash,      1 },
494         {"backslash",      slash,      1 },
495         {"langle",         angle,      0 },
496         {"lceil",          corner,     0 },
497         {"lfloor",         corner,     1 },
498         {"rangle",         angle,      2 },
499         {"rceil",          corner,     3 },
500         {"rfloor",         corner,     2 },
501         {"downarrow",      arrow,      2 },
502         {"Downarrow",      Arrow,      2 },
503         {"uparrow",        arrow,      0 },
504         {"Uparrow",        Arrow,      0 },
505         {"updownarrow",    udarrow,    0 },
506         {"Updownarrow",    Udarrow,    0 },
507
508         // Accents
509         {"ddot",           ddot,       0 },
510         {"dddot",          dddot,      0 },
511         {"hat",            angle,      3 },
512         {"grave",          slash,      1 },
513         {"acute",          slash,      0 },
514         {"tilde",          tilde,      0 },
515         {"bar",            hline,      0 },
516         {"dot",            hlinesmall, 0 },
517         {"check",          angle,      1 },
518         {"breve",          parenth,    1 },
519         {"vec",            arrow,      3 },
520         {"not",            slash,      0 },
521         {"mathring",       ring,       0 },
522
523         // Dots
524         {"ldots",          hline3,     0 },
525         {"cdots",          hline3,     0 },
526         {"vdots",          hline3,     1 },
527         {"ddots",          dline3,     0 },
528         {"dotsb",          hline3,     0 },
529         {"dotsc",          hline3,     0 },
530         {"dotsi",          hline3,     0 },
531         {"dotsm",          hline3,     0 },
532         {"dotso",          hline3,     0 }
533 };
534
535
536 map<string, deco_struct> deco_list;
537
538 // sort the table on startup
539 struct init_deco_table {
540         init_deco_table() {
541                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
542                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
543                         deco_struct d;
544                         d.data  = p->data;
545                         d.angle = p->angle;
546                         deco_list[p->name]= d;
547                 }
548         }
549 };
550
551 static init_deco_table dummy;
552
553
554 deco_struct const * search_deco(string const & name)
555 {
556         map<string, deco_struct>::const_iterator p = deco_list.find(name);
557         return (p == deco_list.end()) ? 0 : &(p->second);
558 }
559
560
561 } // namespace anon
562
563
564 void mathed_char_dim(LyXFont const & font,
565         unsigned char c, int & asc, int & des, int & wid)
566 {
567         des = font_metrics::descent(c, font);
568         asc = font_metrics::ascent(c, font);
569         wid = mathed_char_width(font, c);
570 }
571
572
573 int mathed_char_ascent(LyXFont const & font, unsigned char c)
574 {
575         return font_metrics::ascent(c, font);
576 }
577
578
579 int mathed_char_descent(LyXFont const & font, unsigned char c)
580 {
581         return font_metrics::descent(c, font);
582 }
583
584
585 int mathed_char_width(LyXFont const & font, unsigned char c)
586 {
587         return font_metrics::width(c, font);
588 }
589
590
591 void mathed_string_dim(LyXFont const & font,
592         string const & s, int & asc, int & des, int & wid)
593 {
594         asc = des = 0;
595         for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
596                 des = max(des, font_metrics::descent(*it, font));
597                 asc = max(asc, font_metrics::ascent(*it, font));
598         }
599         wid = font_metrics::width(s, font);
600 }
601
602
603 int mathed_string_width(LyXFont const & font, string const & s)
604 {
605         return font_metrics::width(s, font);
606 }
607
608
609 int mathed_string_ascent(LyXFont const & font, string const & s)
610 {
611         int asc = 0;
612         for (string::const_iterator it = s.begin(); it != s.end(); ++it)
613                 asc = max(asc, font_metrics::ascent(*it, font));
614         return asc;
615 }
616
617
618 int mathed_string_descent(LyXFont const & font, string const & s)
619 {
620         int des = 0;
621         for (string::const_iterator it = s.begin(); it != s.end(); ++it)
622                 des = max(des, font_metrics::descent(*it, font));
623         return des;
624 }
625
626
627
628 void mathed_draw_deco(Painter & pain, int x, int y, int w, int h,
629         const string & name)
630 {
631         if (name == ".") {
632                 pain.line(x + w/2, y, x + w/2, y + h,
633                           LColor::mathcursor, Painter::line_onoffdash);
634                 return;
635         }
636
637         deco_struct const * mds = search_deco(name);
638         if (!mds) {
639                 lyxerr << "Deco was not found. Programming error?\n";
640                 lyxerr << "name: '" << name << "'\n";
641                 return;
642         }
643
644         int const n = (w < h) ? w : h;
645         int const r = mds->angle;
646         double const * d = mds->data;
647
648         if (h > 70 && (name == "(" || name == ")"))
649                 d = parenthHigh;
650
651         Matrix mt(r, w, h);
652         Matrix sqmt(r, n, n);
653
654         if (r > 0 && r < 3)
655                 y += h;
656
657         if (r >= 2)
658                 x += w;
659
660         for (int i = 0; d[i];) {
661                 int code = int(d[i++]);
662                 if (code & 1) {  // code == 1 || code == 3
663                         double xx = d[i++];
664                         double yy = d[i++];
665                         double x2 = d[i++];
666                         double y2 = d[i++];
667                         if (code == 3)
668                                 sqmt.transform(xx, yy);
669                         else
670                                 mt.transform(xx, yy);
671                         mt.transform(x2, y2);
672                         pain.line(x + int(xx), y + int(yy), x + int(x2), y + int(y2),
673                                         LColor::math);
674                 }       else {
675                         int xp[32];
676                         int yp[32];
677                         int const n = int(d[i++]);
678                         for (int j = 0; j < n; ++j) {
679                                 double xx = d[i++];
680                                 double yy = d[i++];
681 //           lyxerr << " " << xx << " " << yy << " ";
682                                 if (code == 4)
683                                         sqmt.transform(xx, yy);
684                                 else
685                                         mt.transform(xx, yy);
686                                 xp[j] = x + int(xx);
687                                 yp[j] = y + int(yy);
688                                 //  lyxerr << "P[" << j " " << xx << " " << yy << " " << x << " " << y << "]";
689                         }
690                         pain.lines(xp, yp, n, LColor::math);
691                 }
692         }
693 }
694
695
696 void mathed_draw_framebox(Painter & pain, int x, int y, MathInset const * p)
697 {
698         if (mathcursor && mathcursor->isInside(p))
699                 pain.rectangle(x, y - p->ascent(), p->width(), p->height(),
700                         LColor::mathframe);
701 }
702
703
704 // In the future maybe we use a better fonts renderer
705 void drawStr(Painter & pain, LyXFont const & font,
706         int x, int y, string const & str)
707 {
708         pain.text(x, y, str, font);
709 }
710
711
712 void drawChar(Painter & pain, LyXFont const & font, int x, int y, char c)
713 {
714         pain.text(x, y, c, font);
715 }
716
717
718 // decrease math size for super- and subscripts
719 void smallerStyleScript(MathMetricsInfo & st)
720 {
721         switch (st.style) {
722                 case LM_ST_DISPLAY:
723                 case LM_ST_TEXT:    st.style = LM_ST_SCRIPT; break;
724                 default:            st.style = LM_ST_SCRIPTSCRIPT;
725         }
726 }
727
728
729 // decrease math size for fractions
730 void smallerStyleFrac(MathMetricsInfo & st)
731 {
732         switch (st.style) {
733                 case LM_ST_DISPLAY: st.style = LM_ST_TEXT; break;
734                 case LM_ST_TEXT:    st.style = LM_ST_SCRIPT; break;
735                 default:            st.style = LM_ST_SCRIPTSCRIPT;
736         }
737 }
738
739
740 void math_font_max_dim(LyXFont const & font, int & asc, int & des)
741 {
742         asc = font_metrics::maxAscent(font);
743         des = font_metrics::maxDescent(font);
744 }
745
746
747 char const * math_font_name(MathTextCodes code)
748 {
749         static char const * theFontNames[] = {
750                 "mathrm",
751                 "mathcal",
752                 "mathfrak",
753                 "mathbf",
754                 "mathbb",
755                 "mathsf",
756                 "mathtt",
757                 "mathit",
758                 "textrm"
759         };
760
761         if (code >= LM_TC_RM && code <= LM_TC_TEXTRM)
762                 return theFontNames[code - LM_TC_RM];
763         return 0;
764 }
765
766 string convertDelimToLatexName(string const & name)
767 {
768         if (name == "(")
769                 return name;
770         if (name == "[")
771                 return name;
772         if (name == ".")
773                 return name;
774         if (name == ")")
775                 return name;
776         if (name == "]")
777                 return name;
778         if (name == "/")
779                 return name;
780         if (name == "|")
781                 return name;
782         return "\\" + name + " ";
783 }