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