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