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