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