2 * \file insetlatexaccent.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * Full author contact details are available in file CREDITS.
13 #include "insetlatexaccent.h"
20 #include "metricsinfo.h"
22 #include "frontends/font_metrics.h"
23 #include "frontends/Painter.h"
25 #include "support/lstrings.h"
27 using lyx::support::contains;
28 using lyx::support::trim;
36 /* LatexAccent. Proper handling of accented characters */
37 /* This part is done by Ivan Schreter, schreter@ccsun.tuke.sk */
38 /* Later modified by Lars G. Bjønnes, larsbj@lyx.org */
40 InsetLatexAccent::InsetLatexAccent()
45 InsetLatexAccent::InsetLatexAccent(string const & str)
52 auto_ptr<InsetBase> InsetLatexAccent::clone() const
54 return auto_ptr<InsetBase>(new InsetLatexAccent(contents));
58 void InsetLatexAccent::checkContents()
59 // check, if we know the modifier and can display it ok on screen
63 if (contents.empty() || contents.length() < 2) {
64 lyxerr[Debug::KEY] << "Cannot decode: " << contents << endl;
69 // Dirty Hack for backward compability. remove in 0.13 (Lgb)
70 contents = trim(contents);
71 if (!contains(contents, "{") && !contains(contents, "}")) {
72 if (contents.length() == 2) {
78 } else if (contents.length() == 3) {
86 } else if (contents.length() == 4 && contents[2] == ' ') {
94 } else if (contents.length() == 4 && contents[2] == '\\'
95 && (contents[3] == 'i' || contents[3] == 'j')) {
106 if (contents[0] != '\\') { // demand that first char is a '\\'
107 lyxerr[Debug::KEY] << "Cannot decode: " << contents << endl;
111 lyxerr[Debug::KEY] << "Decode: " << contents << endl;
117 switch (contents[1]) { // second char should be one of these
119 modtype = ACUTE; // acute
120 plusasc = true; // at the top of character
123 modtype = GRAVE; // grave
124 plusasc = true; // at the top
127 modtype = MACRON; // macron
128 plusasc = true; // at the top
131 modtype = TILDE; // tilde
132 plusasc = true; // at the top
134 case 'b': // underbar
135 modtype = UNDERBAR; // underbar
136 plusdesc = true; // at the bottom
139 modtype = CEDILLA; // cedilla
140 plusdesc = true; // at the bottom
142 case 'd': // underdot
143 modtype = UNDERDOT; // underdot
144 plusdesc = true; // at the bottom
147 modtype = CIRCLE; // circle
148 plusasc = true; // at the top
151 modtype = TIE; // tie
152 plusasc = true; // at the top
155 modtype = BREVE; // breve
156 plusasc = true; // at the top
159 modtype = CARON; // caron
160 plusasc = true; // at the top
162 case 'q': // special caron
163 modtype = SPECIAL_CARON; // special caron
164 plusasc = true; // at the top
166 case 'H': // hungarian umlaut
167 modtype = HUNGARIAN_UMLAUT; // hungarian umlaut
168 plusasc = true; // at the top
171 modtype = UMLAUT; // umlaut
172 plusasc = true; // at the top
175 modtype = DOT; // dot
176 plusasc = true; // at the top
178 case '^': // circumflex
179 modtype = CIRCUMFLEX; // circumflex
180 plusasc = true; // at the top
183 modtype = OGONEK; // ogonek
186 case 'i': // dot-less-i
187 modtype = DOT_LESS_I; // dot-less-i
188 plusasc = true; // at the top (not really needed)
191 case 'j': // dot-less-j
192 modtype = DOT_LESS_J; // dot-less-j
193 plusasc = true; // at the top (not really needed)
198 plusasc = true; // at the top (not really needed)
202 plusasc = true; // at the top (not really needed)
205 lyxerr[Debug::KEY] << "Default" << endl;
206 // unknow accent (or something else)
210 // we demand that third char is a '{' (Lgb)
211 if (contents[2] != '{') return;
213 // special clause for \i{}, \j{} \l{} and \L{}
214 if ((modtype == DOT_LESS_I || modtype == DOT_LESS_J
215 || modtype == lSLASH || modtype == LSLASH)
216 && contents[3] == '}') {
218 case DOT_LESS_I: ic = 'i'; break;
219 case DOT_LESS_J: ic = 'j'; break;
220 case lSLASH: ic = 'l'; break;
221 case LSLASH: ic = 'L'; break;
223 // if this happens something is really wrong
224 lyxerr << "InsetLaTexAccent: weird error." << endl;
227 //ic = (modtype == DOT_LESS_J ? 'j' : 'i');
228 lyxerr[Debug::KEY] << "Contents: [" << contents << ']'
230 << ", top: " << plusasc
231 << ", bot: " << plusdesc
232 << ", dot: " << remdot
233 << ", mod: " << modtype << endl;
234 // Special case for space
235 } else if (contents[3] == '}') {
241 ic = contents[3]; // i will always be 3 here
243 // ic should now be a alfa-char or '\\'
245 ic = contents[++i]; // will only allow \<foo>{\i} and \<foo>{\j}
246 if (ic == 'i' || ic == 'j')
250 } else if ((ic == 'i'|| ic == 'j') && contents[4] == '}') {
251 // Do a rewrite: \<foo>{i} --> \<foo>{\i}
252 string temp = contents;
253 temp.erase(3, string::npos);
256 for (string::size_type j = 4;
257 j < contents.length(); ++j)
264 // demand a '}' at the end
265 if (contents[++i] != '}' && contents[++i]) return;
267 // fine, the char is properly decoded now (hopefully)
268 lyxerr[Debug::KEY] << "Contents: [" << contents << ']'
270 << ", top: " << plusasc
271 << ", bot: " << plusdesc
272 << ", dot: " << remdot
273 << ", mod: " << modtype << endl;
279 void InsetLatexAccent::metrics(MetricsInfo & mi, Dimension & dim) const
281 LyXFont & font = mi.base.font;
282 // This function is a bit too simplistic and is just a
283 // "try to make a fit for all accents" approach, to
284 // make it better we need to know what kind of accent is
285 // used and add to max based on that.
288 dim.asc = font_metrics::ascent('a', font);
290 dim.asc = font_metrics::ascent(ic, font);
292 dim.asc += (font_metrics::maxAscent(font) + 3) / 3;
295 dim.des = font_metrics::descent('a', font);
297 dim.des = font_metrics::descent(ic, font);
301 dim.wid = font_metrics::width(ic, font);
303 dim.asc = font_metrics::maxAscent(font) + 4;
304 dim.des = font_metrics::maxDescent(font) + 4;
305 dim.wid = font_metrics::width(contents, font) + 4;
311 int InsetLatexAccent::lbearing(LyXFont const & font) const
313 return font_metrics::lbearing(ic, font);
317 int InsetLatexAccent::rbearing(LyXFont const & font) const
319 return font_metrics::rbearing(ic, font);
323 bool InsetLatexAccent::displayISO8859_9(PainterInfo & pi, int x, int y) const
325 unsigned char tmpic = ic;
330 if (ic == 'c') tmpic = 0xe7;
331 if (ic == 'C') tmpic = 0xc7;
332 if (ic == 's') tmpic = 0xfe;
333 if (ic == 'S') tmpic = 0xde;
338 if (ic == 'g') tmpic = 0xf0;
339 if (ic == 'G') tmpic = 0xd0;
344 if (ic == 'o') tmpic = 0xf6;
345 if (ic == 'O') tmpic = 0xd6;
346 if (ic == 'u') tmpic = 0xfc;
347 if (ic == 'U') tmpic = 0xdc;
352 if (ic == 'I') tmpic = 0xdd;
366 pi.pain.text(x, y, char(tmpic), pi.base.font);
371 void InsetLatexAccent::drawAccent(PainterInfo const & pi, int x, int y,
374 LyXFont const & font = pi.base.font;
375 x -= font_metrics::center(accent, font);
376 y -= font_metrics::ascent(ic, font);
377 y -= font_metrics::descent(accent, font);
378 y -= font_metrics::height(accent, font) / 2;
379 pi.pain.text(x, y, accent, font);
383 void InsetLatexAccent::draw(PainterInfo & pi, int x, int baseline) const
385 if (lyxrc.font_norm_type == LyXRC::ISO_8859_9)
386 if (displayISO8859_9(pi, x, baseline))
389 // All the manually drawn accents in this function could use an
390 // overhaul. Different ways of drawing (what metrics to use)
391 // should also be considered.
393 LyXFont font = pi.base.font;
394 if (lyxrc.font_norm_type == LyXRC::ISO_10646_1)
395 font.setLanguage(english_language);
398 int x2 = int(x + (rbearing(font) - lbearing(font)) / 2);
403 hg = font_metrics::maxDescent(font);
404 y = baseline - dim_.asc;
405 if (font.shape() == LyXFont::ITALIC_SHAPE)
406 x2 += int(0.8 * hg); // italic
413 double hg35 = hg * 0.6;
415 // display with proper accent mark
417 pi.pain.text(x, baseline, ic, font);
420 int tmpvar = baseline - font_metrics::ascent('i', font);
422 if (font.shape() == LyXFont::ITALIC_SHAPE)
423 tmpx += int(0.8 * hg); // italic
424 lyxerr[Debug::KEY] << "Removing dot." << endl;
425 // remove the dot first
426 pi.pain.fillRectangle(x + tmpx, tmpvar, dim_.wid,
427 font_metrics::ascent('i', pi.base.font) -
428 font_metrics::ascent('x', pi.base.font) - 1,
430 // the five lines below is a simple hack to
431 // make the display of accent 'i' and 'j'
432 // better. It makes the accent be written
433 // closer to the top of the dot-less 'i' or 'j'.
434 char tmpic = ic; // store the ic when we
435 ic = 'x'; // calculates the ascent of
437 int asc = ascent(); // the dot-less version (here: 'x')
438 ic = tmpic; // set the orig ic back
439 y = baseline - asc; // update to new y coord.
442 // now the rest - draw within (x, y, x + wid, y + hg)
445 drawAccent(pi, x2, baseline, char(0xB4));
449 drawAccent(pi, x2, baseline, char(0x60));
453 drawAccent(pi, x2, baseline, char(0xAF));
457 drawAccent(pi, x2, baseline, '~');
460 case UNDERBAR: // underbar 0x5F
461 pi.pain.text(x2 - font_metrics::center(0x5F, font), baseline,
466 pi.pain.text(x2 - font_metrics::center(0xB8, font), baseline,
471 pi.pain.text(x2 - font_metrics::center('.', font),
472 int(baseline + 1.5 * font_metrics::height('.', font)),
477 drawAccent(pi, x2, baseline, '.');
481 drawAccent(pi, x2, baseline, char(0xB0));
485 pi.pain.arc(int(x2 + hg35), y + hg / 2, 2 * hg, hg, 0, 360 * 32,
490 pi.pain.arc(int(x2 - hg / 2), y, hg, hg, 0, -360*32,
496 xp[0] = int(x2 - hg35); yp[0] = int(y + hg35);
497 xp[1] = int(x2); yp[1] = int(y + hg);
498 xp[2] = int(x2 + hg35); yp[2] = int(y + hg35);
499 pi.pain.lines(xp, yp, 3, LColor::foreground);
503 case SPECIAL_CARON: {
505 case 'L': dim_.wid = int(4.0 * dim_.wid / 5.0); break;
506 case 't': y -= int(hg35 / 2.0); break;
509 xp[0] = int(x + dim_.wid);
510 yp[0] = int(y + hg35 + hg);
512 xp[1] = int(x + dim_.wid + (hg35 / 2.0));
513 yp[1] = int(y + hg + (hg35 / 2.0));
515 xp[2] = int(x + dim_.wid + (hg35 / 2.0));
518 pi.pain.lines(xp, yp, 3, LColor::foreground);
522 case HUNGARIAN_UMLAUT:
523 drawAccent(pi, x2 - font_metrics::center('´', font), baseline, '´');
524 drawAccent(pi, x2 + font_metrics::center('´', font), baseline, '´');
528 drawAccent(pi, x2, baseline, '"');
532 drawAccent(pi, x2, baseline, 0x5E);
536 // this does probably not look like an ogonek, so
537 // it should certainly be refined
544 yp[1] = y + int(hg35);
546 xp[2] = int(x2 - hg35);
552 pi.pain.lines(xp, yp, 4, LColor::foreground);
561 yp[0] = y + int(3 * hg);
563 xp[1] = int(x + dim_.wid * 0.75);
566 pi.pain.lines(xp, yp, 2, LColor::foreground);
570 case DOT_LESS_I: // dotless-i
571 case DOT_LESS_J: // dotless-j
572 // nothing to do for these
577 pi.pain.fillRectangle(x + 1,
578 baseline - dim_.asc + 1, dim_.wid - 2,
579 dim_.asc + dim_.des - 2,
581 pi.pain.rectangle(x + 1, baseline - dim_.asc + 1,
582 dim_.wid - 2, dim_.asc + dim_.des - 2,
584 pi.pain.text(x + 2, baseline, contents, font);
589 void InsetLatexAccent::write(Buffer const &, ostream & os) const
591 os << "\\i " << contents << "\n";
595 void InsetLatexAccent::read(Buffer const &, LyXLex & lex)
598 contents = lex.getString();
603 int InsetLatexAccent::latex(Buffer const &, ostream & os,
604 OutputParams const &) const
611 int InsetLatexAccent::plaintext(Buffer const &, ostream & os,
612 OutputParams const &) const
619 int InsetLatexAccent::linuxdoc(Buffer const &, ostream & os,
620 OutputParams const &) const
627 int InsetLatexAccent::docbook(Buffer const &, ostream & os,
628 OutputParams const &) const
635 bool InsetLatexAccent::directWrite() const
641 InsetOld::Code InsetLatexAccent::lyxCode() const
643 return InsetOld::ACCENT_CODE;
647 ostream & operator<<(ostream & o, InsetLatexAccent::ACCENT_TYPES at)