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