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