]> git.lyx.org Git - lyx.git/blob - src/insets/insetlatexaccent.C
The markDirty() and fitCursor() changes
[lyx.git] / src / insets / insetlatexaccent.C
1 /**
2  * \file insetlatexaccent.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS
9  */
10
11 #include <config.h>
12
13
14 #include "insetlatexaccent.h"
15 #include "debug.h"
16 #include "lyxrc.h"
17 #include "support/lstrings.h"
18 #include "BufferView.h"
19 #include "frontends/Painter.h"
20 #include "frontends/font_metrics.h"
21 #include "language.h"
22 #include "lyxlex.h"
23
24 using std::ostream;
25 using std::endl;
26
27 /* LatexAccent. Proper handling of accented characters */
28 /* This part is done by Ivan Schreter, schreter@ccsun.tuke.sk */
29 /* Later modified by Lars G. Bjønnes, larsbj@lyx.org */
30
31 InsetLatexAccent::InsetLatexAccent()
32         : candisp(false)
33 {}
34
35
36 InsetLatexAccent::InsetLatexAccent(string const & str)
37         : contents(str)
38 {
39         checkContents();
40 }
41
42
43 void InsetLatexAccent::checkContents()
44         // check, if we know the modifier and can display it ok on screen
45 {
46         candisp = false;
47
48         if (contents.empty() || contents.length() < 2) {
49                 lyxerr[Debug::KEY] << "Cannot decode: " << contents << endl;
50                 return;
51         }
52
53         // REMOVE IN 0.13
54         // Dirty Hack for backward compability. remove in 0.13 (Lgb)
55         contents = trim(contents);
56         if (!contains(contents, "{") && !contains(contents, "}")) {
57                 if (contents.length() == 2) {
58                         string tmp;
59                         tmp += contents[0];
60                         tmp += contents[1];
61                         tmp += "{}";
62                         contents = tmp;
63                 } else if (contents.length() == 3) {
64                         string tmp;
65                         tmp += contents[0];
66                         tmp += contents[1];
67                         tmp += '{';
68                         tmp += contents[2];
69                         tmp += '}';
70                         contents = tmp;
71                 } else if (contents.length() == 4 && contents[2] == ' ') {
72                         string tmp;
73                         tmp += contents[0];
74                         tmp += contents[1];
75                         tmp += '{';
76                         tmp += contents[3];
77                         tmp += '}';
78                         contents = tmp;
79                 } else if  (contents.length() == 4 && contents[2] == '\\'
80                             && (contents[3] == 'i' || contents[3] == 'j')) {
81                         string tmp;
82                         tmp += contents[0];
83                         tmp += contents[1];
84                         tmp += '{';
85                         tmp += contents[2];
86                         tmp += contents[3];
87                         tmp += '}';
88                         contents = tmp;
89                 }
90         }
91         if (contents[0] != '\\') { // demand that first char is a '\\'
92                 lyxerr[Debug::KEY] << "Cannot decode: " << contents << endl;
93                 return;
94         }
95
96         lyxerr[Debug::KEY] << "Decode: " << contents << endl;
97
98         remdot = false; plusasc = false; plusdesc = false;
99
100         switch (contents[1]) { // second char should be one of these
101         case '\'':  // acute
102                 modtype = ACUTE;    // acute
103                 plusasc = true;    // at the top of character
104                 break;
105         case '`':   // grave
106                 modtype = GRAVE;    // grave
107                 plusasc = true;    // at the top
108                 break;
109         case '=':   // macron
110                 modtype = MACRON;    // macron
111                 plusasc = true;    // at the top
112                 break;
113         case '~':   // tilde
114                 modtype = TILDE;    // tilde
115                 plusasc = true;    // at the top
116                 break;
117         case 'b':   // underbar
118                 modtype = UNDERBAR;    // underbar
119                 plusdesc = true;   // at the bottom
120                 break;
121         case 'c':   // cedilla
122                 modtype = CEDILLA;    // cedilla
123                 plusdesc = true;   // at the bottom
124                 break;
125         case 'd':   // underdot
126                 modtype = UNDERDOT;    // underdot
127                 plusdesc = true;   // at the bottom
128                 break;
129         case 'r':   // circle
130                 modtype = CIRCLE;    // circle
131                 plusasc = true;    // at the top
132                 break;
133         case 't':   // tie
134                 modtype = TIE;    // tie
135                 plusasc = true;    // at the top
136                 break;
137         case 'u':   // breve
138                 modtype = BREVE;    // breve
139                 plusasc = true;    // at the top
140                 break;
141         case 'v':   // caron
142                 modtype = CARON;   // caron
143                 plusasc = true;    // at the top
144                 break;
145         case 'q':   // special caron
146                 modtype = SPECIAL_CARON;   // special caron
147                 plusasc = true;    // at the top
148                 break;
149         case 'H':   // hungarian umlaut
150                 modtype = HUNGARIAN_UMLAUT;   // hungarian umlaut
151                 plusasc = true;    // at the top
152                 break;
153         case '"':   // umlaut
154                 modtype = UMLAUT;   // umlaut
155                 plusasc = true;    // at the top
156                 break;
157         case '.':   // dot
158                 modtype = DOT;   // dot
159                 plusasc = true;    // at the top
160                 break;
161         case '^':   // circumflex
162                 modtype = CIRCUMFLEX;   // circumflex
163                 plusasc = true;    // at the top
164                 break;
165         case 'k':   // ogonek
166                 modtype = OGONEK;  // ogonek
167                 plusdesc = true;
168                 break;
169         case 'i': // dot-less-i
170                 modtype = DOT_LESS_I;  // dot-less-i
171                 plusasc = true; // at the top (not really needed)
172                 remdot = true;
173                 break;
174         case 'j': // dot-less-j
175                 modtype = DOT_LESS_J; // dot-less-j
176                 plusasc = true; // at the top (not really needed)
177                 remdot = true;
178                 break;
179         case 'l': // lslash
180                 modtype = lSLASH;
181                 plusasc = true; // at the top (not really needed)
182                 break;
183         case 'L': // lslash
184                 modtype = LSLASH;
185                 plusasc = true; // at the top (not really needed)
186                 break;
187         default:
188                 lyxerr[Debug::KEY] << "Default" << endl;
189                 // unknow accent (or something else)
190                 return;
191         }
192
193         // we demand that third char is a '{' (Lgb)
194         if (contents[2] != '{') return;
195
196         // special clause for \i{}, \j{} \l{} and \L{}
197         if ((modtype == DOT_LESS_I || modtype == DOT_LESS_J
198              || modtype == lSLASH || modtype == LSLASH)
199             && contents[3] == '}') {
200                 switch (modtype) {
201                 case DOT_LESS_I: ic = 'i'; break;
202                 case DOT_LESS_J: ic = 'j'; break;
203                 case lSLASH:     ic = 'l'; break;
204                 case LSLASH:     ic = 'L'; break;
205                 default:
206                         // if this happens something is really wrong
207                         lyxerr << "InsetLaTexAccent: weird error." << endl;
208                         break;
209                 }
210                 //ic = (modtype == DOT_LESS_J ? 'j' : 'i');
211                 lyxerr[Debug::KEY] << "Contents: [" << contents << ']'
212                                    << ", ic: " << ic
213                                    << ", top: " << plusasc
214                                    << ", bot: " << plusdesc
215                                    << ", dot: " << remdot
216                                    << ", mod: " << modtype << endl;
217                 // Special case for space
218         } else if (contents[3] == '}') {
219                 ic = ' ';
220         } else {
221                 int i = 3;
222
223                 // now get the char
224                 ic = contents[3]; // i will always be 3 here
225
226                 // ic should now be a alfa-char or '\\'
227                 if (ic == '\\') {
228                         ic = contents[++i]; // will only allow \<foo>{\i} and \<foo>{\j}
229                         if (ic == 'i' || ic == 'j')
230                                 remdot = true;
231                         else
232                                 return;
233                 } else if ((ic == 'i'|| ic == 'j') && contents[4] == '}') {
234                         // Do a rewrite: \<foo>{i} --> \<foo>{\i}
235                         string temp = contents;
236                         temp.erase(3, string::npos);
237                         temp += '\\';
238                         temp += char(ic);
239                         for (string::size_type j = 4;
240                             j < contents.length(); ++j)
241                                 temp+= contents[j];
242                         contents= temp;
243                         ++i;
244                         remdot = true;
245                 }
246
247                 // demand a '}' at the end
248                 if (contents[++i] != '}' && contents[++i]) return;
249
250                 // fine, the char is properly decoded now (hopefully)
251                 lyxerr[Debug::KEY] << "Contents: [" << contents << ']'
252                                    << ", ic: " << ic
253                                    << ", top: " << plusasc
254                                    << ", bot: " << plusdesc
255                                    << ", dot: " << remdot
256                                    << ", mod: " << modtype << endl;
257         }
258         candisp = true;
259 }
260
261
262 int InsetLatexAccent::ascent(BufferView *, LyXFont const & font) const
263 {
264         // This function is a bit too simplistix and is just a
265         // "try to make a fit for all accents" approach, to
266         // make it better we need to know what kind of accent is
267         // used and add to max based on that.
268         int max;
269         if (candisp) {
270                 if (ic == ' ')
271                         max = font_metrics::ascent('a', font);
272                 else
273                         max = font_metrics::ascent(ic, font);
274                 if (plusasc)
275                         max += (font_metrics::maxAscent(font) + 3) / 3;
276         } else
277                 max = font_metrics::maxAscent(font) + 4;
278         return max;
279 }
280
281
282 int InsetLatexAccent::descent(BufferView *, LyXFont const & font) const
283 {
284         int max;
285         if (candisp) {
286                 if (ic == ' ')
287                         max = font_metrics::descent('a', font);
288                 else
289                 max = font_metrics::descent(ic, font);
290                 if (plusdesc)
291                 max += 3;
292         } else
293                 max = font_metrics::maxDescent(font) + 4;
294         return max;
295 }
296
297
298 int InsetLatexAccent::width(BufferView *, LyXFont const & font) const
299 {
300         if (candisp)
301                 return font_metrics::width(ic, font);
302         else
303                 return font_metrics::width(contents, font) + 4;
304 }
305
306
307 int InsetLatexAccent::lbearing(LyXFont const & font) const
308 {
309         return font_metrics::lbearing(ic, font);
310 }
311
312
313 int InsetLatexAccent::rbearing(LyXFont const & font) const
314 {
315         return font_metrics::rbearing(ic, font);
316 }
317
318
319 bool InsetLatexAccent::displayISO8859_9(BufferView * bv, LyXFont const & font,
320                                         int baseline,
321                                         float & x) const
322 {
323         unsigned char tmpic = ic;
324
325         switch (modtype) {
326         case CEDILLA:
327         {
328                 if (ic == 'c') tmpic = 0xe7;
329                 if (ic == 'C') tmpic = 0xc7;
330                 if (ic == 's') tmpic = 0xfe;
331                 if (ic == 'S') tmpic = 0xde;
332                 break;
333         }
334         case BREVE:
335         {       if (ic == 'g') tmpic = 0xf0;
336         if (ic == 'G') tmpic = 0xd0;
337         break;
338         }
339         case UMLAUT:
340         {
341                 if (ic == 'o') tmpic = 0xf6;
342                 if (ic == 'O') tmpic = 0xd6;
343                 if (ic == 'u') tmpic = 0xfc;
344                 if (ic == 'U') tmpic = 0xdc;
345                 break;
346         }
347         case DOT:        if (ic == 'I') tmpic = 0xdd; break;
348         case DOT_LESS_I: tmpic = 0xfd; break;
349         default:         return false;
350         }
351         if (tmpic != ic) {
352                 char ch = char(tmpic);
353                 bv->painter().text(int(x), baseline, ch, font);
354                 x += width(bv, font);
355                 return true;
356         }
357         else
358                 return false;
359 }
360
361
362 void InsetLatexAccent::draw(BufferView * bv, LyXFont const & font0,
363                             int baseline, float & x) const
364 {
365         Painter & pain = bv->painter();
366
367         if (lyxrc.font_norm_type == LyXRC::ISO_8859_9)
368                 if (displayISO8859_9(bv, font0, baseline, x))
369                         return;
370
371         /* draw it! */
372         // All the manually drawn accents in this function could use an
373         // overhaul. Different ways of drawing (what metrics to use)
374         // should also be considered.
375
376         LyXFont font(font0);
377         if (lyxrc.font_norm_type == LyXRC::ISO_10646_1)
378                 font.setLanguage(english_language);
379
380         if (candisp) {
381                 int asc = ascent(bv, font);
382                 int desc = descent(bv, font);
383                 int wid = width(bv, font);
384                 float x2 = x + (rbearing(font) - lbearing(font)) / 2.0;
385                 float hg;
386                 int y;
387                 if (plusasc) {
388                         // mark at the top
389                         hg = font_metrics::maxDescent(font);
390                         y = baseline - asc;
391
392                         if (font.shape() == LyXFont::ITALIC_SHAPE)
393                                 x2 += (4.0 * hg) / 5.0; // italic
394                 } else {
395                         // at the bottom
396                         hg = desc;
397                         y = baseline;
398                 }
399
400                 float hg35 = float(hg * 3.0) / 5.0;
401
402                 // display with proper accent mark
403                 // first the letter
404                 pain.text(int(x), baseline, ic, font);
405
406                 if (remdot) {
407                         int tmpvar = baseline - font_metrics::ascent('i', font);
408                         float tmpx = 0;
409                         if (font.shape() == LyXFont::ITALIC_SHAPE)
410                                 tmpx += (8.0 * hg) / 10.0; // italic
411                         lyxerr[Debug::KEY] << "Removing dot." << endl;
412                         // remove the dot first
413                         pain.fillRectangle(int(x + tmpx), tmpvar, wid,
414                                            font_metrics::ascent('i', font) -
415                                            font_metrics::ascent('x', font) - 1,
416                                            backgroundColor());
417                         // the five lines below is a simple hack to
418                         // make the display of accent 'i' and 'j'
419                         // better. It makes the accent be written
420                         // closer to the top of the dot-less 'i' or 'j'.
421                         char tmpic = ic; // store the ic when we
422                         ic = 'x';        // calculates the ascent of
423                         asc = ascent(bv, font); // the dot-less version (here: 'x')
424                         ic = tmpic;      // set the orig ic back
425                         y = baseline - asc; // update to new y coord.
426                 }
427                 // now the rest - draw within (x, y, x+wid, y+hg)
428                 switch (modtype) {
429                 case ACUTE:     // acute 0xB4
430                 {
431                         pain.text(int(x2 - (font_metrics::rbearing(0xB4, font) - font_metrics::lbearing(0xB4, font)) / 2),
432                                   baseline - font_metrics::ascent(ic, font) - font_metrics::descent(0xB4, font) - (font_metrics::ascent(0xB4, font) + font_metrics::descent(0xB4, font)) / 2,
433                                   char(0xB4), font);
434                         break;
435                 }
436                 case GRAVE:     // grave 0x60
437                 {
438                         pain.text(int(x2 - (font_metrics::rbearing(0x60, font) - font_metrics::lbearing(0x60, font)) / 2),
439                                   int(baseline - font_metrics::ascent(ic, font) - font_metrics::descent(0x60, font) - (font_metrics::ascent(0x60, font) + font_metrics::descent(0x60, font)) / 2.0),
440                                   char(0x60), font);
441                         break;
442                 }
443                 case MACRON:     // macron
444                 {
445                         pain.text(int(x2 - (font_metrics::rbearing(0xAF, font) - font_metrics::lbearing(0xAF, font)) / 2),
446                                   baseline - font_metrics::ascent(ic, font) - font_metrics::descent(0xAF, font) - (font_metrics::ascent(0xAF, font) + font_metrics::descent(0xAF, font)),
447                                   char(0xAF), font);
448                         break;
449                 }
450                 case TILDE:     // tilde
451                 {
452                         pain.text(int(x2 - (font_metrics::rbearing('~', font) - font_metrics::lbearing('~', font)) / 2),
453                                   baseline - font_metrics::ascent(ic, font) - font_metrics::descent('~', font) - (font_metrics::ascent('~', font) + font_metrics::descent('~', font)) / 2,
454                                   '~', font);
455                         break;
456                 }
457                 case UNDERBAR:     // underbar 0x5F
458                 {
459                         pain.text(int(x2 - (font_metrics::rbearing(0x5F, font) - font_metrics::lbearing(0x5F, font)) / 2), baseline,
460                                   char(0x5F), font);
461                         break;
462                 }
463                 case CEDILLA:     // cedilla
464                 {
465                         pain.text(int(x2 - (font_metrics::rbearing(0xB8, font) - font_metrics::lbearing(0xB8, font)) / 2), baseline,
466                                   char(0xB8), font);
467
468                         break;
469                 }
470                 case UNDERDOT:     // underdot
471                 {
472                         pain.text(int(x2 - (font_metrics::rbearing('.', font) - font_metrics::lbearing('.', font)) / 2.0),
473                                   int(baseline + 3.0 / 2.0 * (font_metrics::ascent('.', font) + font_metrics::descent('.', font))),
474                                   '.', font);
475                         break;
476                 }
477
478                 case DOT:    // dot
479                 {
480                         pain.text(int(x2 - (font_metrics::rbearing('.', font) - font_metrics::lbearing('.', font)) / 2.0),
481                                   baseline - font_metrics::ascent(ic, font) - font_metrics::descent('.', font) - (font_metrics::ascent('.', font) + font_metrics::descent('.', font)) / 2,
482                                   '.', font);
483                         break;
484                 }
485
486                 case CIRCLE:     // circle
487                 {
488                         LyXFont tmpf(font);
489                         tmpf.decSize().decSize();
490                         pain.text(int(x2 - (font_metrics::rbearing(0xB0, tmpf) - font_metrics::lbearing(0xB0, tmpf)) / 2.0),
491                                   int(baseline - font_metrics::ascent(ic, font) - font_metrics::descent(0xB0, tmpf) - (font_metrics::ascent(0xB0, tmpf) + font_metrics::descent(0xB0, tmpf)) / 3.0),
492                                   char(0xB0), tmpf);
493                         break;
494                 }
495                 case TIE:     // tie
496                 {
497                         pain.arc(int(x2 + hg35), int(y + hg / 2.0),
498                                  int(2 * hg), int(hg), 0, 360 * 32);
499                         break;
500                 }
501                 case BREVE:     // breve
502                 {
503                         pain.arc(int(x2 - (hg / 2.0)), y,
504                                  int(hg), int(hg), 0, -360*32);
505                         break;
506                 }
507                 case CARON:    // caron
508                 {
509                         int xp[3], yp[3];
510
511                         xp[0] = int(x2 - hg35); yp[0] = int(y + hg35);
512                         xp[1] = int(x2);        yp[1] = int(y + hg);
513                         xp[2] = int(x2 + hg35); yp[2] = int(y + hg35);
514                         pain.lines(xp, yp, 3);
515                         break;
516                 }
517                 case SPECIAL_CARON:    // special caron
518                 {
519                         switch (ic) {
520                         case 'L': wid = int(4.0 * wid / 5.0); break;
521                         case 't': y -= int(hg35 / 2.0); break;
522                         }
523                         int xp[3], yp[3];
524                         xp[0] = int(x + wid);
525                         yp[0] = int(y + hg35 + hg);
526
527                         xp[1] = int(x + wid + (hg35 / 2.0));
528                         yp[1] = int(y + hg + (hg35 / 2.0));
529
530                         xp[2] = int(x + wid + (hg35 / 2.0));
531                         yp[2] = y + int(hg);
532
533                         pain.lines(xp, yp, 3);
534                         break;
535                 }
536                 case HUNGARIAN_UMLAUT:    // hung. umlaut
537                 {
538                         pain.text(int(x2 - (font_metrics::rbearing('´', font) - font_metrics::lbearing('´', font))),
539                                   baseline - font_metrics::ascent(ic, font) - font_metrics::descent('´', font) - (font_metrics::ascent('´', font) + font_metrics::descent('´', font)) / 2,
540                                   '´', font);
541                         pain.text(int(x2),
542                                   baseline - font_metrics::ascent(ic, font) - font_metrics::descent('´', font) - (font_metrics::ascent('´', font) + font_metrics::descent('´', font)) / 2,
543                                   '´', font);
544                         break;
545                 }
546                 case UMLAUT:    // umlaut
547                 {
548                         pain.text(int(x2 - (font_metrics::rbearing('¨', font) - font_metrics::lbearing('¨', font)) / 2),
549                                   baseline - font_metrics::ascent(ic, font) - font_metrics::descent('¨', font) - (font_metrics::ascent('¨', font) + font_metrics::descent('¨', font)) / 2,
550                                   '¨', font);
551                         break;
552                 }
553                 case CIRCUMFLEX:    // circumflex
554                 {
555                         LyXFont tmpf(font);
556                         tmpf.decSize().decSize().decSize();
557                         pain.text(int(x2 - (font_metrics::rbearing(0x5E, tmpf) - font_metrics::lbearing(0x5E, tmpf)) / 2),
558                                   int(baseline - font_metrics::ascent(ic, font) - font_metrics::descent(0x5E, tmpf) - (font_metrics::ascent(0x5E, tmpf) + font_metrics::descent(0x5E, tmpf)) / 3.0),
559                                   char(0x5E), tmpf);
560                         break;
561                 }
562                 case OGONEK:    // ogonek
563                 {
564                         // this does probably not look like an ogonek, so
565                         // it should certainly be refined
566                         int xp[4], yp[4];
567
568                         xp[0] = int(x2);
569                         yp[0] = y;
570
571                         xp[1] = int(x2);
572                         yp[1] = y + int(hg35);
573
574                         xp[2] = int(x2 - hg35);
575                         yp[2] = y + int(hg / 2.0);
576
577                         xp[3] = int(x2 + hg / 4.0);
578                         yp[3] = y + int(hg);
579
580                         pain.lines(xp, yp, 4);
581                         break;
582                 }
583                 case lSLASH:
584                 case LSLASH:
585                 {
586                         int xp[2], yp[2];
587
588                         xp[0] = int(x);
589                         yp[0] = y + int(3.0 * hg);
590
591                         xp[1] = int(x + float(wid) * 0.75);
592                         yp[1] = y + int(hg);
593
594                         pain.lines(xp, yp, 2);
595                         break;
596                 }
597                 case DOT_LESS_I: // dotless-i
598                 case DOT_LESS_J: // dotless-j
599                 {
600                         // nothing to do for these
601                         break;
602                 }
603                 }
604         } else {
605                 pain.fillRectangle(int(x + 1),
606                                    baseline - ascent(bv, font) + 1,
607                                    width(bv, font) - 2,
608                                    ascent(bv, font)
609                                    + descent(bv, font) - 2,
610                                    backgroundColor());
611                 pain.rectangle(int(x + 1), baseline - ascent(bv, font) + 1,
612                                width(bv, font) - 2,
613                                ascent(bv, font) + descent(bv, font) - 2);
614                 pain.text(int(x + 2), baseline, contents, font);
615         }
616         x +=  width(bv, font);
617 }
618
619
620 void InsetLatexAccent::write(Buffer const *, ostream & os) const
621 {
622         os << "\\i " << contents << "\n";
623 }
624
625
626 void InsetLatexAccent::read(Buffer const *, LyXLex & lex)
627 {
628         lex.eatLine();
629         contents = lex.getString();
630         checkContents();
631 }
632
633
634 int InsetLatexAccent::latex(Buffer const *, ostream & os,
635                             bool /*fragile*/, bool/*fs*/) const
636 {
637         os << contents;
638         return 0;
639 }
640
641
642 int InsetLatexAccent::ascii(Buffer const *, ostream & os, int) const
643 {
644         os << contents;
645         return 0;
646 }
647
648
649 int InsetLatexAccent::linuxdoc(Buffer const *, ostream & os) const
650 {
651         os << contents;
652         return 0;
653 }
654
655
656 int InsetLatexAccent::docbook(Buffer const *, ostream & os, bool) const
657 {
658         os << contents;
659         return 0;
660 }
661
662
663 bool InsetLatexAccent::directWrite() const
664 {
665         return true;
666 }
667
668
669 Inset * InsetLatexAccent::clone(Buffer const &, bool) const
670 {
671         return new InsetLatexAccent(contents);
672 }
673
674
675 Inset::Code InsetLatexAccent::lyxCode() const
676 {
677         return Inset::ACCENT_CODE;
678 }
679
680
681 ostream & operator<<(ostream & o, InsetLatexAccent::ACCENT_TYPES at)
682 {
683         return o << int(at);
684 }