]> git.lyx.org Git - lyx.git/blob - src/mathed/support.C
iookup unification - another 100 lines bite the dust
[lyx.git] / src / mathed / support.C
1 #include <config.h>
2
3 #include <algorithm>
4
5 #include "mathed/support.h"
6 #include "lyxfont.h"
7 #include "font.h"
8 #include "math_defs.h"
9 #include "math_parser.h"
10 #include "Painter.h"
11 #include "debug.h"
12
13 using std::sort;
14 using std::lower_bound;
15 using std::endl;
16 using std::max;
17
18
19 bool MathIsInset(MathTextCodes x)
20 {
21         return LM_TC_INSET == x;
22 }
23
24
25 bool MathIsAlphaFont(MathTextCodes x)
26 {
27         return LM_TC_VAR <= x && x <= LM_TC_TEXTRM;
28 }
29
30
31 bool MathIsBinary(MathTextCodes x)
32 {
33         return x == LM_TC_BOP || x == LM_TC_BOPS;
34 }
35
36
37 bool MathIsSymbol(MathTextCodes x)
38 {
39         return x == LM_TC_SYMB || x == LM_TC_BOPS || x == LM_TC_BSYM;
40 }
41      
42
43
44 ///
45 class Matrix {
46 public:
47         ///
48         typedef float matriz_data[2][2];
49         ///
50         Matrix();
51         ///
52         void rotate(int);
53         ///
54         void escalate(float, float);
55         ///
56         void transform(float, float, float &, float &);
57 private:
58         ///
59         matriz_data m_;
60         ///
61         void multiply(matriz_data & a);
62 };
63
64 Matrix::Matrix()
65 {
66         m_[0][0] = 1;
67         m_[0][1] = 0;
68         m_[1][0] = 0;
69         m_[1][1] = 1;
70 }
71
72 void Matrix::rotate(int code)
73 {
74         matriz_data r;
75         r[0][0] = 1;
76         r[0][1] = 0;
77         r[1][0] = 0;
78         r[1][1] = 1;
79         float const cs = (code & 1) ? 0 : (1 - code);
80         float const sn = (code & 1) ? (2 - code) : 0;
81         r[0][0] = cs;
82         r[0][1] = sn;
83         r[1][0] = -r[0][1];
84         r[1][1] = r[0][0];
85         multiply(r);
86 }
87
88 void Matrix::escalate(float x, float y)
89 {
90         matriz_data s;
91         s[0][0] = x;
92         s[0][1] = 0;
93         s[1][0] = 0;
94         s[1][1] = y;
95         multiply(s);
96 }
97
98 void Matrix::multiply(matriz_data & a)
99 {
100         matriz_data c;
101         c[0][0] = a[0][0] * m_[0][0] + a[0][1] * m_[1][0];
102         c[1][0] = a[1][0] * m_[0][0] + a[1][1] * m_[1][0];
103         c[0][1] = a[0][0] * m_[0][1] + a[0][1] * m_[1][1];
104         c[1][1] = a[1][0] * m_[0][1] + a[1][1] * m_[1][1];
105         m_[0][0] = c[0][0];     
106         m_[0][1] = c[0][1];     
107         m_[1][0] = c[1][0];     
108         m_[1][1] = c[1][1];     
109 }
110
111 void Matrix::transform(float xp, float yp, float & x, float & y)
112 {
113         x = m_[0][0] * xp + m_[0][1] * yp;
114         y = m_[1][0] * xp + m_[1][1] * yp;
115 }
116
117
118 namespace {
119
120 LyXFont           * Math_Fonts = 0;
121
122 void mathed_init_fonts()
123 {
124         Math_Fonts = new LyXFont[8]; //DEC cxx cannot initialize all fonts
125         //at once (JMarc) rc
126
127         for (int i = 0 ; i < 8 ; ++i) {
128                 Math_Fonts[i] = LyXFont(LyXFont::ALL_SANE);
129         }
130
131         Math_Fonts[0].setShape(LyXFont::ITALIC_SHAPE);
132
133         Math_Fonts[1].setFamily(LyXFont::SYMBOL_FAMILY);
134
135         Math_Fonts[2].setFamily(LyXFont::SYMBOL_FAMILY);
136         Math_Fonts[2].setShape(LyXFont::ITALIC_SHAPE);
137
138         Math_Fonts[3].setSeries(LyXFont::BOLD_SERIES);
139
140         Math_Fonts[4].setFamily(LyXFont::SANS_FAMILY);
141         Math_Fonts[4].setShape(LyXFont::ITALIC_SHAPE);
142
143         Math_Fonts[5].setFamily(LyXFont::TYPEWRITER_FAMILY);
144
145         Math_Fonts[6].setFamily(LyXFont::ROMAN_FAMILY);
146
147         Math_Fonts[7].setFamily(LyXFont::SANS_FAMILY);
148 }
149
150 } // namespace
151
152
153 LyXFont WhichFont(MathTextCodes type, MathStyles size)
154 {
155         LyXFont f;
156         
157         if (!Math_Fonts)
158                 mathed_init_fonts();
159
160         switch (type) {
161         case LM_TC_SYMB:        
162                 f = Math_Fonts[2];
163                 break;
164
165         case LM_TC_BSYM:        
166                 f = Math_Fonts[2];
167                 break;
168
169         case LM_TC_VAR:
170         case LM_TC_IT:
171                 f = Math_Fonts[0];
172                 break;
173
174         case LM_TC_BF:
175                 f = Math_Fonts[3];
176                 break;
177
178         case LM_TC_SF:
179                 f = Math_Fonts[7];
180                 break;
181
182         case LM_TC_CAL:
183                 f = Math_Fonts[4];
184                 break;
185
186         case LM_TC_TT:
187                 f = Math_Fonts[5];
188                 break;
189
190         case LM_TC_SPECIAL: //f = Math_Fonts[0]; break;
191         case LM_TC_TEXTRM:
192         case LM_TC_TEX:
193         case LM_TC_RM:
194                 f = Math_Fonts[6];
195                 break;
196
197         default:
198                 f = Math_Fonts[1];
199                 break;
200         }
201
202         switch (size) {
203         case LM_ST_DISPLAY:
204                 if (type == LM_TC_BSYM) {
205                         f.incSize();
206                         f.incSize();
207                 }
208                 break;
209
210         case LM_ST_TEXT:
211                 break;
212
213         case LM_ST_SCRIPT:
214                 f.decSize();
215                 f.decSize();
216                 break;
217
218         case LM_ST_SCRIPTSCRIPT:
219                 f.decSize();
220                 f.decSize();
221                 f.decSize();
222                 break;
223
224         default:
225                 lyxerr << "Math Error: wrong font size: " << size << endl;
226                 break;
227         }
228
229         if (type != LM_TC_TEXTRM)
230                 f.setColor(LColor::math);
231
232         if (type == LM_TC_TEX)
233                 f.setColor(LColor::latex);
234
235         return f;
236 }
237
238 char const * math_font_name[] = {
239         "mathrm",
240         "mathcal",
241         "mathbf",
242         "mathsf",
243         "mathtt",
244         "mathit",
245         "textrm"
246 };
247
248
249 char const * latex_mathspace[] = {
250         "!", ",", ":", ";", "quad", "qquad"
251 };
252
253
254 namespace {
255
256 /* 
257  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
258  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4= square polyline
259  */
260
261
262 float const parenthHigh[] = {
263         2.0, 13.0,
264         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
265         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
266         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
267         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
268         0.9840, 0.9986,
269         0.0 
270 };
271
272
273 float const parenth[] = {
274         2.0, 13.0,
275         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
276         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
277         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
278         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
279         0.9930, 0.9919,
280         0.0   
281 };
282
283
284 float const brace[] = {
285         2.0, 21.0,
286         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
287         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
288         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
289         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
290         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
291         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
292         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
293         0.0
294 };
295
296
297 // Is this correct? (Lgb)
298 float const arrow[] = {
299         4, 7,
300         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
301         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
302         0.9500, 0.7500,
303         3, 0.5000, 0.1500, 0.5000, 0.9500,
304         0.0 
305 };
306
307
308 // Is this correct? (Lgb)
309 float const Arrow[] = {
310         4, 7,
311         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
312         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
313         0.9500, 0.7500,
314         3, 0.3500, 0.5000, 0.3500, 0.9500,
315         3, 0.6500, 0.5000, 0.6500, 0.9500,
316         0.0
317 };
318
319
320 float const udarrow[] = {
321         2, 3,
322         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
323         2, 3,
324         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,  
325         1, 0.5, 0.2,  0.5, 0.8,
326         0.0 
327 };
328
329
330 float const Udarrow[] = {
331         2, 3,
332         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
333         2, 3,
334         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,  
335         1, 0.35, 0.2, 0.35, 0.8,
336         1, 0.65, 0.2, 0.65, 0.8,
337         0.0 
338 };
339
340
341 float const brack[] = {
342         2.0, 4,
343         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
344         0.0
345 };
346
347
348 float const corner[] = {
349         2.0, 3,
350         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
351         0.0
352 };
353
354
355 float const angle[] = {
356         2.0, 3,
357         1, 0,  0.05, 0.5,  1, 1,
358         0.0
359 };
360
361
362 float const slash[] = {
363         1, 0.95, 0.05,  0.05, 0.95, 
364         0.0
365 };
366
367
368 float const hline[] = {
369         1, 0.05, 0.5,  0.95, 0.5, 
370         0.0
371 };
372
373
374 float const hline2[] = {
375    1, 0.1, 0.5,  0.3, 0.5,
376    1, 0.7, 0.5,  0.9, 0.5,
377    0.0
378 }; 
379
380
381 float const hline3[] = {
382         1, 0.1, 0,  0.15, 0,
383         1, 0.475, 0,  0.525, 0,
384         1, 0.85, 0,  0.9, 0,  
385         0.0
386 };
387
388
389 float const dline3[] = {
390         1, 0.1, 0.1,  0.15, 0.15,
391         1, 0.475, 0.475,  0.525, 0.525,
392         1, 0.85, 0.85,  0.9, 0.9,
393         0.0
394 };     
395
396
397 float const hlinesmall[] = {
398         1, 0.4, 0.5,  0.6, 0.5, 
399         0.0
400 };
401
402
403 float const vert[] = {
404         1, 0.5, 0.05,  0.5, 0.95, 
405         0.0
406 };
407
408
409 float const  Vert[] = {
410         1, 0.3, 0.05,  0.3, 0.95, 
411         1, 0.7, 0.05,  0.7, 0.95,
412         0.0
413 };
414
415
416 float const tilde[] = {
417         2.0, 4,
418         0.05, 0.8,  0.25, 0.2,  0.75, 0.8,  0.95, 0.2,
419         0.0
420 };
421
422
423 struct math_deco_struct {
424         int code;
425         float const * data;
426         int angle;
427 };
428
429 math_deco_struct math_deco_table[] = {   
430         // Decorations
431         { LM_widehat,       &angle[0],      3 },
432         { LM_widetilde,     &tilde[0],      0 },
433         { LM_underline,     &hline[0],      0 },
434         { LM_overline,      &hline[0],      0 },
435         { LM_underbrace,    &brace[0],      1 },
436         { LM_overbrace,     &brace[0],      3 },
437         { LM_overleftarrow, &arrow[0],      1 },
438         { LM_overightarrow, &arrow[0],      3 },
439                                             
440         // Delimiters                       
441         { '(',              &parenth[0],    0 },
442         { ')',              &parenth[0],    2 },
443         { '{',              &brace[0],      0 },
444         { '}',              &brace[0],      2 },
445         { '[',              &brack[0],      0 },
446         { ']',              &brack[0],      2 },
447         { '|',              &vert[0],       0 },
448         { '/',              &slash[0],      0 },
449         { LM_Vert,          &Vert[0],       0 },
450         { LM_backslash,     &slash[0],      1 },
451         { LM_langle,        &angle[0],      0 },
452         { LM_lceil,         &corner[0],     0 }, 
453         { LM_lfloor,        &corner[0],     1 },  
454         { LM_rangle,        &angle[0],      2 }, 
455         { LM_rceil,         &corner[0],     3 }, 
456         { LM_rfloor,        &corner[0],     2 },
457         { LM_downarrow,     &arrow[0],      2 },
458         { LM_Downarrow,     &Arrow[0],      2 }, 
459         { LM_uparrow,       &arrow[0],      0 },
460         { LM_Uparrow,       &Arrow[0],      0 },
461         { LM_updownarrow,   &udarrow[0],    0 },
462         { LM_Updownarrow,   &Udarrow[0],    0 },         
463                                             
464         // Accents                          
465         { LM_ddot,          &hline2[0],     0 },
466         { LM_hat,           &angle[0],      3 },
467         { LM_grave,         &slash[0],      1 },
468         { LM_acute,         &slash[0],      0 },
469         { LM_tilde,         &tilde[0],      0 },
470         { LM_bar,           &hline[0],      0 },
471         { LM_dot,           &hlinesmall[0], 0 },
472         { LM_check,         &angle[0],      1 },
473         { LM_breve,         &parenth[0],    1 },
474         { LM_vec,           &arrow[0],      3 },
475         { LM_not,           &slash[0],      0 },  
476                                             
477         // Dots                             
478         { LM_ldots,         &hline3[0],     0 }, 
479         { LM_cdots,         &hline3[0],     0 },
480         { LM_vdots,         &hline3[0],     1 },
481         { LM_ddots,         &dline3[0],     0 }
482 };
483
484
485 struct math_deco_compare {
486         /// for use by sort and lower_bound
487         inline
488         int operator()(math_deco_struct const & a,
489                        math_deco_struct const & b) const {
490                 return a.code < b.code;
491         }
492 };
493
494
495 int const math_deco_table_size =
496 sizeof(math_deco_table) /sizeof(math_deco_struct);
497
498
499 class init_deco_table {
500 public:
501         init_deco_table() {
502                 if (!init) {
503                         sort(math_deco_table,
504                              math_deco_table + math_deco_table_size,
505                              math_deco_compare());
506                         init_deco_table::init = true;
507                 }
508         }
509 private:
510         static bool init;
511 };
512
513
514 bool init_deco_table::init = false;
515 static init_deco_table idt;
516
517 } // namespace anon
518
519 void mathed_char_dim (MathTextCodes type, MathStyles size, unsigned char c,
520         int & asc, int & des, int & wid)
521 {
522         LyXFont const font = WhichFont(type, size);
523         des = lyxfont::descent(c, font);
524         asc = lyxfont::ascent(c, font);
525         wid = mathed_char_width(type, size, c);
526 }
527
528 int mathed_char_height(MathTextCodes type, MathStyles size, unsigned char c,
529         int & asc, int & des)
530 {
531         LyXFont const font = WhichFont(type, size);
532         des = lyxfont::descent(c, font);
533         asc = lyxfont::ascent(c, font);
534         return asc + des;
535 }
536
537
538 int mathed_char_width(MathTextCodes type, MathStyles size, unsigned char c)
539 {
540         if (MathIsBinary(type)) {
541                 string s;
542                 s += c;
543                 return mathed_string_width(type, size, s);
544         } else
545                 return lyxfont::width(c, WhichFont(type, size));
546 }
547
548
549 void mathed_string_dim(MathTextCodes type, MathStyles size, string const & s,
550                          int & asc, int & des, int & wid)
551 {
552         mathed_string_height(type, size, s, asc, des);
553         wid = mathed_string_width(type, size, s);
554 }
555
556 int mathed_string_height(MathTextCodes type, MathStyles size, string const & s,
557                          int & asc, int & des)
558 {
559         LyXFont const font = WhichFont(type, size);
560         asc = des = 0;
561         for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
562                 des = max(des, lyxfont::descent(*it, font));
563                 asc = max(asc, lyxfont::ascent(*it, font));
564         }
565         return asc + des;
566 }
567
568
569 int mathed_string_width(MathTextCodes type, MathStyles size, string const & s)
570 {
571         string st;
572         if (MathIsBinary(type))
573                 for (string::const_iterator it = s.begin();
574                      it != s.end(); ++it) {
575                         st += ' ';
576                         st += *it;
577                         st += ' ';
578                 }
579         else
580                 st = s;
581         
582         return lyxfont::width(st, WhichFont(type, size));
583 }
584
585
586 namespace {
587
588 math_deco_struct const * search_deco(int code)
589 {
590         math_deco_struct search_elem = { code, 0, 0 };
591         
592         math_deco_struct const * res =
593                 lower_bound(math_deco_table,
594                             math_deco_table + math_deco_table_size,
595                             search_elem, math_deco_compare());
596         if (res != math_deco_table + math_deco_table_size &&
597             res->code == code)
598                 return res;
599         return 0;
600 }
601
602 }
603
604 void mathed_draw_deco(Painter & pain, int x, int y, int w, int h, int code)
605 {
606         Matrix mt;
607         Matrix sqmt;
608         float xx;
609         float yy;
610         float x2;
611         float y2;
612         int i = 0;
613         
614         math_deco_struct const * mds = search_deco(code);
615         if (!mds) {
616                 // Should this ever happen?
617                 lyxerr << "Deco was not found. Programming error?" << endl;
618                 return;
619         }
620         
621         int const r = mds->angle;
622         float const * d = mds->data;
623         
624         if (h > 70 && (mds->code == int('(') || mds->code == int(')')))
625                 d = parenthHigh;
626         
627         mt.rotate(r);
628         mt.escalate(w, h);
629         
630         int const n = (w < h) ? w : h;
631         sqmt.rotate(r);
632         sqmt.escalate(n, n);
633         if (r > 0 && r < 3) y += h;   
634         if (r >= 2) x += w;   
635         do {
636                 code = int(d[i++]);
637                 switch (code) {
638                 case 0: break;
639                 case 1: 
640                 case 3:
641                 {
642                         xx = d[i++]; yy = d[i++];
643                         x2 = d[i++]; y2 = d[i++];
644                         if (code == 3) 
645                                 sqmt.transform(xx, yy, xx, yy);
646                         else
647                                 mt.transform(xx, yy, xx, yy);
648                         mt.transform(x2, y2, x2, y2);
649                         pain.line(x + int(xx), y + int(yy),
650                                   x + int(x2), y + int(y2),
651                                   LColor::mathline);
652                         break;
653                 }        
654                 case 2: 
655                 case 4:
656                 {
657                         int xp[32];
658                         int yp[32];
659                         int const n = int(d[i++]);
660                         for (int j = 0; j < n; ++j) {
661                                 xx = d[i++]; yy = d[i++];
662 //           lyxerr << " " << xx << " " << yy << " ";
663                                 if (code == 4) 
664                                         sqmt.transform(xx, yy, xx, yy);
665                                 else
666                                         mt.transform(xx, yy, xx, yy);
667                                 xp[j] = x + int(xx);
668                                 yp[j] = y + int(yy);
669                                 //  lyxerr << "P[" << j " " << xx << " " << yy << " " << x << " " << y << "]";
670                         }
671                         pain.lines(xp, yp, n, LColor::mathline);
672                 }
673                 }
674         } while (code);
675 }
676
677
678
679 // In a near future maybe we use a better fonts renderer
680 void drawStr(Painter & pain, MathTextCodes type, MathStyles siz,
681         int x, int y, string const & s)
682 {
683         string st;
684         if (MathIsBinary(type))
685                 for (string::const_iterator it = s.begin();
686                      it != s.end(); ++it) {
687                         st += ' ';
688                         st += *it;
689                         st += ' ';
690                 }
691         else
692                 st = s;
693         
694         pain.text(x, y, st, WhichFont(type, siz));
695 }
696
697 void drawChar(Painter & pain, MathTextCodes type, MathStyles siz, int x, int y, char c)
698 {
699         string s;
700         s += c;
701         drawStr(pain, type, siz, x, y, s);
702 }
703
704 // decrease math size for super- and subscripts
705 MathStyles smallerStyleScript(MathStyles st)
706 {
707         switch (st) {
708                 case LM_ST_DISPLAY:
709                 case LM_ST_TEXT:    st = LM_ST_SCRIPT; break;
710                 default:            st = LM_ST_SCRIPTSCRIPT;
711         }
712         return st;
713 }
714
715 // decrease math size for fractions
716 MathStyles smallerStyleFrac(MathStyles st)
717 {
718         switch (st) {
719                 case LM_ST_DISPLAY: st = LM_ST_TEXT; break;
720                 case LM_ST_TEXT:    st = LM_ST_SCRIPT; break;
721                 default:            st = LM_ST_SCRIPTSCRIPT;
722         }
723         return st;
724 }
725
726 bool MathIsRelOp(unsigned char c, MathTextCodes f)
727 {
728         if (f == LM_TC_BOP && (c == '=' || c == '<' || c == '>'))
729                 return true;
730 #ifndef WITH_WARNINGS
731 #warning implement me properly
732 #endif
733         if (f == LM_TC_SYMB && (c == LM_leq || c == LM_geq))
734                 return true;
735         return false;
736 }
737
738
739 void math_font_max_dim(MathTextCodes code, MathStyles siz, int & asc, int & des)
740 {
741         LyXFont font = WhichFont(code, siz);
742         asc = lyxfont::maxAscent(font);
743         des = lyxfont::maxDescent(font);
744 }
745