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