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