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