]> git.lyx.org Git - lyx.git/blob - src/mathed/math_support.C
fix #1073
[lyx.git] / src / mathed / math_support.C
1
2 #include <config.h>
3
4 #include "math_support.h"
5 #include "lyxfont.h"
6 #include "math_cursor.h"
7 #include "math_inset.h"
8 #include "math_parser.h"
9 #include "metricsinfo.h"
10 #include "frontends/Painter.h"
11 #include "frontends/font_metrics.h"
12 #include "frontends/lyx_gui.h"
13 #include "debug.h"
14 #include "lfuns.h"
15 #include "dimension.h"
16
17 #include <map>
18
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
56 namespace {
57
58 /*
59  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
60  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4 = square polyline
61  */
62
63
64 double const parenthHigh[] = {
65         2, 13,
66         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
67         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
68         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
69         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
70         0.9840, 0.9986,
71         0
72 };
73
74
75 double const parenth[] = {
76         2, 13,
77         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
78         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
79         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
80         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
81         0.9930, 0.9919,
82         0
83 };
84
85
86 double const brace[] = {
87         2, 21,
88         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
89         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
90         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
91         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
92         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
93         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
94         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
95         0
96 };
97
98
99 double const arrow[] = {
100         4, 7,
101         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
102         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
103         0.9500, 0.7500,
104         3, 0.5000, 0.1500, 0.5000, 0.9500,
105         0
106 };
107
108
109 double const Arrow[] = {
110         4, 7,
111         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
112         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
113         0.9500, 0.7500,
114         3, 0.3500, 0.5000, 0.3500, 0.9500,
115         3, 0.6500, 0.5000, 0.6500, 0.9500,
116         0
117 };
118
119
120 double const udarrow[] = {
121         2, 3,
122         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
123         2, 3,
124         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
125         1, 0.5, 0.2,  0.5, 0.8,
126         0
127 };
128
129
130 double const Udarrow[] = {
131         2, 3,
132         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
133         2, 3,
134         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
135         1, 0.35, 0.2, 0.35, 0.8,
136         1, 0.65, 0.2, 0.65, 0.8,
137         0
138 };
139
140
141 double const brack[] = {
142         2, 4,
143         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
144         0
145 };
146
147
148 double const corner[] = {
149         2, 3,
150         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
151         0
152 };
153
154
155 double const angle[] = {
156         2, 3,
157         1, 0,  0.05, 0.5,  1, 1,
158         0
159 };
160
161
162 double const slash[] = {
163         1, 0.95, 0.05, 0.05, 0.95,
164         0
165 };
166
167
168 double const hline[] = {
169         1, 0.00, 0.5, 1.0, 0.5,
170         0
171 };
172
173
174 double const ddot[] = {
175         1, 0.2, 0.5,  0.3, 0.5,
176         1, 0.7, 0.5,  0.8, 0.5,
177         0
178 };
179
180
181 double const dddot[] = {
182         1, 0.1, 0.5,  0.2, 0.5,
183         1, 0.45, 0.5, 0.55, 0.5,
184         1, 0.8, 0.5,  0.9, 0.5,
185         0
186 };
187
188
189 double const hline3[] = {
190         1, 0.1,   0,  0.15,  0,
191         1, 0.475, 0,  0.525, 0,
192         1, 0.85,  0,  0.9,   0,
193         0
194 };
195
196
197 double const dline3[] = {
198         1, 0.1,   0.1,   0.15,  0.15,
199         1, 0.475, 0.475, 0.525, 0.525,
200         1, 0.85,  0.85,  0.9,   0.9,
201         0
202 };
203
204
205 double const hlinesmall[] = {
206         1, 0.4, 0.5, 0.6, 0.5,
207         0
208 };
209
210
211 double const ring[] = {
212         2, 5,
213         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
214         0
215 };
216
217
218 double const vert[] = {
219         1, 0.5, 0.05,  0.5, 0.95,
220         0
221 };
222
223
224 double const  Vert[] = {
225         1, 0.3, 0.05,  0.3, 0.95,
226         1, 0.7, 0.05,  0.7, 0.95,
227         0
228 };
229
230
231 double const tilde[] = {
232         2, 4,
233         0.00, 0.8,  0.25, 0.2,  0.75, 0.8,  1.00, 0.2,
234         0
235 };
236
237
238 struct deco_struct {
239         double const * data;
240         int angle;
241 };
242
243 struct named_deco_struct {
244         char const * name;
245         double const * data;
246         int angle;
247 };
248
249 named_deco_struct deco_table[] = {
250         // Decorations
251         {"widehat",             angle,    3 },
252         {"widetilde",           tilde,    0 },
253         {"underbar",            hline,    0 },
254         {"underline",           hline,    0 },
255         {"overline",            hline,    0 },
256         {"underbrace",          brace,    1 },
257         {"overbrace",           brace,    3 },
258         {"overleftarrow",       arrow,    1 },
259         {"overrightarrow",      arrow,    3 },
260         {"overleftrightarrow",  udarrow,  1 },
261         {"xleftarrow",          arrow,    1 },
262         {"xrightarrow",         arrow,    3 },
263         {"underleftarrow",      arrow,    1 },
264         {"underrightarrow",     arrow,    3 },
265         {"underleftrightarrow", udarrow,  1 },
266
267         // Delimiters
268         {"(",              parenth,    0 },
269         {")",              parenth,    2 },
270         {"{",              brace,      0 },
271         {"}",              brace,      2 },
272         {"[",              brack,      0 },
273         {"]",              brack,      2 },
274         {"|",              vert,       0 },
275         {"/",              slash,      0 },
276         {"vert",           vert,       0 },
277         {"Vert",           Vert,       0 },
278         {"'",              slash,      1 },
279         {"backslash",      slash,      1 },
280         {"langle",         angle,      0 },
281         {"lceil",          corner,     0 },
282         {"lfloor",         corner,     1 },
283         {"rangle",         angle,      2 },
284         {"rceil",          corner,     3 },
285         {"rfloor",         corner,     2 },
286         {"downarrow",      arrow,      2 },
287         {"Downarrow",      Arrow,      2 },
288         {"uparrow",        arrow,      0 },
289         {"Uparrow",        Arrow,      0 },
290         {"updownarrow",    udarrow,    0 },
291         {"Updownarrow",    Udarrow,    0 },
292
293         // Accents
294         {"ddot",           ddot,       0 },
295         {"dddot",          dddot,      0 },
296         {"hat",            angle,      3 },
297         {"grave",          slash,      1 },
298         {"acute",          slash,      0 },
299         {"tilde",          tilde,      0 },
300         {"bar",            hline,      0 },
301         {"dot",            hlinesmall, 0 },
302         {"check",          angle,      1 },
303         {"breve",          parenth,    1 },
304         {"vec",            arrow,      3 },
305         {"mathring",       ring,       0 },
306
307         // Dots
308         {"dots",           hline3,     0 },
309         {"ldots",          hline3,     0 },
310         {"cdots",          hline3,     0 },
311         {"vdots",          hline3,     1 },
312         {"ddots",          dline3,     0 },
313         {"dotsb",          hline3,     0 },
314         {"dotsc",          hline3,     0 },
315         {"dotsi",          hline3,     0 },
316         {"dotsm",          hline3,     0 },
317         {"dotso",          hline3,     0 }
318 };
319
320
321 std::map<string, deco_struct> deco_list;
322
323 // sort the table on startup
324 struct init_deco_table {
325         init_deco_table() {
326                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
327                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
328                         deco_struct d;
329                         d.data  = p->data;
330                         d.angle = p->angle;
331                         deco_list[p->name]= d;
332                 }
333         }
334 };
335
336 static init_deco_table dummy;
337
338
339 deco_struct const * search_deco(string const & name)
340 {
341         std::map<string, deco_struct>::const_iterator p = deco_list.find(name);
342         return (p == deco_list.end()) ? 0 : &(p->second);
343 }
344
345
346 } // namespace anon
347
348
349 void mathed_char_dim(LyXFont const & font, unsigned char c, Dimension & dim)
350 {
351         dim.d = font_metrics::descent(c, font);
352         dim.a = font_metrics::ascent(c, font);
353         dim.w = mathed_char_width(font, c);
354 }
355
356
357 int mathed_char_ascent(LyXFont const & font, unsigned char c)
358 {
359         return font_metrics::ascent(c, font);
360 }
361
362
363 int mathed_char_descent(LyXFont const & font, unsigned char c)
364 {
365         return font_metrics::descent(c, font);
366 }
367
368
369 int mathed_char_width(LyXFont const & font, unsigned char c)
370 {
371         return font_metrics::width(c, font);
372 }
373
374
375 void mathed_string_dim(LyXFont const & font, string const & s, Dimension & dim)
376 {
377 #if 1
378         dim.a = 0;
379         dim.d = 0;
380         for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
381                 dim.a = max(dim.a, font_metrics::ascent(*it, font));
382                 dim.d = max(dim.d, font_metrics::descent(*it, font));
383         }
384 #else
385         dim.a = font_metrics::maxAscent(font);
386         dim.d = font_metrics::maxDescent(font);
387 #endif
388         dim.w = font_metrics::width(s, font);
389 }
390
391
392 int mathed_string_width(LyXFont const & font, string const & s)
393 {
394         return font_metrics::width(s, font);
395 }
396
397
398 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
399         const string & name)
400 {
401         if (name == ".") {
402                 pi.pain.line(x + w/2, y, x + w/2, y + h,
403                           LColor::cursor, Painter::line_onoffdash);
404                 return;
405         }
406
407         deco_struct const * mds = search_deco(name);
408         if (!mds) {
409                 lyxerr << "Deco was not found. Programming error?\n";
410                 lyxerr << "name: '" << name << "'\n";
411                 return;
412         }
413
414         int const n = (w < h) ? w : h;
415         int const r = mds->angle;
416         double const * d = mds->data;
417
418         if (h > 70 && (name == "(" || name == ")"))
419                 d = parenthHigh;
420
421         Matrix mt(r, w, h);
422         Matrix sqmt(r, n, n);
423
424         if (r > 0 && r < 3)
425                 y += h;
426
427         if (r >= 2)
428                 x += w;
429
430         for (int i = 0; d[i]; ) {
431                 int code = int(d[i++]);
432                 if (code & 1) {  // code == 1 || code == 3
433                         double xx = d[i++];
434                         double yy = d[i++];
435                         double x2 = d[i++];
436                         double y2 = d[i++];
437                         if (code == 3)
438                                 sqmt.transform(xx, yy);
439                         else
440                                 mt.transform(xx, yy);
441                         mt.transform(x2, y2);
442                         pi.pain.line(
443                                 int(x + xx + 0.5), int(y + yy + 0.5),
444                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
445                                 LColor::math);
446                 }       else {
447                         int xp[32];
448                         int yp[32];
449                         int const n = int(d[i++]);
450                         for (int j = 0; j < n; ++j) {
451                                 double xx = d[i++];
452                                 double yy = d[i++];
453 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
454                                 if (code == 4)
455                                         sqmt.transform(xx, yy);
456                                 else
457                                         mt.transform(xx, yy);
458                                 xp[j] = int(x + xx + 0.5);
459                                 yp[j] = int(y + yy + 0.5);
460                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
461                         }
462                         pi.pain.lines(xp, yp, n, LColor::math);
463                 }
464         }
465 }
466
467
468 void mathed_draw_framebox(PainterInfo & pi, int x, int y, MathInset const * p)
469 {
470         if (mathcursor && mathcursor->isInside(p))
471                 pi.pain.rectangle(x, y - p->ascent(), p->width(), p->height(),
472                         LColor::mathframe);
473 }
474
475
476 // In the future maybe we use a better fonts renderer
477 void drawStr(PainterInfo & pi, LyXFont const & font,
478         int x, int y, string const & str)
479 {
480         pi.pain.text(x, y, str, font);
481 }
482
483
484 void drawStrRed(PainterInfo & pi, int x, int y, string const & str)
485 {
486         LyXFont f = pi.base.font;
487         f.setColor(LColor::latex);
488         pi.pain.text(x, y, str, f);
489 }
490
491
492 void drawStrBlack(PainterInfo & pi, int x, int y, string const & str)
493 {
494         LyXFont f = pi.base.font;
495         f.setColor(LColor::foreground);
496         pi.pain.text(x, y, str, f);
497 }
498
499
500 void drawChar(PainterInfo & pi, LyXFont const & font, int x, int y, char c)
501 {
502         pi.pain.text(x, y, c, font);
503 }
504
505
506 void math_font_max_dim(LyXFont const & font, int & asc, int & des)
507 {
508         asc = font_metrics::maxAscent(font);
509         des = font_metrics::maxDescent(font);
510 }
511
512
513 struct fontinfo {
514         string cmd_;
515         LyXFont::FONT_FAMILY family_;
516         LyXFont::FONT_SERIES series_;
517         LyXFont::FONT_SHAPE  shape_;
518         LColor::color        color_;
519 };
520
521
522 LyXFont::FONT_FAMILY const inh_family = LyXFont::INHERIT_FAMILY;
523 LyXFont::FONT_SERIES const inh_series = LyXFont::INHERIT_SERIES;
524 LyXFont::FONT_SHAPE  const inh_shape  = LyXFont::INHERIT_SHAPE;
525
526
527 // mathnormal should be the first, otherwise the fallback further down
528 // does not work
529 fontinfo fontinfos[] = {
530         // math fonts
531         {"mathnormal",    inh_family, LyXFont::MEDIUM_SERIES,
532                           LyXFont::UP_SHAPE, LColor::math},
533         {"mathbf",        inh_family, LyXFont::BOLD_SERIES,
534                           inh_shape, LColor::math},
535         {"mathcal",       LyXFont::CMSY_FAMILY, inh_series,
536                           inh_shape, LColor::math},
537         {"mathfrak",      LyXFont::EUFRAK_FAMILY, inh_series,
538                           inh_shape, LColor::math},
539         {"mathrm",        LyXFont::ROMAN_FAMILY, inh_series,
540                           inh_shape, LColor::math},
541         {"mathsf",        LyXFont::SANS_FAMILY, inh_series,
542                           inh_shape, LColor::math},
543         {"mathbb",        LyXFont::MSB_FAMILY, inh_series,
544                           inh_shape, LColor::math},
545         {"mathtt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
546                           inh_shape, LColor::math},
547         {"cmex",          LyXFont::CMEX_FAMILY, inh_series,
548                           inh_shape, LColor::none},
549         {"cmm",           LyXFont::CMM_FAMILY, inh_series,
550                           inh_shape, LColor::none},
551         {"cmr",           LyXFont::CMR_FAMILY, inh_series,
552                           inh_shape, LColor::none},
553         {"cmsy",          LyXFont::CMSY_FAMILY, inh_series,
554                           inh_shape, LColor::none},
555         {"eufrak",        LyXFont::EUFRAK_FAMILY, inh_series,
556                           inh_shape, LColor::none},
557         {"msa",           LyXFont::MSA_FAMILY, inh_series,
558                           inh_shape, LColor::none},
559         {"msb",           LyXFont::MSB_FAMILY, inh_series,
560                           inh_shape, LColor::none},
561         {"wasy",          LyXFont::WASY_FAMILY, inh_series,
562                           inh_shape, LColor::none},
563
564         // Text fonts
565         {"text",          inh_family, inh_series,
566                           inh_shape, LColor::foreground},
567         {"textbf",        inh_family, LyXFont::BOLD_SERIES,
568                           inh_shape, LColor::foreground},
569         {"textit",        inh_family, inh_series,
570                           LyXFont::ITALIC_SHAPE, LColor::foreground},
571         {"textmd",        inh_family, LyXFont::MEDIUM_SERIES,
572                           inh_shape, LColor::foreground},
573         {"textnormal",    inh_family, inh_series,
574                           LyXFont::UP_SHAPE, LColor::foreground},
575         {"textrm",        LyXFont::ROMAN_FAMILY,
576                           inh_series,LyXFont::UP_SHAPE,LColor::foreground},
577         {"textsc",        inh_family, inh_series,
578                           LyXFont::SMALLCAPS_SHAPE, LColor::foreground},
579         {"textsf",        LyXFont::SANS_FAMILY, inh_series,
580                           inh_shape, LColor::foreground},
581         {"textsl",        inh_family, inh_series,
582                           LyXFont::SLANTED_SHAPE, LColor::foreground},
583         {"texttt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
584                           inh_shape, LColor::foreground},
585         {"textup",        inh_family, inh_series,
586                           LyXFont::UP_SHAPE, LColor::foreground},
587
588         // TIPA support
589         {"textipa",       inh_family, inh_series,
590                           inh_shape, LColor::foreground},
591
592         // LyX internal usage
593         {"lyxtex",        inh_family, inh_series,
594                           inh_shape, LColor::latex},
595         {"lyxert",        LyXFont::TYPEWRITER_FAMILY, inh_series,
596                           inh_shape, LColor::latex},
597         {"lyxsymbol",     LyXFont::SYMBOL_FAMILY, inh_series,
598                           inh_shape, LColor::math},
599         {"lyxboldsymbol", LyXFont::SYMBOL_FAMILY, LyXFont::BOLD_SERIES,
600                           inh_shape, LColor::math},
601         {"lyxitsymbol",   LyXFont::SYMBOL_FAMILY, inh_series,
602                     LyXFont::ITALIC_SHAPE, LColor::math},
603         {"lyxblacktext",  LyXFont::ROMAN_FAMILY, LyXFont::MEDIUM_SERIES,
604                     LyXFont::UP_SHAPE, LColor::foreground},
605         {"lyxnochange",   inh_family, inh_series,
606                     inh_shape, LColor::foreground},
607         {"lyxfakebb",     LyXFont::TYPEWRITER_FAMILY, LyXFont::BOLD_SERIES,
608                           LyXFont::UP_SHAPE, LColor::math},
609         {"lyxfakecal",    LyXFont::SANS_FAMILY, LyXFont::MEDIUM_SERIES,
610                           LyXFont::ITALIC_SHAPE, LColor::math},
611         {"lyxfakefrak",   LyXFont::ROMAN_FAMILY, LyXFont::BOLD_SERIES,
612                           LyXFont::ITALIC_SHAPE, LColor::math}
613 };
614
615
616 fontinfo * lookupFont(string const & name)
617 {
618         //lyxerr << "searching font '" << name << "'\n";
619         int const n = sizeof(fontinfos) / sizeof(fontinfo);
620         for (int i = 0; i < n; ++i)
621                 if (fontinfos[i].cmd_ == name) {
622                         //lyxerr << "found '" << i << "'\n";
623                         return fontinfos + i;
624                 }
625         return 0;
626 }
627
628
629 fontinfo * searchFont(string const & name)
630 {
631         fontinfo * f = lookupFont(name);
632         return f ? f : fontinfos;
633         // this should be mathnormal
634         //return searchFont("mathnormal");
635 }
636
637
638 bool isFontName(string const & name)
639 {
640         return lookupFont(name);
641 }
642
643
644 LyXFont getFont(string const & name)
645 {
646         LyXFont font;
647         augmentFont(font, name);
648         return font;
649 }
650
651
652 void fakeFont(string const & orig, string const & fake)
653 {
654         fontinfo * forig = searchFont(orig);
655         fontinfo * ffake = searchFont(fake);
656         if (forig && ffake) {
657                 forig->family_ = ffake->family_;
658                 forig->series_ = ffake->series_;
659                 forig->shape_  = ffake->shape_;
660                 forig->color_  = ffake->color_;
661         } else {
662                 lyxerr << "Can't fake font '" << orig << "' with '" << fake << "'\n";
663         }
664 }
665
666
667 void augmentFont(LyXFont & font, string const & name)
668 {
669         static bool initialized = false;
670         if (!initialized) {
671                 initialized = true;
672                 // fake fonts if necessary
673                 if (!lyx_gui::font_available(getFont("mathfrak")))
674                         fakeFont("mathfrak", "lyxfakefrak");
675                 if (!lyx_gui::font_available(getFont("mathcal")))
676                         fakeFont("mathcal", "lyxfakecal");
677         }
678         fontinfo * info = searchFont(name);
679         if (info->family_ != inh_family)
680                 font.setFamily(info->family_);
681         if (info->series_ != inh_series)
682                 font.setSeries(info->series_);
683         if (info->shape_ != inh_shape)
684                 font.setShape(info->shape_);
685         if (info->color_ != LColor::none)
686                 font.setColor(info->color_);
687 }