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