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/Application.h"
23 #include "frontends/FontLoader.h"
24 #include "frontends/FontMetrics.h"
25 #include "frontends/Painter.h"
27 #include "support/lstrings.h"
31 using lyx::support::contains;
32 using lyx::support::trim;
40 /* LatexAccent. Proper handling of accented characters */
41 /* This part is done by Ivan Schreter, schreter@ccsun.tuke.sk */
42 /* Later modified by Lars G. Bjønnes, larsbj@lyx.org */
44 InsetLatexAccent::InsetLatexAccent()
49 InsetLatexAccent::InsetLatexAccent(string const & str)
56 auto_ptr<InsetBase> InsetLatexAccent::doClone() const
58 return auto_ptr<InsetBase>(new InsetLatexAccent(contents));
62 void InsetLatexAccent::checkContents()
63 // check, if we know the modifier and can display it ok on screen
67 if (contents.empty() || contents.length() < 2) {
68 lyxerr[Debug::KEY] << "Cannot decode: " << contents << endl;
72 contents = trim(contents);
73 if (contents[0] != '\\') { // demand that first char is a '\\'
74 lyxerr[Debug::KEY] << "Cannot decode: " << contents << endl;
78 lyxerr[Debug::KEY] << "Decode: " << contents << endl;
84 switch (contents[1]) { // second char should be one of these
86 modtype = ACUTE; // acute
87 plusasc = true; // at the top of character
90 modtype = GRAVE; // grave
91 plusasc = true; // at the top
94 modtype = MACRON; // macron
95 plusasc = true; // at the top
98 modtype = TILDE; // tilde
99 plusasc = true; // at the top
101 case 'b': // underbar
102 modtype = UNDERBAR; // underbar
103 plusdesc = true; // at the bottom
106 modtype = CEDILLA; // cedilla
107 plusdesc = true; // at the bottom
109 case 'd': // underdot
110 modtype = UNDERDOT; // underdot
111 plusdesc = true; // at the bottom
114 modtype = CIRCLE; // circle
115 plusasc = true; // at the top
118 modtype = TIE; // tie
119 plusasc = true; // at the top
122 modtype = BREVE; // breve
123 plusasc = true; // at the top
126 modtype = CARON; // caron
127 plusasc = true; // at the top
129 case 'q': // special caron
130 modtype = SPECIAL_CARON; // special caron
131 plusasc = true; // at the top
133 case 'H': // hungarian umlaut
134 modtype = HUNGARIAN_UMLAUT; // hungarian umlaut
135 plusasc = true; // at the top
138 modtype = UMLAUT; // umlaut
139 plusasc = true; // at the top
142 modtype = DOT; // dot
143 plusasc = true; // at the top
145 case '^': // circumflex
146 modtype = CIRCUMFLEX; // circumflex
147 plusasc = true; // at the top
150 modtype = OGONEK; // ogonek
153 case 'i': // dot-less-i
154 modtype = DOT_LESS_I; // dot-less-i
155 plusasc = true; // at the top (not really needed)
158 case 'j': // dot-less-j
159 modtype = DOT_LESS_J; // dot-less-j
160 plusasc = true; // at the top (not really needed)
165 plusasc = true; // at the top (not really needed)
169 plusasc = true; // at the top (not really needed)
172 lyxerr[Debug::KEY] << "Default" << endl;
173 // unknown accent (or something else)
177 // we demand that third char is a '{' (Lgb)
178 if (contents[2] != '{') return;
180 // special clause for \i{}, \j{} \l{} and \L{}
181 if ((modtype == DOT_LESS_I || modtype == DOT_LESS_J
182 || modtype == lSLASH || modtype == LSLASH)
183 && contents[3] == '}') {
185 case DOT_LESS_I: ic = 'i'; break;
186 case DOT_LESS_J: ic = 'j'; break;
187 case lSLASH: ic = 'l'; break;
188 case LSLASH: ic = 'L'; break;
190 // if this happens something is really wrong
191 lyxerr << "InsetLaTexAccent: weird error." << endl;
194 //ic = (modtype == DOT_LESS_J ? 'j' : 'i');
195 lyxerr[Debug::KEY] << "Contents: [" << contents << ']'
197 << ", top: " << plusasc
198 << ", bot: " << plusdesc
199 << ", dot: " << remdot
200 << ", mod: " << modtype << endl;
201 // Special case for space
202 } else if (contents[3] == '}') {
208 ic = contents[3]; // i will always be 3 here
210 // ic should now be a alfa-char or '\\'
212 ic = contents[++i]; // will only allow \<foo>{\i} and \<foo>{\j}
213 if (ic == 'i' || ic == 'j')
217 } else if ((ic == 'i'|| ic == 'j') && contents[4] == '}') {
218 // Do a rewrite: \<foo>{i} --> \<foo>{\i}
219 string temp = contents;
220 temp.erase(3, string::npos);
223 for (string::size_type j = 4;
224 j < contents.length(); ++j)
231 // demand a '}' at the end
232 if (contents[++i] != '}' && contents[++i]) return;
234 // fine, the char is properly decoded now (hopefully)
235 lyxerr[Debug::KEY] << "Contents: [" << contents << ']'
237 << ", top: " << plusasc
238 << ", bot: " << plusdesc
239 << ", dot: " << remdot
240 << ", mod: " << modtype << endl;
246 void InsetLatexAccent::metrics(MetricsInfo & mi, Dimension & dim) const
248 LyXFont & font = mi.base.font;
249 lyx::frontend::FontMetrics const & fm =
250 theApp->fontLoader().metrics(font);
252 // This function is a bit too simplistic and is just a
253 // "try to make a fit for all accents" approach, to
254 // make it better we need to know what kind of accent is
255 // used and add to max based on that.
258 dim.asc = fm.ascent('a');
260 dim.asc = fm.ascent(ic);
262 dim.asc += (fm.maxAscent() + 3) / 3;
265 dim.des = fm.descent('a');
267 dim.des = fm.descent(ic);
271 dim.wid = fm.width(ic);
273 dim.asc = fm.maxAscent() + 4;
274 dim.des = fm.maxDescent() + 4;
275 docstring dcon(contents.begin(), contents.end());
276 dim.wid = fm.width(dcon) + 4;
282 bool InsetLatexAccent::displayISO8859_9(PainterInfo & pi, int x, int y) const
284 unsigned char tmpic = ic;
289 if (ic == 'c') tmpic = '\xe7';
290 if (ic == 'C') tmpic = '\xc7';
291 if (ic == 's') tmpic = '\xfe';
292 if (ic == 'S') tmpic = '\xde';
297 if (ic == 'g') tmpic = '\xf0';
298 if (ic == 'G') tmpic = '\xd0';
303 if (ic == 'o') tmpic = '\xf6';
304 if (ic == 'O') tmpic = '\xd6';
305 if (ic == 'u') tmpic = '\xfc';
306 if (ic == 'U') tmpic = '\xdc';
311 if (ic == 'I') tmpic = '\xdd';
325 pi.pain.text(x, y, char(tmpic), pi.base.font);
330 void InsetLatexAccent::drawAccent(PainterInfo const & pi, int x, int y,
331 char_type accent) const
333 LyXFont const & font = pi.base.font;
334 lyx::frontend::FontMetrics const & fm =
335 theApp->fontLoader().metrics(font);
337 x -= fm.center(accent);
339 y -= fm.descent(accent);
340 y -= fm.height(accent) / 2;
341 pi.pain.text(x, y, accent, font);
345 void InsetLatexAccent::draw(PainterInfo & pi, int x, int baseline) const
347 if (lyxrc.font_norm_type == LyXRC::ISO_8859_9)
348 if (displayISO8859_9(pi, x, baseline))
351 // All the manually drawn accents in this function could use an
352 // overhaul. Different ways of drawing (what metrics to use)
353 // should also be considered.
355 LyXFont font = pi.base.font;
356 if (lyxrc.font_norm_type == LyXRC::ISO_10646_1)
357 font.setLanguage(english_language);
359 lyx::frontend::FontMetrics const & fm =
360 theApp->fontLoader().metrics(font);
363 int x2 = int(x + (fm.rbearing(ic) - fm.lbearing(ic)) / 2);
368 hg = fm.maxDescent();
369 y = baseline - dim_.asc;
370 if (font.shape() == LyXFont::ITALIC_SHAPE)
371 x2 += int(0.8 * hg); // italic
378 double hg35 = hg * 0.6;
380 // display with proper accent mark
382 pi.pain.text(x, baseline, ic, font);
385 int tmpvar = baseline - fm.ascent('i');
387 if (font.shape() == LyXFont::ITALIC_SHAPE)
388 tmpx += int(0.8 * hg); // italic
389 lyxerr[Debug::KEY] << "Removing dot." << endl;
390 // remove the dot first
391 pi.pain.fillRectangle(x + tmpx, tmpvar, dim_.wid,
395 // the five lines below is a simple hack to
396 // make the display of accent 'i' and 'j'
397 // better. It makes the accent be written
398 // closer to the top of the dot-less 'i' or 'j'.
399 char tmpic = ic; // store the ic when we
400 ic = 'x'; // calculates the ascent of
404 int asc = ascent(); // the dot-less version (here: 'x')
405 ic = tmpic; // set the orig ic back
406 y = baseline - asc; // update to new y coord.
409 // now the rest - draw within (x, y, x + wid, y + hg)
412 //drawAccent(pi, x2, baseline, '\xB4');
413 drawAccent(pi, x2, baseline, 0xB4);
417 //drawAccent(pi, x2, baseline, '\x60');
418 drawAccent(pi, x2, baseline, 0x60);
422 //drawAccent(pi, x2, baseline, '\xAF');
423 drawAccent(pi, x2, baseline, 0xAF);
427 drawAccent(pi, x2, baseline, '~');
431 char_type const underbar = 0x5F; //('\x5F');
432 pi.pain.text(x2 - fm.center(underbar),
433 baseline, underbar, font);
438 char_type const cedilla = 0xB8; //('\xB8');
439 pi.pain.text(x2 - fm.center(cedilla),
440 baseline, cedilla, font);
445 pi.pain.text(x2 - fm.center('.'),
446 int(baseline + 1.5 * fm.height('.')),
451 drawAccent(pi, x2, baseline, '.');
455 //drawAccent(pi, x2, baseline, '\xB0');
456 drawAccent(pi, x2, baseline, 0xB0);
460 pi.pain.arc(int(x2 + hg35), y + hg / 2, 2 * hg, hg, 0, 360 * 32,
465 pi.pain.arc(int(x2 - hg / 2), y, hg, hg, 0, -360*32,
471 xp[0] = int(x2 - hg35); yp[0] = int(y + hg35);
472 xp[1] = int(x2); yp[1] = int(y + hg);
473 xp[2] = int(x2 + hg35); yp[2] = int(y + hg35);
474 pi.pain.lines(xp, yp, 3, LColor::foreground);
478 case SPECIAL_CARON: {
480 case 'L': dim_.wid = int(4.0 * dim_.wid / 5.0); break;
481 case 't': y -= int(hg35 / 2.0); break;
484 xp[0] = int(x + dim_.wid);
485 yp[0] = int(y + hg35 + hg);
487 xp[1] = int(x + dim_.wid + (hg35 / 2.0));
488 yp[1] = int(y + hg + (hg35 / 2.0));
490 xp[2] = int(x + dim_.wid + (hg35 / 2.0));
493 pi.pain.lines(xp, yp, 3, LColor::foreground);
497 case HUNGARIAN_UMLAUT:
498 drawAccent(pi, x2 - fm.center('´'), baseline, '´');
499 drawAccent(pi, x2 + fm.center('´'), baseline, '´');
503 drawAccent(pi, x2, baseline, '"');
507 drawAccent(pi, x2, baseline, '\x5E');
511 // this does probably not look like an ogonek, so
512 // it should certainly be refined
519 yp[1] = y + int(hg35);
521 xp[2] = int(x2 - hg35);
527 pi.pain.lines(xp, yp, 4, LColor::foreground);
536 yp[0] = y + int(3 * hg);
538 xp[1] = int(x + dim_.wid * 0.75);
541 pi.pain.lines(xp, yp, 2, LColor::foreground);
545 case DOT_LESS_I: // dotless-i
546 case DOT_LESS_J: // dotless-j
547 // nothing to do for these
552 pi.pain.fillRectangle(x + 1,
553 baseline - dim_.asc + 1, dim_.wid - 2,
554 dim_.asc + dim_.des - 2,
556 pi.pain.rectangle(x + 1, baseline - dim_.asc + 1,
557 dim_.wid - 2, dim_.asc + dim_.des - 2,
559 docstring dcon(contents.begin(), contents.end());
560 pi.pain.text(x + 2, baseline, dcon, font);
565 void InsetLatexAccent::write(Buffer const &, ostream & os) const
567 os << "\\i " << contents << "\n";
571 void InsetLatexAccent::read(Buffer const &, LyXLex & lex)
574 contents = lex.getString();
579 int InsetLatexAccent::latex(Buffer const &, ostream & os,
580 OutputParams const &) const
587 int InsetLatexAccent::plaintext(Buffer const &, ostream & os,
588 OutputParams const &) const
595 int InsetLatexAccent::docbook(Buffer const &, ostream & os,
596 OutputParams const &) const
603 int InsetLatexAccent::textString(Buffer const & buf, ostream & os,
604 OutputParams const & op) const
606 return plaintext(buf, os, op);
610 bool InsetLatexAccent::directWrite() const
616 InsetBase::Code InsetLatexAccent::lyxCode() const
618 return InsetBase::ACCENT_CODE;
622 ostream & operator<<(ostream & o, InsetLatexAccent::ACCENT_TYPES at)