]> git.lyx.org Git - lyx.git/blob - src/insets/InsetIPAMacro.cpp
Now Inset::dimension is only an access to cache
[lyx.git] / src / insets / InsetIPAMacro.cpp
1 /**
2  * \file InsetIPAMacro.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Spitzmüller
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "InsetIPAMacro.h"
14
15 #include "Buffer.h"
16 #include "BufferParams.h"
17 #include "Dimension.h"
18 #include "Encoding.h"
19 #include "Font.h"
20 #include "FuncRequest.h"
21 #include "FuncStatus.h"
22 #include "LaTeXFeatures.h"
23 #include "Lexer.h"
24 #include "MetricsInfo.h"
25 #include "output_xhtml.h"
26 #include "texstream.h"
27
28 #include "frontends/FontMetrics.h"
29 #include "frontends/Painter.h"
30
31 #include "support/debug.h"
32 #include "support/docstream.h"
33 #include "support/gettext.h"
34 #include "support/Translator.h"
35
36 using namespace std;
37
38 namespace lyx {
39
40 namespace {
41
42 typedef Translator<string, InsetIPADecoParams::Type> IPADecoTranslator;
43 typedef Translator<docstring, InsetIPADecoParams::Type> IPADecoTranslatorLoc;
44
45 IPADecoTranslator const init_ipadecotranslator()
46 {
47         IPADecoTranslator translator("toptiebar", InsetIPADecoParams::Toptiebar);
48         translator.addPair("bottomtiebar", InsetIPADecoParams::Bottomtiebar);
49         return translator;
50 }
51
52
53 IPADecoTranslatorLoc const init_ipadecotranslator_loc()
54 {
55         IPADecoTranslatorLoc translator(_("Top tie bar"), InsetIPADecoParams::Toptiebar);
56         translator.addPair(_("Bottom tie bar"), InsetIPADecoParams::Bottomtiebar);
57         return translator;
58 }
59
60
61 IPADecoTranslator const & ipadecotranslator()
62 {
63         static IPADecoTranslator const decotranslator =
64                         init_ipadecotranslator();
65         return decotranslator;
66 }
67
68
69 IPADecoTranslatorLoc const & ipadecotranslator_loc()
70 {
71         static IPADecoTranslatorLoc const translator =
72             init_ipadecotranslator_loc();
73         return translator;
74 }
75
76
77 typedef Translator<string, InsetIPAChar::Kind> IPACharTranslator;
78
79 IPACharTranslator const init_ipachartranslator()
80 {
81         IPACharTranslator translator("\\tone{51}", InsetIPAChar::TONE_FALLING);
82         translator.addPair("\\tone{15}", InsetIPAChar::TONE_RISING);
83         translator.addPair("\\tone{45}", InsetIPAChar::TONE_HIGH_RISING);
84         translator.addPair("\\tone{12}", InsetIPAChar::TONE_LOW_RISING);
85         translator.addPair("\\tone{454}", InsetIPAChar::TONE_HIGH_RISING_FALLING);
86         return translator;
87 }
88
89
90 IPACharTranslator const & ipachartranslator()
91 {
92         static IPACharTranslator const chartranslator =
93             init_ipachartranslator();
94         return chartranslator;
95 }
96
97 } // anon
98
99
100 InsetIPADecoParams::InsetIPADecoParams()
101         : type(Bottomtiebar)
102 {}
103
104
105 void InsetIPADecoParams::write(ostream & os) const
106 {
107         string const label = ipadecotranslator().find(type);
108         os << "IPADeco " << label << "\n";
109 }
110
111
112 void InsetIPADecoParams::read(Lexer & lex)
113 {
114         string label;
115         lex >> label;
116         if (lex)
117                 type = ipadecotranslator().find(label);
118 }
119
120
121 /////////////////////////////////////////////////////////////////////
122 //
123 // InsetIPADeco
124 //
125 /////////////////////////////////////////////////////////////////////
126
127 InsetIPADeco::InsetIPADeco(Buffer * buf, string const & label)
128         : InsetCollapsable(buf)
129 {
130         setDrawFrame(true);
131         setFrameColor(Color_insetframe);
132         params_.type = ipadecotranslator().find(label);
133 }
134
135
136 InsetIPADeco::~InsetIPADeco()
137 {}
138
139
140 docstring InsetIPADeco::layoutName() const
141 {
142         return from_ascii("IPADeco:" + ipadecotranslator().find(params_.type));
143 }
144
145
146 void InsetIPADeco::metrics(MetricsInfo & mi, Dimension & dim) const
147 {
148         InsetText::metrics(mi, dim);
149
150         if (params_.type == InsetIPADecoParams::Toptiebar) {
151                 // consider width of the inset label
152                 FontInfo font(getLayout().labelfont());
153                 font.realize(sane_font);
154                 font.decSize();
155                 font.decSize();
156                 int w = 0;
157                 int a = 0;
158                 int d = 0;
159                 docstring const label(1, char_type(0x2040));
160                 theFontMetrics(font).rectText(label, w, a, d);
161                 dim.asc += int(a * 0.5);
162         }
163         if (params_.type == InsetIPADecoParams::Bottomtiebar) {
164                 // consider width of the inset label
165                 FontInfo font(getLayout().labelfont());
166                 font.realize(sane_font);
167                 font.decSize();
168                 font.decSize();
169                 int w = 0;
170                 int a = 0;
171                 int d = 0;
172                 docstring const label(1, char_type(0x203f));
173                 theFontMetrics(font).rectText(label, w, a, d);
174                 dim.des += int(d * 1.5);
175         }
176
177         // cache the inset dimension
178         setDimCache(mi, dim);
179 }
180
181
182 void InsetIPADeco::draw(PainterInfo & pi, int x, int y) const
183 {
184         // draw the text
185         InsetCollapsable::draw(pi, x, y);
186
187         // draw the inset marker
188         drawMarkers(pi, x, y);
189
190         Dimension const dim = dimension(*pi.base.bv);
191
192         if (params_.type == InsetIPADecoParams::Toptiebar) {
193                 FontInfo font(getLayout().labelfont());
194                 font.realize(sane_font);
195                 font.decSize();
196                 font.decSize();
197                 int w = 0;
198                 int a = 0;
199                 int d = 0;
200                 int asc = dim.ascent();
201                 docstring const label(1, char_type(0x2040));
202                 theFontMetrics(font).rectText(label, w, a, d);
203                 int const ww = max(dim.wid, w);
204                 pi.pain.rectText(x + (ww - w) / 2, y - int(asc / 2.5),
205                         label, font, Color_none, Color_none);
206         }
207
208         if (params_.type == InsetIPADecoParams::Bottomtiebar) {
209                 FontInfo font(getLayout().labelfont());
210                 font.realize(sane_font);
211                 font.decSize();
212                 font.decSize();
213                 int w = 0;
214                 int a = 0;
215                 int d = 0;
216                 int desc = dim.descent();
217                 docstring const label(1, char_type(0x203f));
218                 theFontMetrics(font).rectText(label, w, a, d);
219                 int const ww = max(dim.wid, w);
220                 pi.pain.rectText(x + (ww - w) / 2, y + int(desc / 1.5),
221                         label, font, Color_none, Color_none);
222         }
223 }
224
225
226 void InsetIPADeco::write(ostream & os) const
227 {
228         params_.write(os);
229         InsetCollapsable::write(os);
230 }
231
232
233 void InsetIPADeco::read(Lexer & lex)
234 {
235         params_.read(lex);
236         InsetCollapsable::read(lex);
237 }
238
239
240 void InsetIPADeco::doDispatch(Cursor & cur, FuncRequest & cmd)
241 {
242         switch (cmd.action()) {
243         case LFUN_QUOTE_INSERT: {
244                 FuncRequest fr(LFUN_SELF_INSERT, "\"");
245                 InsetText::doDispatch(cur, fr);
246                 break;
247         }
248         default:
249                 InsetText::doDispatch(cur, cmd);
250                 break;
251         }
252 }
253
254
255 bool InsetIPADeco::getStatus(Cursor & cur, FuncRequest const & cmd,
256                 FuncStatus & flag) const
257 {
258         switch (cmd.action()) {
259         case LFUN_SCRIPT_INSERT: {
260                 if (cmd.argument() == "subscript") {
261                         flag.setEnabled(false);
262                         return true;
263                 }
264                 break;
265         }
266         case LFUN_IN_IPA:
267                 flag.setEnabled(true);
268                 return true;
269                 break;
270         default:
271                 break;
272         }
273         return InsetText::getStatus(cur, cmd, flag);
274 }
275
276
277 void InsetIPADeco::latex(otexstream & os, OutputParams const & runparams) const
278 {
279         if (params_.type == InsetIPADecoParams::Toptiebar)
280                 os << "\\texttoptiebar{";
281         else if (params_.type == InsetIPADecoParams::Bottomtiebar)
282                 os << "\\textbottomtiebar{";
283         InsetCollapsable::latex(os, runparams);
284         os << "}";
285 }
286
287
288 int InsetIPADeco::plaintext(odocstringstream & os,
289                             OutputParams const & runparams, size_t max_length) const
290 {
291         odocstringstream ods;
292         int h = (int)(InsetCollapsable::plaintext(ods, runparams, max_length) / 2);
293         docstring result = ods.str();
294         docstring const before = result.substr(0, h);
295         docstring const after = result.substr(h, result.size());
296
297         if (params_.type == InsetIPADecoParams::Toptiebar) {
298                 os << before;
299                 os.put(0x0361);
300                 os << after;
301         }
302         else if (params_.type == InsetIPADecoParams::Bottomtiebar) {
303                 os << before;
304                 os.put(0x035c);
305                 os << after;
306         }
307         return result.size();
308 }
309
310
311 int InsetIPADeco::docbook(odocstream & os, OutputParams const & runparams) const
312 {
313         // FIXME: Any docbook option here?
314         return InsetCollapsable::docbook(os, runparams);
315 }
316
317
318 docstring InsetIPADeco::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
319 {
320         // FIXME: Like in plaintext, the combining characters "&#x361;" (toptiebar)
321         // or "&#x35c;" (bottomtiebar) would need to be inserted just in the mid
322         // of the text string. (How) can this be done with the xhtml stream?
323         return InsetCollapsable::xhtml(xs, runparams);
324 }
325
326
327 docstring InsetIPADeco::toolTip(BufferView const &, int, int) const
328 {
329         return ipadecotranslator_loc().find(params_.type);
330 }
331
332
333 string InsetIPADeco::params2string(InsetIPADecoParams const & params)
334 {
335         ostringstream data;
336         data << "IPADeco" << ' ';
337         params.write(data);
338         return data.str();
339 }
340
341
342 void InsetIPADeco::string2params(string const & in, InsetIPADecoParams & params)
343 {
344         params = InsetIPADecoParams();
345
346         if (in.empty())
347                 return;
348
349         istringstream data(in);
350         Lexer lex;
351         lex.setStream(data);
352         lex.setContext("InsetIPADeco::string2params");
353         lex >> "IPADeco" >> "toptiebar";
354
355         params.read(lex);
356 }
357
358
359 void InsetIPADeco::validate(LaTeXFeatures & features) const
360 {
361         features.require("tipa");
362         InsetText::validate(features);
363 }
364
365
366 bool InsetIPADeco::insetAllowed(InsetCode code) const
367 {
368         switch (code) {
369         // code that is allowed
370         case ERT_CODE:
371         case IPACHAR_CODE:
372         case SCRIPT_CODE:
373                 return true;
374         default:
375                 return false;
376         }
377 }
378
379 /////////////////////////////////////////////////////////////////////////
380 //
381 // InsetIPAChar
382 //
383 /////////////////////////////////////////////////////////////////////////
384
385
386 InsetIPAChar::InsetIPAChar(Kind k)
387         : Inset(0), kind_(k)
388 {}
389
390
391 InsetIPAChar::Kind InsetIPAChar::kind() const
392 {
393         return kind_;
394 }
395
396
397 void InsetIPAChar::metrics(MetricsInfo & mi, Dimension & dim) const
398 {
399         frontend::FontMetrics const & fm =
400                 theFontMetrics(mi.base.font);
401         dim.asc = fm.maxAscent();
402         dim.des = fm.maxDescent();
403
404         string s;
405         switch (kind_) {
406                 case TONE_FALLING:
407                 case TONE_RISING:
408                 case TONE_HIGH_RISING:
409                 case TONE_LOW_RISING:
410                 case TONE_HIGH_RISING_FALLING:
411                         s = "_";
412                         break;
413         }
414         docstring ds(s.begin(), s.end());
415         dim.wid = fm.width(ds);
416         setDimCache(mi, dim);
417 }
418
419
420 void InsetIPAChar::draw(PainterInfo & pi, int x, int y) const
421 {
422         FontInfo font = pi.base.font;
423         frontend::FontMetrics const & fm =
424                         theFontMetrics(font);
425
426         switch (kind_) {
427         case TONE_FALLING:
428         {
429                 int w = fm.width(char_type('-'));
430                 int h = fm.ascent(char_type('M'));
431                 int x2 = x + w;
432                 int y2 = y - h;
433
434                 pi.pain.line(x2, y2, x2, y, Color_foreground);
435                 pi.pain.line(x2, y, x, y2, Color_foreground);
436                 break;
437         }
438         case TONE_RISING:
439         {
440                 int w = fm.width(char_type('-'));
441                 int h = fm.ascent(char_type('M'));
442                 int x2 = x + w;
443                 int y2 = y - h;
444
445                 pi.pain.line(x2, y, x2, y2, Color_foreground);
446                 pi.pain.line(x2, y2, x, y, Color_foreground);
447                 break;
448         }
449         case TONE_HIGH_RISING:
450         {
451                 int w = fm.width(char_type('-'));
452                 int h = fm.ascent(char_type('M'));
453                 int x2 = x + w;
454                 int y2 = y - h;
455                 int y3 = y - int(h * 0.75);
456
457                 pi.pain.line(x2, y, x2, y2, Color_foreground);
458                 pi.pain.line(x2, y2, x, y3, Color_foreground);
459                 break;
460         }
461         case TONE_LOW_RISING:
462         {
463                 int w = fm.width(char_type('-'));
464                 int h = fm.ascent(char_type('M'));
465                 int x2 = x + w;
466                 int y2 = y - h;
467                 int y3 = y - int(h * 0.25);
468
469                 pi.pain.line(x2, y, x2, y2, Color_foreground);
470                 pi.pain.line(x2, y3, x, y, Color_foreground);
471                 break;
472         }
473         case TONE_HIGH_RISING_FALLING:
474         {
475                 int w = fm.width(char_type('-'));
476                 int h = fm.ascent(char_type('M'));
477                 int x2 = x + w;
478                 int y2 = y - h;
479                 int x3 = x + int(w * 0.5);
480                 int y3 = y - int(h * 0.75);
481
482                 pi.pain.line(x2, y, x2, y2, Color_foreground);
483                 pi.pain.line(x2, y3, x3, y2, Color_foreground);
484                 pi.pain.line(x3, y2, x, y3, Color_foreground);
485                 break;
486         }
487         }
488 }
489
490
491 void InsetIPAChar::write(ostream & os) const
492 {
493         string const command = ipachartranslator().find(kind_);
494         if (command.empty()) {
495                 LYXERR0("InsetIPAChar::write: Unknown type");
496                 return;
497         }
498         os << "\\IPAChar " << command << "\n";
499 }
500
501
502 void InsetIPAChar::read(Lexer & lex)
503 {
504         lex.next();
505         string const command = lex.getString();
506         kind_ = ipachartranslator().find(command);
507 }
508
509
510 void InsetIPAChar::latex(otexstream & os,
511                          OutputParams const &) const
512 {
513         string const command = ipachartranslator().find(kind_);
514         os << command;
515 }
516
517
518 int InsetIPAChar::plaintext(odocstringstream & os, OutputParams const &, size_t) const
519 {
520         switch (kind_) {
521         case TONE_FALLING:
522                 os.put(0x02e5);
523                 os.put(0x02e9);
524                 return 2;
525         case TONE_RISING:
526                 os.put(0x02e9);
527                 os.put(0x02e5);
528                 return 2;
529         case TONE_HIGH_RISING:
530                 os.put(0x02e7);
531                 os.put(0x02e5);
532                 return 2;
533         case TONE_LOW_RISING:
534                 os.put(0x02e9);
535                 os.put(0x02e7);
536                 return 2;
537         case TONE_HIGH_RISING_FALLING:
538                 os.put(0x02e8);
539                 os.put(0x02e5);
540                 os.put(0x02e8);
541                 return 3;
542         }
543         return 0;
544 }
545
546
547 int InsetIPAChar::docbook(odocstream & /*os*/, OutputParams const &) const
548 {
549         switch (kind_) {
550         // FIXME
551         LYXERR0("IPA tone macros not yet implemented with DocBook!");
552         case TONE_FALLING:
553         case TONE_RISING:
554         case TONE_HIGH_RISING:
555         case TONE_LOW_RISING:
556         case TONE_HIGH_RISING_FALLING:
557                 break;
558         }
559         return 0;
560 }
561
562
563 docstring InsetIPAChar::xhtml(XHTMLStream & xs, OutputParams const &) const
564 {
565         switch (kind_) {
566         case TONE_FALLING:
567                 xs << XHTMLStream::ESCAPE_NONE << "&#x2e5;"
568                    << XHTMLStream::ESCAPE_NONE << "&#x2e9;";
569                 break;
570         case TONE_RISING:
571                 xs << XHTMLStream::ESCAPE_NONE << "&#x2e9;"
572                    << XHTMLStream::ESCAPE_NONE << "&#x2e5;";
573                 break;
574         case TONE_HIGH_RISING:
575                 xs << XHTMLStream::ESCAPE_NONE << "&#x2e7;"
576                    << XHTMLStream::ESCAPE_NONE << "&#x2e5;";
577                 break;
578         case TONE_LOW_RISING:
579                 xs << XHTMLStream::ESCAPE_NONE << "&#x2e9;"
580                    << XHTMLStream::ESCAPE_NONE << "&#x2e7;";
581                 break;
582         case TONE_HIGH_RISING_FALLING:
583                 xs << XHTMLStream::ESCAPE_NONE << "&#x2e8;"
584                    << XHTMLStream::ESCAPE_NONE << "&#x2e5;"
585                    << XHTMLStream::ESCAPE_NONE << "&#x2e8;";
586                 break;
587         }
588         return docstring();
589 }
590
591
592 void InsetIPAChar::toString(odocstream & os) const
593 {
594         odocstringstream ods;
595         plaintext(ods, OutputParams(0));
596         os << ods.str();
597 }
598
599
600 void InsetIPAChar::forOutliner(docstring & os, size_t const, bool const) const
601 {
602         odocstringstream ods;
603         plaintext(ods, OutputParams(0));
604         os += ods.str();
605 }
606
607
608 void InsetIPAChar::validate(LaTeXFeatures & features) const
609 {
610         switch (kind_) {
611         case TONE_FALLING:
612         case TONE_RISING:
613         case TONE_HIGH_RISING:
614         case TONE_LOW_RISING:
615         case TONE_HIGH_RISING_FALLING:
616                 features.require("tone");
617                 break;
618         default:
619                 break;
620         }
621 }
622
623
624 } // namespace lyx