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