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