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