]> git.lyx.org Git - lyx.git/blob - src/insets/InsetIPAMacro.cpp
Amend 6c3447c8: FindAdv: sometimes a space is added on some math symbols
[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 "MetricsInfo.h"
25 #include "xml.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/Lexer.h"
35 #include "support/Translator.h"
36
37 using namespace std;
38
39 namespace lyx {
40
41 using support::Lexer;
42
43 namespace {
44
45 typedef Translator<string, InsetIPADecoParams::Type> IPADecoTranslator;
46 typedef Translator<docstring, InsetIPADecoParams::Type> IPADecoTranslatorLoc;
47
48 IPADecoTranslator const init_ipadecotranslator()
49 {
50         IPADecoTranslator translator("toptiebar", InsetIPADecoParams::Toptiebar);
51         translator.addPair("bottomtiebar", InsetIPADecoParams::Bottomtiebar);
52         return translator;
53 }
54
55
56 IPADecoTranslatorLoc const init_ipadecotranslator_loc()
57 {
58         IPADecoTranslatorLoc translator(_("Top tie bar"), InsetIPADecoParams::Toptiebar);
59         translator.addPair(_("Bottom tie bar"), InsetIPADecoParams::Bottomtiebar);
60         return translator;
61 }
62
63
64 IPADecoTranslator const & ipadecotranslator()
65 {
66         static IPADecoTranslator const decotranslator =
67                         init_ipadecotranslator();
68         return decotranslator;
69 }
70
71
72 IPADecoTranslatorLoc const & ipadecotranslator_loc()
73 {
74         static IPADecoTranslatorLoc const translator =
75             init_ipadecotranslator_loc();
76         return translator;
77 }
78
79
80 typedef Translator<string, InsetIPAChar::Kind> IPACharTranslator;
81
82 IPACharTranslator const init_ipachartranslator()
83 {
84         IPACharTranslator translator("\\tone{51}", InsetIPAChar::TONE_FALLING);
85         translator.addPair("\\tone{15}", InsetIPAChar::TONE_RISING);
86         translator.addPair("\\tone{45}", InsetIPAChar::TONE_HIGH_RISING);
87         translator.addPair("\\tone{12}", InsetIPAChar::TONE_LOW_RISING);
88         translator.addPair("\\tone{454}", InsetIPAChar::TONE_HIGH_RISING_FALLING);
89         return translator;
90 }
91
92
93 IPACharTranslator const & ipachartranslator()
94 {
95         static IPACharTranslator const chartranslator =
96             init_ipachartranslator();
97         return chartranslator;
98 }
99
100 } // namespace
101
102
103 InsetIPADecoParams::InsetIPADecoParams()
104         : type(Bottomtiebar)
105 {}
106
107
108 void InsetIPADecoParams::write(ostream & os) const
109 {
110         string const label = ipadecotranslator().find(type);
111         os << "IPADeco " << label << "\n";
112 }
113
114
115 void InsetIPADecoParams::read(Lexer & lex)
116 {
117         string label;
118         lex >> label;
119         if (lex)
120                 type = ipadecotranslator().find(label);
121 }
122
123
124 /////////////////////////////////////////////////////////////////////
125 //
126 // InsetIPADeco
127 //
128 /////////////////////////////////////////////////////////////////////
129
130 InsetIPADeco::InsetIPADeco(Buffer * buf, string const & label)
131         : InsetCollapsible(buf)
132 {
133         setDrawFrame(true);
134         setFrameColor(Color_insetframe);
135         params_.type = ipadecotranslator().find(label);
136 }
137
138
139 InsetIPADeco::~InsetIPADeco()
140 {}
141
142
143 docstring InsetIPADeco::layoutName() const
144 {
145         return from_ascii("IPADeco:" + ipadecotranslator().find(params_.type));
146 }
147
148
149 void InsetIPADeco::metrics(MetricsInfo & mi, Dimension & dim) const
150 {
151         InsetText::metrics(mi, dim);
152
153         if (params_.type == InsetIPADecoParams::Toptiebar) {
154                 // consider width of the inset label
155                 FontInfo font(getLayout().labelfont());
156                 font.realize(sane_font);
157                 font.decSize();
158                 font.decSize();
159                 int w = 0;
160                 int a = 0;
161                 int d = 0;
162                 docstring const label(1, char_type(0x2040));
163                 theFontMetrics(font).rectText(label, w, a, d);
164                 dim.asc += int(a * 0.5);
165         }
166         if (params_.type == InsetIPADecoParams::Bottomtiebar) {
167                 // consider width of the inset label
168                 FontInfo font(getLayout().labelfont());
169                 font.realize(sane_font);
170                 font.decSize();
171                 font.decSize();
172                 int w = 0;
173                 int a = 0;
174                 int d = 0;
175                 docstring const label(1, char_type(0x203f));
176                 theFontMetrics(font).rectText(label, w, a, d);
177                 dim.des += int(d * 1.5);
178         }
179 }
180
181
182 void InsetIPADeco::draw(PainterInfo & pi, int x, int y) const
183 {
184         // draw the text
185         InsetCollapsible::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         InsetCollapsible::write(os);
230 }
231
232
233 void InsetIPADeco::read(Lexer & lex)
234 {
235         params_.read(lex);
236         InsetCollapsible::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         InsetCollapsible::latex(os, runparams);
284         os << "}";
285 }
286
287
288 namespace {
289 std::pair<docstring, docstring> splitPlainTextInHalves(
290                 const InsetIPADeco * inset, OutputParams const & runparams,
291                 size_t max_length = INT_MAX)
292 {
293         odocstringstream ods;
294         int h = inset->InsetCollapsible::plaintext(ods, runparams, max_length) / 2;
295         docstring result = ods.str();
296         docstring const before = result.substr(0, h);
297         docstring const after = result.substr(h, result.size());
298         return {before, after};
299 }
300 }
301
302
303 int InsetIPADeco::plaintext(odocstringstream & os,
304                             OutputParams const & runparams, size_t max_length) const
305 {
306         docstring before;
307         docstring after;
308         tie(before, after) = splitPlainTextInHalves(this, runparams, max_length);
309
310         if (params_.type == InsetIPADecoParams::Toptiebar) {
311                 os << before;
312                 os.put(0x0361);
313                 os << after;
314         }
315         else if (params_.type == InsetIPADecoParams::Bottomtiebar) {
316                 os << before;
317                 os.put(0x035c);
318                 os << after;
319         }
320         return before.size() + after.size();
321 }
322
323
324 void InsetIPADeco::docbook(XMLStream & xs, OutputParams const & runparams) const
325 {
326         // The special combining character must be put in the middle, between the two other characters.
327         // It will not work if there is anything else than two pure characters, so going back to plaintext.
328         docstring before;
329         docstring after;
330         tie(before, after) = splitPlainTextInHalves(this, runparams);
331
332         xs << XMLStream::ESCAPE_NONE << before;
333         if (params_.type == InsetIPADecoParams::Toptiebar)
334                 xs << XMLStream::ESCAPE_NONE << "&#x0361;";
335         else if (params_.type == InsetIPADecoParams::Bottomtiebar)
336                 xs << XMLStream::ESCAPE_NONE << "&#x035c;";
337         xs << XMLStream::ESCAPE_NONE << after;
338 }
339
340
341 docstring InsetIPADeco::xhtml(XMLStream & xs, OutputParams const & runparams) const
342 {
343         // The DocBook encoding for this inset has no DocBook tag, but sheer XML (relying on a plaintext
344         // transformation of the inset).
345         docbook(xs, runparams);
346         return docstring();
347 }
348
349
350 docstring InsetIPADeco::toolTip(BufferView const &, int, int) const
351 {
352         return ipadecotranslator_loc().find(params_.type);
353 }
354
355
356 string InsetIPADeco::params2string(InsetIPADecoParams const & params)
357 {
358         ostringstream data;
359         data << "IPADeco" << ' ';
360         params.write(data);
361         return data.str();
362 }
363
364
365 void InsetIPADeco::string2params(string const & in, InsetIPADecoParams & params)
366 {
367         params = InsetIPADecoParams();
368
369         if (in.empty())
370                 return;
371
372         istringstream data(in);
373         Lexer lex;
374         lex.setStream(data);
375         lex.setContext("InsetIPADeco::string2params");
376         lex >> "IPADeco" >> "toptiebar";
377
378         params.read(lex);
379 }
380
381
382 void InsetIPADeco::validate(LaTeXFeatures & features) const
383 {
384         features.require("tipa");
385         InsetText::validate(features);
386 }
387
388
389 bool InsetIPADeco::insetAllowed(InsetCode code) const
390 {
391         switch (code) {
392         // code that is allowed
393         case ERT_CODE:
394         case IPACHAR_CODE:
395         case SCRIPT_CODE:
396                 return true;
397         default:
398                 return false;
399         }
400 }
401
402 /////////////////////////////////////////////////////////////////////////
403 //
404 // InsetIPAChar
405 //
406 /////////////////////////////////////////////////////////////////////////
407
408
409 InsetIPAChar::InsetIPAChar(Kind k)
410         : Inset(nullptr), kind_(k)
411 {}
412
413
414 InsetIPAChar::Kind InsetIPAChar::kind() const
415 {
416         return kind_;
417 }
418
419
420 void InsetIPAChar::metrics(MetricsInfo & mi, Dimension & dim) const
421 {
422         frontend::FontMetrics const & fm =
423                 theFontMetrics(mi.base.font);
424         dim.asc = fm.maxAscent();
425         dim.des = fm.maxDescent();
426
427         string s;
428         switch (kind_) {
429                 case TONE_FALLING:
430                 case TONE_RISING:
431                 case TONE_HIGH_RISING:
432                 case TONE_LOW_RISING:
433                 case TONE_HIGH_RISING_FALLING:
434                         s = "_";
435                         break;
436         }
437         docstring ds(s.begin(), s.end());
438         dim.wid = fm.width(ds);
439 }
440
441
442 void InsetIPAChar::draw(PainterInfo & pi, int x, int y) const
443 {
444         FontInfo font = pi.base.font;
445         frontend::FontMetrics const & fm =
446                         theFontMetrics(font);
447
448         switch (kind_) {
449         case TONE_FALLING:
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
456                 pi.pain.line(x2, y2, x2, y, Color_foreground);
457                 pi.pain.line(x2, y, x, y2, Color_foreground);
458                 break;
459         }
460         case TONE_RISING:
461         {
462                 int w = fm.width(char_type('-'));
463                 int h = fm.ascent(char_type('M'));
464                 int x2 = x + w;
465                 int y2 = y - h;
466
467                 pi.pain.line(x2, y, x2, y2, Color_foreground);
468                 pi.pain.line(x2, y2, x, y, Color_foreground);
469                 break;
470         }
471         case TONE_HIGH_RISING:
472         {
473                 int w = fm.width(char_type('-'));
474                 int h = fm.ascent(char_type('M'));
475                 int x2 = x + w;
476                 int y2 = y - h;
477                 int y3 = y - int(h * 0.75);
478
479                 pi.pain.line(x2, y, x2, y2, Color_foreground);
480                 pi.pain.line(x2, y2, x, y3, Color_foreground);
481                 break;
482         }
483         case TONE_LOW_RISING:
484         {
485                 int w = fm.width(char_type('-'));
486                 int h = fm.ascent(char_type('M'));
487                 int x2 = x + w;
488                 int y2 = y - h;
489                 int y3 = y - int(h * 0.25);
490
491                 pi.pain.line(x2, y, x2, y2, Color_foreground);
492                 pi.pain.line(x2, y3, x, y, Color_foreground);
493                 break;
494         }
495         case TONE_HIGH_RISING_FALLING:
496         {
497                 int w = fm.width(char_type('-'));
498                 int h = fm.ascent(char_type('M'));
499                 int x2 = x + w;
500                 int y2 = y - h;
501                 int x3 = x + int(w * 0.5);
502                 int y3 = y - int(h * 0.75);
503
504                 pi.pain.line(x2, y, x2, y2, Color_foreground);
505                 pi.pain.line(x2, y3, x3, y2, Color_foreground);
506                 pi.pain.line(x3, y2, x, y3, Color_foreground);
507                 break;
508         }
509         }
510 }
511
512
513 void InsetIPAChar::write(ostream & os) const
514 {
515         string const command = ipachartranslator().find(kind_);
516         if (command.empty()) {
517                 LYXERR0("InsetIPAChar::write: Unknown type");
518                 return;
519         }
520         os << "\\IPAChar " << command << "\n";
521 }
522
523
524 void InsetIPAChar::read(Lexer & lex)
525 {
526         lex.next();
527         string const command = lex.getString();
528         kind_ = ipachartranslator().find(command);
529 }
530
531
532 void InsetIPAChar::latex(otexstream & os,
533                          OutputParams const &) const
534 {
535         string const command = ipachartranslator().find(kind_);
536         os << command;
537 }
538
539
540 int InsetIPAChar::plaintext(odocstringstream & os, OutputParams const &, size_t) const
541 {
542         switch (kind_) {
543         case TONE_FALLING:
544                 os.put(0x02e5);
545                 os.put(0x02e9);
546                 return 2;
547         case TONE_RISING:
548                 os.put(0x02e9);
549                 os.put(0x02e5);
550                 return 2;
551         case TONE_HIGH_RISING:
552                 os.put(0x02e7);
553                 os.put(0x02e5);
554                 return 2;
555         case TONE_LOW_RISING:
556                 os.put(0x02e9);
557                 os.put(0x02e7);
558                 return 2;
559         case TONE_HIGH_RISING_FALLING:
560                 os.put(0x02e8);
561                 os.put(0x02e5);
562                 os.put(0x02e8);
563                 return 3;
564         }
565         return 0;
566 }
567
568
569 namespace {
570 std::string ipaCharToXMLEntity(InsetIPAChar::Kind kind) {
571         switch (kind) {
572         case InsetIPAChar::Kind::TONE_FALLING:
573                 return "&#x2e5;&#x2e9;";
574         case InsetIPAChar::Kind::TONE_RISING:
575                 return "&#x2e9;&#x2e5;";
576         case InsetIPAChar::Kind::TONE_HIGH_RISING:
577                 return "&#x2e7;&#x2e5;";
578         case InsetIPAChar::Kind::TONE_LOW_RISING:
579                 return "&#x2e9;&#x2e7;";
580         case InsetIPAChar::Kind::TONE_HIGH_RISING_FALLING:
581                 return "&#x2e8;&#x2e5;&#x2e8;";
582         }
583         return "";
584 }
585 }
586
587
588 void InsetIPAChar::docbook(XMLStream & xs, OutputParams const &) const
589 {
590         xs << XMLStream::ESCAPE_NONE << from_ascii(ipaCharToXMLEntity(kind()));
591 }
592
593
594 docstring InsetIPAChar::xhtml(XMLStream & xs, OutputParams const &) const
595 {
596         xs << XMLStream::ESCAPE_NONE << from_ascii(ipaCharToXMLEntity(kind()));
597         return docstring();
598 }
599
600
601 void InsetIPAChar::toString(odocstream & os) const
602 {
603         odocstringstream ods;
604         plaintext(ods, OutputParams(0));
605         os << ods.str();
606 }
607
608
609 void InsetIPAChar::forOutliner(docstring & os, size_t const, bool const) const
610 {
611         odocstringstream ods;
612         plaintext(ods, OutputParams(0));
613         os += ods.str();
614 }
615
616
617 void InsetIPAChar::validate(LaTeXFeatures & features) const
618 {
619         switch (kind_) {
620         case TONE_FALLING:
621         case TONE_RISING:
622         case TONE_HIGH_RISING:
623         case TONE_LOW_RISING:
624         case TONE_HIGH_RISING_FALLING:
625                 features.require("tone");
626                 break;
627         default:
628                 break;
629         }
630 }
631
632
633 } // namespace lyx