1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
14 #pragma implementation
17 #include "insetlatexaccent.h"
21 #include "support/lstrings.h"
26 /* LatexAccent. Proper handling of accented characters */
27 /* This part is done by Ivan Schreter, schreter@ccsun.tuke.sk */
28 /* Later modified by Lars G. Bjønnes, larsbj@lyx.org */
30 InsetLatexAccent::InsetLatexAccent()
36 InsetLatexAccent::InsetLatexAccent(string const & string)
43 void InsetLatexAccent::checkContents()
44 // check, if we know the modifier and can display it ok on screen
48 if (contents.empty() || contents.length() < 2) return;
51 // Dirty Hack for backward compability. remove in 0.13 (Lgb)
52 contents = frontStrip(strip(contents));
53 if (!contains(contents, "{") && !contains(contents, "}")) {
54 if (contents.length() == 2) {
60 } else if (contents.length() == 3) {
68 } else if (contents.length() == 4 && contents[2] == ' ') {
76 } else if (contents.length() == 4 && contents[2] == '\\'
77 && (contents[3] == 'i' || contents[3] == 'j')) {
88 if (contents[0] != '\\') return; // demand that first char is a '\\'
90 lyxerr[Debug::KEY] << "Decode: " << contents << endl;
92 remdot = false; plusasc = false; plusdesc = false;
94 switch (contents[1]) { // second char should be one of these
96 modtype = ACUTE; // acute
97 plusasc = true; // at the top of character
100 modtype = GRAVE; // grave
101 plusasc = true; // at the top
104 modtype = MACRON; // macron
105 plusasc = true; // at the top
108 modtype = TILDE; // tilde
109 plusasc = true; // at the top
111 case 'b': // underbar
112 modtype = UNDERBAR; // underbar
113 plusdesc = true; // at the bottom
116 modtype = CEDILLA; // cedilla
117 plusdesc = true; // at the bottom
119 case 'd': // underdot
120 modtype = UNDERDOT; // underdot
121 plusdesc = true; // at the bottom
124 modtype = CIRCLE; // circle
125 plusasc = true; // at the top
128 modtype = TIE; // tie
129 plusasc = true; // at the top
132 modtype = BREVE; // breve
133 plusasc = true; // at the top
136 modtype = CARON; // caron
137 plusasc = true; // at the top
139 case 'q': // special caron
140 modtype = SPECIAL_CARON; // special caron
141 plusasc = true; // at the top
143 case 'H': // hungarian umlaut
144 modtype = HUNGARIAN_UMLAUT; // hungarian umlaut
145 plusasc = true; // at the top
148 modtype = UMLAUT; // umlaut
149 plusasc = true; // at the top
152 modtype = DOT; // dot
153 plusasc = true; // at the top
155 case '^': // circumflex
156 modtype = CIRCUMFLEX; // circumflex
157 plusasc = true; // at the top
160 modtype = OGONEK; // ogonek
163 case 'i': // dot-less-i
164 modtype = DOT_LESS_I; // dot-less-i
165 plusasc = true; // at the top (not really needed)
168 case 'j': // dot-less-j
169 modtype = DOT_LESS_J; // dot-less-j
170 plusasc = true; // at the top (not really needed)
175 plusasc = true; // at the top (not really needed)
179 plusasc = true; // at the top (not really needed)
182 printf ("Default\n");
183 // unknow accent (or something else)
187 // we demand that third char is a '{' (Lgb)
188 if (contents[2] != '{') return;
190 // special clause for \i{}, \j{} \l{} and \L{}
191 if ((modtype == DOT_LESS_I || modtype == DOT_LESS_J
192 || modtype == lSLASH || modtype == LSLASH)
193 && contents[3] == '}' ) {
195 case DOT_LESS_I: ic = 'i'; break;
196 case DOT_LESS_J: ic = 'j'; break;
197 case lSLASH: ic = 'l'; break;
198 case LSLASH: ic = 'L'; break;
200 // if this happens something is really wrong
201 lyxerr << "InsetLaTexAccent: weird error." << endl;
204 //ic = (modtype == DOT_LESS_J ? 'j' : 'i');
205 lyxerr[Debug::KEY] << "Contents: [" << contents << "]"
207 << ", top: " << plusasc
208 << ", bot: " << plusdesc
209 << ", dot: " << remdot
210 << ", mod: " << modtype << endl;
211 // Special case for space
212 } else if (contents[3] == '}') {
218 ic = contents[3]; // i will always be 3 here
220 // ic should now be a alfa-char or '\\'
222 ic = contents[++i]; // will only allow \<foo>{\i} and \<foo>{\j}
223 if (ic == 'i' || ic == 'j')
227 } else if ( (ic == 'i'|| ic == 'j') && contents[4] == '}') {
228 // Do a rewrite: \<foo>{i} --> \<foo>{\i}
229 string temp = contents;
230 temp.erase(3, string::npos);
233 for(string::size_type j = 4;
234 j < contents.length(); ++j)
241 // demand a '}' at the end
242 if (contents[++i] != '}' && contents[++i]) return;
244 // fine, the char is properly decoded now (hopefully)
245 lyxerr[Debug::KEY] << "Contents: [" << contents << "]"
247 << ", top: " << plusasc
248 << ", bot: " << plusdesc
249 << ", dot: " << remdot
250 << ", mod: " << modtype << endl;
257 int InsetLatexAccent::ascent(Painter &, LyXFont const & font) const
259 // This function is a bit too simplistix and is just a
260 // "try to make a fit for all accents" approach, to
261 // make it better we need to know what kind of accent is
262 // used and add to max based on that.
266 max = font.ascent('a');
268 max = font.ascent(ic);
270 max += (font.maxAscent() + 3) / 3;
272 max = font.maxAscent() + 4;
276 int InsetLatexAccent::Ascent(LyXFont const & font) const
278 // This function is a bit too simplistix and is just a
279 // "try to make a fit for all accents" approach, to
280 // make it better we need to know what kind of accent is
281 // used and add to max based on that.
285 max = font.ascent('a');
287 max = font.ascent(ic);
289 max += (font.maxAscent() + 3) / 3;
291 max = font.maxAscent() + 4;
298 int InsetLatexAccent::descent(Painter &, LyXFont const & font) const
303 max = font.descent('a');
305 max = font.descent(ic);
309 max = font.maxDescent() + 4;
313 int InsetLatexAccent::Descent(LyXFont const & font) const
318 max = font.descent('a');
320 max = font.descent(ic);
324 max = font.maxDescent() + 4;
331 int InsetLatexAccent::width(Painter &, LyXFont const & font) const
334 return font.textWidth(&ic, 1);
336 return font.stringWidth(contents) + 4;
339 int InsetLatexAccent::Width(LyXFont const & font) const
342 return font.textWidth(&ic, 1);
344 return font.stringWidth(contents) + 4;
349 int InsetLatexAccent::Lbearing(LyXFont const & font) const
351 return font.lbearing(ic);
355 int InsetLatexAccent::Rbearing(LyXFont const & font) const
357 return font.rbearing(ic);
362 bool InsetLatexAccent::DisplayISO8859_9(Painter & pain, LyXFont const & font,
366 unsigned char tmpic = ic;
371 if (ic == 'c') tmpic = 0xe7;
372 if (ic == 'C') tmpic = 0xc7;
373 if (ic == 's') tmpic = 0xfe;
374 if (ic == 'S') tmpic = 0xde;
378 { if (ic == 'g') tmpic = 0xf0;
379 if (ic == 'G') tmpic = 0xd0;
384 if (ic == 'o') tmpic = 0xf6;
385 if (ic == 'O') tmpic = 0xd6;
386 if (ic == 'u') tmpic = 0xfc;
387 if (ic == 'U') tmpic = 0xdc;
390 case DOT: if (ic == 'I') tmpic = 0xdd; break;
391 case DOT_LESS_I: tmpic = 0xfd; break;
392 default: return false;
395 char ch = char(tmpic);
396 pain.text(x, baseline, ch, font);
397 x += width(pain, font);
404 bool InsetLatexAccent::DisplayISO8859_9(LyXFont font,
409 unsigned char tmpic = ic;
414 if (ic == 'c') tmpic = 0xe7;
415 if (ic == 'C') tmpic = 0xc7;
416 if (ic == 's') tmpic = 0xfe;
417 if (ic == 'S') tmpic = 0xde;
421 { if (ic == 'g') tmpic = 0xf0;
422 if (ic == 'G') tmpic = 0xd0;
427 if (ic == 'o') tmpic = 0xf6;
428 if (ic == 'O') tmpic = 0xd6;
429 if (ic == 'u') tmpic = 0xfc;
430 if (ic == 'U') tmpic = 0xdc;
433 case DOT: if (ic == 'I') tmpic = 0xdd; break;
434 case DOT_LESS_I: tmpic = 0xfd; break;
435 default: return false;
438 char ch = char(tmpic);
439 scr.drawText(font, &ch, 1, baseline, int(x));
450 void InsetLatexAccent::draw(Painter & pain, LyXFont const & font,
451 int baseline, float & x) const
453 if (lyxrc->font_norm == "iso8859-9")
454 if (DisplayISO8859_9(pain, font, baseline, x))
458 // All the manually drawn accents in this function could use an
459 // overhaul. Different ways of drawing (what metrics to use)
460 // should also be considered.
463 int asc = ascent(pain, font);
464 int desc = descent(pain, font);
465 int wid = width(pain, font);
466 float x2 = x + (Rbearing(font) - Lbearing(font)) / 2.0;
471 hg = font.maxDescent();
474 if (font.shape() == LyXFont::ITALIC_SHAPE)
475 x2 += (4.0 * hg) / 5.0; // italic
482 float hg35 = float(hg * 3.0) / 5.0;
484 // display with proper accent mark
486 pain.text(int(x), baseline, ic, font);
489 GC pgc = GetAccentGC(font, int((hg + 3.0) / 5.0));
493 int tmpvar = baseline - font.ascent('i');
495 if (font.shape() == LyXFont::ITALIC_SHAPE)
496 tmpx += (8.0 * hg) / 10.0; // italic
497 lyxerr[Debug::KEY] << "Removing dot." << endl;
498 // remove the dot first
499 pain.fillRectangle(int(x + tmpx), tmpvar, wid,
501 font.ascent('x') - 1,
503 // the five lines below is a simple hack to
504 // make the display of accent 'i' and 'j'
505 // better. It makes the accent be written
506 // closer to the top of the dot-less 'i' or 'j'.
507 char tmpic = ic; // store the ic when we
508 ic = 'x'; // calculates the ascent of
509 asc = ascent(pain, font); // the dot-less version (here: 'x')
510 ic = tmpic; // set the orig ic back
511 y = baseline - asc; // update to new y coord.
513 // now the rest - draw within (x, y, x+wid, y+hg)
517 pain.line(int(x2), int(y + hg),
518 int(x2 + hg35), y + hg35);
523 pain.line(int(x2), int(y + hg),
524 int(x2 - hg35), y + hg35);
527 case MACRON: // macron
529 pain.line(int(x2 - wid * 0.4),
537 if (hg35 > 2.0) hg35 -= 1.0;
541 xp[0] = int(x2 - 2.0 * hg35);
544 xp[1] = int(x2 - hg35);
545 yp[1] = int(y + hg35);
550 xp[3] = int(x2 + hg35);
551 yp[3] = int(y + hg35);
553 pain.lines(xp, yp, 4);
556 case UNDERBAR: // underbar
558 pain.line(int(x2 - wid * 0.4),
564 case CEDILLA: // cedilla
572 yp[1] = y + int(hg / 3.0);
574 xp[2] = int(x2 + (hg / 3.0));
575 yp[2] = y + int(hg / 2.0);
577 xp[3] = int(x2 - (hg / 4.0));
580 pain.lines(xp, yp, 4);
583 case UNDERDOT: // underdot
585 pain.arc(int(x2), y + hg35,
592 pain.arc(int(x2), y + hg * 0.5,
599 case CIRCLE: // circle
601 pain.arc(int(x2 - (hg / 2.0)),
602 y + (hg / 2.0), hg, hg , 0,
608 pain.arc(int(x2 + hg35), y + hg / 2.0,
609 2 * hg, hg, 0, 360 * 32);
614 pain.arc(int(x2 - (hg / 2.0)), y,
622 xp[0] = int(x2 - hg35); yp[0] = int(y + hg35);
623 xp[1] = int(x2); yp[1] = int(y + hg);
624 xp[2] = int(x2 + hg35); yp[2] = int(y + hg35);
625 pain.lines(xp, yp, 3);
628 case SPECIAL_CARON: // special caron
631 case 'L': wid = int(4.0 * wid / 5.0); break;
632 case 't': y -= int(hg35 / 2.0); break;
635 xp[0] = int(x + wid);
636 yp[0] = int(y + hg35 + hg);
638 xp[1] = int(x + wid + (hg35 / 2.0));
639 yp[1] = int(y + hg + (hg35 / 2.0));
641 xp[2] = int(x + wid + (hg35 / 2.0));
644 pain.lines(xp, yp, 3);
647 case HUNGARIAN_UMLAUT: // hung. umlaut
649 int xs1[2], xs2[2], ys1[2], ys2[2];
651 xs1[0] = int(x2 - (hg / 2.0));
652 ys1[0] = int(y + hg);
654 xs2[0] = int(x2 + hg35 - (hg / 2.0));
655 ys2[0] = int(y + hg35);
657 xs1[1] = int(x2 + (hg / 2.0));
658 ys1[1] = int(y + hg);
660 xs2[1] = int(x2 + hg35 + (hg / 2.0));
661 ys2[1] = int(y + hg35);
663 pain.segments(xs1, ys1, xs2, ys2, 2);
666 case UMLAUT: // umlaut
668 float rad = hg / 2.0;
670 pain.point(int(x2 - 4.0 * hg / 7.0),
672 pain.point(int(x2 + 4.0 * hg / 7.0),
675 rad += .5; // this ensures that f.ex. 1.5 will
676 // not be rounded down to .5 and then
677 // converted to int = 0
678 pain.arc(int(x2 - 2.0 * hg / 4.0),
682 pain.arc(int(x2 + 2.0 * hg / 4.0),
684 rad, rad, 0, 360*64);
688 case CIRCUMFLEX: // circumflex
692 xp[0] = int(x2 - hg35); yp[0] = y + int(hg);
693 xp[1] = int(x2); yp[1] = int(y + hg35);
694 xp[2] = int(x2 + hg35); yp[2] = y + int(hg);
695 pain.lines(xp, yp, 3);
698 case OGONEK: // ogonek
700 // this does probably not look like an ogonek, so
701 // it should certainly be refined
708 yp[1] = y + int(hg35);
710 xp[2] = int(x2 - hg35);
711 yp[2] = y + int(hg / 2.0);
713 xp[3] = int(x2 + hg / 4.0);
716 pain.lines(xp, yp, 4);
725 yp[0] = y + int(3.0 * hg);
727 xp[1] = int(x + float(wid) * 0.75);
730 pain.lines(xp, yp, 2);
733 case DOT_LESS_I: // dotless-i
734 case DOT_LESS_J: // dotless-j
736 // nothing to do for these
741 pain.fillRectangle(int(x + 1),
742 baseline - ascent(pain, font) + 1,
743 width(pain, font) - 2,
745 + descent(pain, font) - 2);
746 pain.rectangle(int(x + 1), baseline - ascent(pain, font) + 1,
747 width(pain, font) - 2,
748 ascent(pain, font) + descent(pain, font) - 2);
749 pain.text(int(x + 2), baseline, contents, font);
751 x += width(pain, font);
754 void InsetLatexAccent::Draw(LyXFont font,
759 if (lyxrc->font_norm == "iso8859-9")
760 if (DisplayISO8859_9 (font, scr, baseline, x))
764 // All the manually drawn accents in this function could use an
765 // overhaul. Different ways of drawing (what metrics to use)
766 // should also be considered.
769 int asc = Ascent(font);
770 int desc = Descent(font);
771 int wid = Width(font);
772 float x2 = x + (Rbearing(font) - Lbearing(font)) / 2.0;
777 hg = font.maxDescent();
780 if (font.shape() == LyXFont::ITALIC_SHAPE)
781 x2 += (4.0 * hg) / 5.0; // italic
788 float hg35 = float(hg * 3.0) / 5.0;
790 // display with proper accent mark
792 scr.drawText(font, &ic, 1, baseline, int(x));
794 GC pgc = GetAccentGC(font, int((hg + 3.0) / 5.0));
797 int tmpvar = baseline - font.ascent('i');
799 if (font.shape() == LyXFont::ITALIC_SHAPE)
800 tmpx += (8.0 * hg) / 10.0; // italic
801 lyxerr[Debug::KEY] << "Removing dot." << endl;
802 // remove the dot first
803 scr.fillRectangle(gc_clear, int(x + tmpx),
806 font.ascent('x') - 1);
807 // the five lines below is a simple hack to
808 // make the display of accent 'i' and 'j'
809 // better. It makes the accent be written
810 // closer to the top of the dot-less 'i' or 'j'.
811 char tmpic = ic; // store the ic when we
812 ic = 'x'; // calculates the ascent of
813 asc = Ascent(font); // the dot-less version (here: 'x')
814 ic = tmpic; // set the orig ic back
815 y = baseline - asc; // update to new y coord.
817 // now the rest - draw within (x, y, x+wid, y+hg)
821 scr.drawLine(pgc, int(x2), int(y + hg),
822 int(x2 + hg35), y + hg35);
827 scr.drawLine(pgc, int(x2), int(y + hg),
828 int(x2 - hg35), y + hg35);
831 case MACRON: // macron
842 if (hg35 > 2.0) hg35 -= 1.0;
845 p[0].x = int(x2 - 2.0 * hg35); p[0].y = int(y + hg);
846 p[1].x = int(x2 - hg35); p[1].y = int(y + hg35);
847 p[2].x = int(x2); p[2].y = int(y + hg);
848 p[3].x = int(x2 + hg35); p[3].y = int(y + hg35);
849 scr.drawLines(pgc, p, 4);
852 case UNDERBAR: // underbar
861 case CEDILLA: // cedilla
864 p[0].x = int(x2); p[0].y = y;
865 p[1].x = int(x2); p[1].y = y + int(hg / 3.0);
866 p[2].x = int(x2 + (hg / 3.0));
867 p[2].y = y + int(hg / 2.0);
868 p[3].x = int(x2 - (hg / 4.0)); p[3].y = y + int(hg);
869 scr.drawLines(pgc, p, 4);
872 case UNDERDOT: // underdot
874 scr.fillArc(pgc, int(x2), y + hg35,
881 scr.fillArc(pgc, int(x2), y + hg * 0.5,
883 (hg + 3.0)/5.0, 0, 360*64);
887 case CIRCLE: // circle
889 scr.drawArc(pgc, int(x2 - (hg / 2.0)),
890 y + (hg / 2.0), hg, hg, 0,
897 int(x2 + hg35), y + (hg / 2.0),
898 2 * hg, hg, 0, 360*32);
904 int(x2 - (hg / 2.0)), y,
911 p[0].x = int(x2 - hg35); p[0].y = int(y + hg35);
912 p[1].x = int(x2); p[1].y = int(y + hg);
913 p[2].x = int(x2 + hg35); p[2].y = int(y + hg35);
914 scr.drawLines(pgc, p, 3);
917 case SPECIAL_CARON: // special caron
920 case 'L': wid = int(4.0 * wid / 5.0); break;
921 case 't': y -= int(hg35 / 2.0); break;
924 p[0].x = int(x + wid); p[0].y = int(y + hg35 + hg);
925 p[1].x = int(x + wid + (hg35 / 2.0));
926 p[1].y = int(y + hg + (hg35 / 2.0));
927 p[2].x = int(x + wid + (hg35 / 2.0));
928 p[2].y = y + int(hg);
929 scr.drawLines(pgc, p, 3);
932 case HUNGARIAN_UMLAUT: // hung. umlaut
935 s[0].x1= int(x2 - (hg / 2.0)); s[0].y1 = int(y + hg);
936 s[0].x2 = int(x2 + hg35 - (hg / 2.0));
937 s[0].y2 = int(y + hg35);
938 s[1].x1 = int(x2 + (hg / 2.0));
939 s[1].y1 = int(y + hg);
940 s[1].x2 = int(x2 + hg35 + (hg / 2.0));
941 s[1].y2 = int(y + hg35);
943 scr.drawSegments(pgc, s, 2);
946 case UMLAUT: // umlaut
948 float rad = hg / 2.0;
951 int(x2 - ((4.0 * hg) / 7.0)),
954 int(x2 + ((4.0 * hg) / 7.0)),
957 rad += .5; // this ensures that f.ex. 1.5 will
958 // not be rounded down to .5 and then
959 // converted to int = 0
960 scr.fillArc(pgc, int(x2 - ((2.0 * hg) / 4.0)),
962 rad, rad, 0, 360*64);
963 scr.fillArc(pgc, int(x2 + ((2.0 * hg) / 4.0)),
965 rad, rad, 0, 360*64);
967 //scr.drawText(font, "¨", 1, y + hg, x2);
970 case CIRCUMFLEX: // circumflex
973 p[0].x = int(x2 - hg35); p[0].y = y + int(hg);
974 p[1].x = int(x2); p[1].y = int(y + hg35);
975 p[2].x = int(x2 + hg35); p[2].y = y + int(hg);
976 scr.drawLines(pgc, p, 3);
979 case OGONEK: // ogonek
981 // this does probably not look like an ogonek, so
982 // it should certainly be refined
984 p[0].x = int(x2); p[0].y = y;
985 p[1].x = int(x2); p[1].y = y + int(hg35);
986 p[2].x = int(x2 - hg35);
987 p[2].y = y + int(hg / 2.0);
988 p[3].x = int(x2 + (hg / 4.0)); p[3].y = y + int(hg);
989 scr.drawLines(pgc, p, 4);
997 p[0].y = y + int(3.0 * hg);
998 p[1].x = int(x + float(wid) * 0.75);
999 p[1].y = y + int(hg);
1000 scr.drawLines(pgc, p, 2);
1003 case DOT_LESS_I: // dotless-i
1004 case DOT_LESS_J: // dotless-j
1006 // nothing to do for these
1011 scr.fillRectangle(gc_lighted,
1012 int(x + 1), baseline - Ascent(font) + 1,
1014 Ascent(font) + Descent(font) - 2);
1016 scr.drawRectangle(gc_lighted,
1017 int(x), baseline - Ascent(font),
1019 Ascent(font) + Descent(font) - 1);
1020 scr.drawString(font, contents, baseline, int(x + 2));
1027 void InsetLatexAccent::Write(ostream & os)
1029 os << "\\i " << contents << "\n";
1033 void InsetLatexAccent::Read(LyXLex & lex)
1036 contents = lex.GetString();
1041 int InsetLatexAccent::Latex(ostream & os, signed char /*fragile*/)
1048 int InsetLatexAccent::Latex(string & file, signed char /*fragile*/)
1055 int InsetLatexAccent::Linuxdoc(string & file)
1062 int InsetLatexAccent::DocBook(string & file)
1069 bool InsetLatexAccent::Deletable() const
1075 bool InsetLatexAccent::DirectWrite() const
1081 Inset * InsetLatexAccent::Clone() const
1083 return new InsetLatexAccent(contents);
1087 Inset::Code InsetLatexAccent::LyxCode() const
1089 return Inset::ACCENT_CODE;
1093 ostream & operator<<(ostream & o, InsetLatexAccent::ACCENT_TYPES at)
1095 return o << int(at);