]> git.lyx.org Git - lyx.git/blob - src/insets/insetlatexaccent.C
5d3928ee93f89ec80236216f612a1df29f4e10eb
[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 "error.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);
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.print("InsetLaTexAccent: weird error.");
217                         break;
218                 }
219                 //ic = (modtype == DOT_LESS_J ? 'j' : 'i');
220                 lyxerr.debug("Contents: [" + contents + "], ic: " + ic 
221                              + ", top: " + tostr(plusasc) 
222                              + ", bot: " + tostr(plusdesc) 
223                              + ", dot: " + tostr(remdot) 
224                              + ", mod: " + tostr(modtype));
225         // Special case for space
226         } else if (contents[3] == '}') {
227                 ic = ' ';
228         } else {
229                 int i = 3;
230                 
231                 // now get the char
232                 ic = contents[3]; // i will always be 3 here
233
234                 // ic should now be a alfa-char or '\\'
235                 if (ic == '\\') {
236                         ic = contents[++i]; // will only allow \<foo>{\i} and \<foo>{\j}
237                         if (ic == 'i' || ic == 'j')
238                                 remdot = true;
239                         else
240                                 return;
241                 } else if ( (ic=='i'|| ic=='j') && contents[4]=='}') {
242                     // Do a rewrite: \<foo>{i} --> \<foo>{\i}
243                     string temp=contents;
244                     temp.erase(3, string::npos);
245                     temp+='\\';
246                         temp+=char(ic);
247                     for(string::size_type j = 4; j < contents.length(); ++j)
248                                 temp+=contents[j];
249                     contents=temp;
250                     i++;
251                         remdot=true;
252                 }    
253
254                 // demand a '}' at the end
255                 if (contents[++i] != '}' && contents[++i]) return;
256                                            
257                 // fine, the char is properly decoded now (hopefully)
258                 lyxerr.debug("Contents: [" + contents + "], ic: " + ic
259                              + ", top: " + tostr(plusasc) 
260                              + ", bot: " + tostr(plusdesc) 
261                              + ", dot: " + tostr(remdot) 
262                              + ", mod: " + tostr(modtype));
263         }
264         candisp = true;
265 }
266
267
268 int InsetLatexAccent::Ascent(LyXFont const &font) const
269 {
270         int max;
271         if (candisp) {
272                 if (ic == ' ')
273                         max = font.ascent('a');
274                 else
275                         max = font.ascent(ic);
276                 if (plusasc) 
277                         max += (font.maxAscent()+3) / 3;
278         } else
279                 max = font.maxAscent()+4;
280         return max;
281 }
282
283
284 int InsetLatexAccent::Descent(LyXFont const &font) const
285 {
286         int max;
287         if (candisp) {
288                 if (ic==' ') 
289                         max = font.descent('a');
290                 else 
291                         max = font.descent(ic);
292                 if (plusdesc)
293                         max += 3;
294         } else
295                 max = font.maxDescent() + 4;
296         return max;
297 }
298
299
300 int InsetLatexAccent::Width(LyXFont const &font) const
301 {
302         if (candisp)
303                 return font.textWidth(&ic, 1);
304         else
305                 return font.stringWidth(contents) + 4;
306 }
307
308
309
310 bool InsetLatexAccent::DisplayISO8859_9(LyXFont font,
311                             LyXScreen &scr,
312                             int baseline, 
313                             float &x)
314 {
315         unsigned char tmpic = ic;
316         
317         switch (modtype) {
318         case CEDILLA:
319         {
320                 if (ic == 'c') tmpic = 0xe7;
321                 if (ic == 'C') tmpic = 0xc7;
322                 if (ic == 's') tmpic = 0xfe;
323                 if (ic == 'S') tmpic = 0xde;
324                 break;
325         }
326         case BREVE:
327         {       if (ic == 'g') tmpic = 0xf0;
328                 if (ic == 'G') tmpic = 0xd0;
329                 break;
330         }
331         case UMLAUT:
332         {
333                 if (ic == 'o') tmpic = 0xf6;
334                 if (ic == 'O') tmpic = 0xd6;
335                 if (ic == 'u') tmpic = 0xfc;
336                 if (ic == 'U') tmpic = 0xdc;
337                 break;
338         }
339         case DOT:        if (ic == 'I') tmpic = 0xdd; break;
340         case DOT_LESS_I: tmpic = 0xfd; break;
341         default:         return false;
342         }
343         if (tmpic != ic) {
344                 char ch = char(tmpic);
345                 scr.drawText(font, &ch, 1, baseline, int(x));
346                 x += Width (font);
347                 return true;
348         }
349         else
350                 return false;
351 }
352
353
354 void InsetLatexAccent::Draw(LyXFont font,
355                             LyXScreen &scr,
356                             int baseline, 
357                             float &x)
358 {
359         if (lyxrc->font_norm=="iso8859-9")
360                 if (DisplayISO8859_9 (font, scr, baseline, x))  
361                         return;
362         
363         /* draw it! */ 
364   
365         if (candisp) {
366                 int asc = Ascent(font);
367                 int desc = Descent(font);
368                 int wid = Width(font);
369                 float x2 = x+(float(wid)/2);
370                 float hg35;
371                 int hg, y;
372                 if (plusasc) {
373                         // mark at the top
374                         hg = font.maxDescent();
375                         y = baseline - asc;
376
377                         if (font.shape() == LyXFont::ITALIC_SHAPE) x2 += (4*hg)/5; // italic
378                 } else {
379                         // at the bottom
380                         hg = desc;
381                         y = baseline;
382                 }
383
384                 hg35 = float(hg*3)/5;
385
386                 // display with proper accent mark
387                 // first the letter
388                 scr.drawText(font, &ic, 1, baseline, int(x));
389
390                 GC pgc = GetAccentGC(font, (hg+3)/5);
391
392                 if (remdot) {
393                         int tmpvar = baseline - font.ascent('i');
394                         float tmpx = 0;
395                         if (font.shape() == LyXFont::ITALIC_SHAPE) tmpx += (8*hg)/10; // italic
396                         lyxerr.debug("Removing dot.", Error::ANY);
397                         // remove the dot first
398                         scr.fillRectangle(gc_clear, int(x + tmpx),
399                                           tmpvar, wid,
400                                           font.ascent('i') -
401                                           font.ascent('x')-1);
402                         
403                 }
404                 // now the rest - draw within (x,y, x+wid, y+hg)
405                 switch (modtype) {
406                 case ACUTE:     // acute
407                 {
408                         scr.drawLine(pgc, int(x2), int(y+hg35),
409                                      int(x2+hg35), y);
410                         break;
411                 }
412                 case GRAVE:     // grave
413                 {
414                         scr.drawLine(pgc,int(x2), int(y+hg35),
415                                      int(x2-hg35), y); 
416                         break;
417                 }
418                 case MACRON:     // macron
419                 {
420                         scr.drawLine(pgc,
421                                      int(x2-(3*wid/7)), int(y+(hg/2) + hg35),
422                                      int(x2+(3*wid/7)), int(y+(hg/2) + hg35));
423                         break;
424                 }
425                 case TILDE:     // tilde
426                 {
427                         if (hg35 > 2) --hg35;
428                         x2 += (hg35/2);
429                         XPoint p[4];
430                         p[0].x = int(x2 - 2*hg35); p[0].y = int(y + hg);
431                         p[1].x = int(x2 - hg35);   p[1].y = int(y + hg35);
432                         p[2].x = int(x2);          p[2].y = int(y + hg);
433                         p[3].x = int(x2 + hg35);   p[3].y = int(y + hg35);
434                         scr.drawLines(pgc, p, 4);
435                         break;
436                 }
437                 case UNDERBAR:     // underbar
438                 {
439                         scr.drawLine(pgc,
440                                      int(x2-(3*wid/7)), y+(hg/2),
441                                      int(x2+(3*wid/7)), y+(hg/2));
442                         break;
443                 }
444                 case CEDILLA:     // cedilla
445                 {
446                         XPoint p[4];
447                         p[0].x = int(x2);          p[0].y = y;
448                         p[1].x = int(x2);          p[1].y = y + (hg/3);
449                         p[2].x = int(x2 + (hg/3)); p[2].y = y + (hg/2);
450                         p[3].x = int(x2 - (hg/4)); p[3].y = y + hg;
451                         scr.drawLines(pgc, p, 4);
452                         break;
453                 }
454                 case UNDERDOT:     // underdot
455                 case DOT:    // dot
456                 {
457                         scr.drawArc(pgc,int(x2), y+(hg/2),
458                                     1, 1, 0, 360*64); 
459                         break;
460                 }
461                 case CIRCLE:     // circle
462                 {
463                         scr.drawArc(pgc, int(x2-(hg/2)), y, hg, hg, 0,
464                                     360*64);
465                         break;
466                 }
467                 case TIE:     // tie
468                 {
469                         scr.drawArc(pgc,
470                                     int(x2), y+(hg/4),
471                                     hg, hg, 0, 11519);
472                         break;
473                 }
474                 case BREVE:     // breve
475                 {
476                         scr.drawArc(pgc,
477                                     int(x2-(hg/2)), y-(hg/4),
478                                     hg, hg, 0, -11519);
479                         break;
480                 }
481                 case CARON:    // caron
482                 {
483                         XPoint p[3];
484                         p[0].x = int(x2 - hg35); p[0].y = y;
485                         p[1].x = int(x2);        p[1].y = int(y+hg35);
486                         p[2].x = int(x2 + hg35); p[2].y = y;
487                         scr.drawLines(pgc, p, 3);
488                         break;
489                 }
490                 case SPECIAL_CARON:    // special caron
491                 {
492                         switch (ic) {
493                         case 'L': wid = 4*wid/5; break;
494                         case 't': y -= int(hg35/2); break;
495                         }
496                         XPoint p[3];
497                         p[0].x = int(x+wid);          p[0].y = int(y+hg35+hg);
498                         p[1].x = int(x+wid+(hg35/2)); p[1].y = int(y+ hg+(hg35/2));
499                         p[2].x = int(x+wid+(hg35/2)); p[2].y = y + hg;
500                         scr.drawLines(pgc, p, 3);
501                         break;
502                 }
503                 case HUNGARIAN_UMLAUT:    // hung. umlaut
504                 {
505                         XSegment s[2];
506                         s[0].x1= int(x2-(hg/2));      s[0].y1 = int(y + hg35);
507                         s[0].x2= int(x2+hg35-(hg/2)); s[0].y2 = y;
508                         s[1].x1= int(x2+(hg/2));      s[1].y1 = int(y + hg35);
509                         s[1].x2= int(x2+hg35+(hg/2)); s[1].y2 = y;
510
511                         scr.drawSegments(pgc, s, 2);
512                         break;
513                 }
514                 case UMLAUT:    // umlaut
515                 {
516                         int tmpadd = y;
517                         tmpadd += (remdot) ? ((19*hg)/10) : ((20*hg)/27); // if (remdot) -> i or j
518                         int rad = ((hg*4)/8);
519                         if (rad <= 1) {
520                                 scr.drawPoint(pgc, int(x2-((4*hg)/7)), tmpadd);
521                                 scr.drawPoint(pgc, int(x2+((4*hg)/7)), tmpadd);
522                         } else {
523                                 scr.drawArc(pgc, int(x2-((2*hg)/4)), tmpadd,
524                                             rad-1, rad-1, 0, 360*64);
525                                 scr.drawArc(pgc, int(x2+((2*hg)/4)), tmpadd,
526                                             rad-1, rad-1, 0, 360*64);
527                         }
528                         break;
529                 }
530                 case CIRCUMFLEX:    // circumflex
531                 {
532                         XPoint p[3];
533                         p[0].x = int(x2 - hg35); p[0].y = y + hg;
534                         p[1].x = int(x2);        p[1].y = int(y + hg35);
535                         p[2].x = int(x2 + hg35); p[2].y = y + hg;
536                         scr.drawLines(pgc, p, 3);
537                         break;
538                 }
539                 case OGONEK:    // ogonek
540                 {
541                         // this does probably not look like an ogonek, so
542                         // it should certainly be refined
543                         XPoint p[4];
544                         p[0].x = int(x2);          p[0].y = y;
545                         p[1].x = int(x2);          p[1].y = y + (hg/3);
546                         p[2].x = int(x2 - (hg/3)); p[2].y = y + (hg/2);
547                         p[3].x = int(x2 + (hg/4)); p[3].y = y + hg;
548                         scr.drawLines(pgc, p, 4);
549                         break;
550                 }
551                 case lSLASH:
552                 case LSLASH:
553                 {
554                         XPoint p[2];
555                         p[0].x = int(x);                p[0].y = y+3*hg;
556                         p[1].x = int(x+float(wid)*.75); p[1].y = y+hg;
557                         scr.drawLines(pgc, p, 2);
558                         break;
559                 }
560                 case DOT_LESS_I: // dotless-i
561                 case DOT_LESS_J: // dotless-j
562                 {
563                         // nothing to do for these
564                         break;
565                 }
566                 }
567         } else {
568                 //scr.drawFilledRectangle(int(x+1), baseline - Ascent(font)+1,
569                 //                      Width(font)-2,
570                 //                      Ascent(font)+Descent(font)-2,
571                 //                      FL_GRAY80);
572                 scr.fillRectangle(gc_lighted,
573                                   int(x+1), baseline - Ascent(font)+1,
574                                   Width(font)-2,
575                                   Ascent(font)+Descent(font)-2);
576                 
577                 //scr.drawRectangle(int(x), baseline - Ascent(font),
578                 //                Width(font)-1,
579                 //                Ascent(font)+Descent(font)-1,
580                 //                FL_GRAY80);
581                 scr.drawRectangle(gc_lighted,
582                                   int(x), baseline - Ascent(font),
583                                   Width(font)-1,
584                                   Ascent(font)+Descent(font)-1);
585                 scr.drawString(font, contents, baseline, int(x+2));
586         }
587         x +=  Width(font);
588 }
589
590
591 void InsetLatexAccent::Write(FILE *file)
592 {
593         fprintf(file, "\\i %s\n", contents.c_str());
594 }
595
596
597 void InsetLatexAccent::Read(LyXLex &lex)
598 {
599         lex.EatLine();
600         contents = lex.GetString();
601         checkContents();
602 }
603
604
605 int InsetLatexAccent::Latex(FILE *file, signed char /*fragile*/)
606 {
607         fprintf(file, "%s", contents.c_str());
608         return 0;
609 }
610
611
612 int InsetLatexAccent::Latex(string &file, signed char /*fragile*/)
613 {
614         file += contents;
615         return 0;
616 }
617
618
619 int InsetLatexAccent::Linuxdoc(string &file)
620 {
621         file += contents;
622         return 0;
623 }
624
625
626 int InsetLatexAccent::DocBook(string &file)
627 {
628         file += contents;
629         return 0;
630 }
631
632
633 bool InsetLatexAccent::Deletable() const
634 {
635         return true;
636 }
637
638
639 bool InsetLatexAccent::DirectWrite() const
640 {
641         return true;
642 }
643
644
645 Inset* InsetLatexAccent::Clone()
646 {
647         InsetLatexAccent *result = new InsetLatexAccent(contents);
648         return result;
649 }
650
651
652 Inset::Code InsetLatexAccent::LyxCode() const
653 {
654         return Inset::ACCENT_CODE;
655 }
656
657
658 bool InsetLatexAccent::IsEqual(Inset* other)
659 {
660         if (other && other->LyxCode() == Inset::ACCENT_CODE){
661                 InsetLatexAccent* otheraccent = (InsetLatexAccent*) other;
662                 return (contents == otheraccent->contents);
663         }
664         return false;
665 }