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
init_ipadecotranslator()45 IPADecoTranslator const init_ipadecotranslator()
46 {
47 IPADecoTranslator translator("toptiebar", InsetIPADecoParams::Toptiebar);
48 translator.addPair("bottomtiebar", InsetIPADecoParams::Bottomtiebar);
49 return translator;
50 }
51
52
init_ipadecotranslator_loc()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
ipadecotranslator()61 IPADecoTranslator const & ipadecotranslator()
62 {
63 static IPADecoTranslator const decotranslator =
64 init_ipadecotranslator();
65 return decotranslator;
66 }
67
68
ipadecotranslator_loc()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
init_ipachartranslator()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
ipachartranslator()90 IPACharTranslator const & ipachartranslator()
91 {
92 static IPACharTranslator const chartranslator =
93 init_ipachartranslator();
94 return chartranslator;
95 }
96
97 } // namespace
98
99
InsetIPADecoParams()100 InsetIPADecoParams::InsetIPADecoParams()
101 : type(Bottomtiebar)
102 {}
103
104
write(ostream & os) const105 void InsetIPADecoParams::write(ostream & os) const
106 {
107 string const label = ipadecotranslator().find(type);
108 os << "IPADeco " << label << "\n";
109 }
110
111
read(Lexer & lex)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
InsetIPADeco(Buffer * buf,string const & label)127 InsetIPADeco::InsetIPADeco(Buffer * buf, string const & label)
128 : InsetCollapsible(buf)
129 {
130 setDrawFrame(true);
131 setFrameColor(Color_insetframe);
132 params_.type = ipadecotranslator().find(label);
133 }
134
135
~InsetIPADeco()136 InsetIPADeco::~InsetIPADeco()
137 {}
138
139
layoutName() const140 docstring InsetIPADeco::layoutName() const
141 {
142 return from_ascii("IPADeco:" + ipadecotranslator().find(params_.type));
143 }
144
145
metrics(MetricsInfo & mi,Dimension & dim) const146 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
178
draw(PainterInfo & pi,int x,int y) const179 void InsetIPADeco::draw(PainterInfo & pi, int x, int y) const
180 {
181 // draw the text
182 InsetCollapsible::draw(pi, x, y);
183
184 // draw the inset marker
185 drawMarkers(pi, x, y);
186
187 Dimension const dim = dimension(*pi.base.bv);
188
189 if (params_.type == InsetIPADecoParams::Toptiebar) {
190 FontInfo font(getLayout().labelfont());
191 font.realize(sane_font);
192 font.decSize();
193 font.decSize();
194 int w = 0;
195 int a = 0;
196 int d = 0;
197 int asc = dim.ascent();
198 docstring const label(1, char_type(0x2040));
199 theFontMetrics(font).rectText(label, w, a, d);
200 int const ww = max(dim.wid, w);
201 pi.pain.rectText(x + (ww - w) / 2, y - int(asc / 2.5),
202 label, font, Color_none, Color_none);
203 }
204
205 if (params_.type == InsetIPADecoParams::Bottomtiebar) {
206 FontInfo font(getLayout().labelfont());
207 font.realize(sane_font);
208 font.decSize();
209 font.decSize();
210 int w = 0;
211 int a = 0;
212 int d = 0;
213 int desc = dim.descent();
214 docstring const label(1, char_type(0x203f));
215 theFontMetrics(font).rectText(label, w, a, d);
216 int const ww = max(dim.wid, w);
217 pi.pain.rectText(x + (ww - w) / 2, y + int(desc / 1.5),
218 label, font, Color_none, Color_none);
219 }
220 }
221
222
write(ostream & os) const223 void InsetIPADeco::write(ostream & os) const
224 {
225 params_.write(os);
226 InsetCollapsible::write(os);
227 }
228
229
read(Lexer & lex)230 void InsetIPADeco::read(Lexer & lex)
231 {
232 params_.read(lex);
233 InsetCollapsible::read(lex);
234 }
235
236
doDispatch(Cursor & cur,FuncRequest & cmd)237 void InsetIPADeco::doDispatch(Cursor & cur, FuncRequest & cmd)
238 {
239 switch (cmd.action()) {
240 case LFUN_QUOTE_INSERT: {
241 FuncRequest fr(LFUN_SELF_INSERT, "\"");
242 InsetText::doDispatch(cur, fr);
243 break;
244 }
245 default:
246 InsetText::doDispatch(cur, cmd);
247 break;
248 }
249 }
250
251
getStatus(Cursor & cur,FuncRequest const & cmd,FuncStatus & flag) const252 bool InsetIPADeco::getStatus(Cursor & cur, FuncRequest const & cmd,
253 FuncStatus & flag) const
254 {
255 switch (cmd.action()) {
256 case LFUN_SCRIPT_INSERT: {
257 if (cmd.argument() == "subscript") {
258 flag.setEnabled(false);
259 return true;
260 }
261 break;
262 }
263 case LFUN_IN_IPA:
264 flag.setEnabled(true);
265 return true;
266 break;
267 default:
268 break;
269 }
270 return InsetText::getStatus(cur, cmd, flag);
271 }
272
273
latex(otexstream & os,OutputParams const & runparams) const274 void InsetIPADeco::latex(otexstream & os, OutputParams const & runparams) const
275 {
276 if (params_.type == InsetIPADecoParams::Toptiebar)
277 os << "\\texttoptiebar{";
278 else if (params_.type == InsetIPADecoParams::Bottomtiebar)
279 os << "\\textbottomtiebar{";
280 InsetCollapsible::latex(os, runparams);
281 os << "}";
282 }
283
284
plaintext(odocstringstream & os,OutputParams const & runparams,size_t max_length) const285 int InsetIPADeco::plaintext(odocstringstream & os,
286 OutputParams const & runparams, size_t max_length) const
287 {
288 odocstringstream ods;
289 int h = (int)(InsetCollapsible::plaintext(ods, runparams, max_length) / 2);
290 docstring result = ods.str();
291 docstring const before = result.substr(0, h);
292 docstring const after = result.substr(h, result.size());
293
294 if (params_.type == InsetIPADecoParams::Toptiebar) {
295 os << before;
296 os.put(0x0361);
297 os << after;
298 }
299 else if (params_.type == InsetIPADecoParams::Bottomtiebar) {
300 os << before;
301 os.put(0x035c);
302 os << after;
303 }
304 return result.size();
305 }
306
307
docbook(odocstream & os,OutputParams const & runparams) const308 int InsetIPADeco::docbook(odocstream & os, OutputParams const & runparams) const
309 {
310 // FIXME: Any docbook option here?
311 return InsetCollapsible::docbook(os, runparams);
312 }
313
314
xhtml(XHTMLStream & xs,OutputParams const & runparams) const315 docstring InsetIPADeco::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
316 {
317 // FIXME: Like in plaintext, the combining characters "͡" (toptiebar)
318 // or "͜" (bottomtiebar) would need to be inserted just in the mid
319 // of the text string. (How) can this be done with the xhtml stream?
320 return InsetCollapsible::xhtml(xs, runparams);
321 }
322
323
toolTip(BufferView const &,int,int) const324 docstring InsetIPADeco::toolTip(BufferView const &, int, int) const
325 {
326 return ipadecotranslator_loc().find(params_.type);
327 }
328
329
params2string(InsetIPADecoParams const & params)330 string InsetIPADeco::params2string(InsetIPADecoParams const & params)
331 {
332 ostringstream data;
333 data << "IPADeco" << ' ';
334 params.write(data);
335 return data.str();
336 }
337
338
string2params(string const & in,InsetIPADecoParams & params)339 void InsetIPADeco::string2params(string const & in, InsetIPADecoParams & params)
340 {
341 params = InsetIPADecoParams();
342
343 if (in.empty())
344 return;
345
346 istringstream data(in);
347 Lexer lex;
348 lex.setStream(data);
349 lex.setContext("InsetIPADeco::string2params");
350 lex >> "IPADeco" >> "toptiebar";
351
352 params.read(lex);
353 }
354
355
validate(LaTeXFeatures & features) const356 void InsetIPADeco::validate(LaTeXFeatures & features) const
357 {
358 features.require("tipa");
359 InsetText::validate(features);
360 }
361
362
insetAllowed(InsetCode code) const363 bool InsetIPADeco::insetAllowed(InsetCode code) const
364 {
365 switch (code) {
366 // code that is allowed
367 case ERT_CODE:
368 case IPACHAR_CODE:
369 case SCRIPT_CODE:
370 return true;
371 default:
372 return false;
373 }
374 }
375
376 /////////////////////////////////////////////////////////////////////////
377 //
378 // InsetIPAChar
379 //
380 /////////////////////////////////////////////////////////////////////////
381
382
InsetIPAChar(Kind k)383 InsetIPAChar::InsetIPAChar(Kind k)
384 : Inset(0), kind_(k)
385 {}
386
387
kind() const388 InsetIPAChar::Kind InsetIPAChar::kind() const
389 {
390 return kind_;
391 }
392
393
metrics(MetricsInfo & mi,Dimension & dim) const394 void InsetIPAChar::metrics(MetricsInfo & mi, Dimension & dim) const
395 {
396 frontend::FontMetrics const & fm =
397 theFontMetrics(mi.base.font);
398 dim.asc = fm.maxAscent();
399 dim.des = fm.maxDescent();
400
401 string s;
402 switch (kind_) {
403 case TONE_FALLING:
404 case TONE_RISING:
405 case TONE_HIGH_RISING:
406 case TONE_LOW_RISING:
407 case TONE_HIGH_RISING_FALLING:
408 s = "_";
409 break;
410 }
411 docstring ds(s.begin(), s.end());
412 dim.wid = fm.width(ds);
413 }
414
415
draw(PainterInfo & pi,int x,int y) const416 void InsetIPAChar::draw(PainterInfo & pi, int x, int y) const
417 {
418 FontInfo font = pi.base.font;
419 frontend::FontMetrics const & fm =
420 theFontMetrics(font);
421
422 switch (kind_) {
423 case TONE_FALLING:
424 {
425 int w = fm.width(char_type('-'));
426 int h = fm.ascent(char_type('M'));
427 int x2 = x + w;
428 int y2 = y - h;
429
430 pi.pain.line(x2, y2, x2, y, Color_foreground);
431 pi.pain.line(x2, y, x, y2, Color_foreground);
432 break;
433 }
434 case TONE_RISING:
435 {
436 int w = fm.width(char_type('-'));
437 int h = fm.ascent(char_type('M'));
438 int x2 = x + w;
439 int y2 = y - h;
440
441 pi.pain.line(x2, y, x2, y2, Color_foreground);
442 pi.pain.line(x2, y2, x, y, Color_foreground);
443 break;
444 }
445 case TONE_HIGH_RISING:
446 {
447 int w = fm.width(char_type('-'));
448 int h = fm.ascent(char_type('M'));
449 int x2 = x + w;
450 int y2 = y - h;
451 int y3 = y - int(h * 0.75);
452
453 pi.pain.line(x2, y, x2, y2, Color_foreground);
454 pi.pain.line(x2, y2, x, y3, Color_foreground);
455 break;
456 }
457 case TONE_LOW_RISING:
458 {
459 int w = fm.width(char_type('-'));
460 int h = fm.ascent(char_type('M'));
461 int x2 = x + w;
462 int y2 = y - h;
463 int y3 = y - int(h * 0.25);
464
465 pi.pain.line(x2, y, x2, y2, Color_foreground);
466 pi.pain.line(x2, y3, x, y, Color_foreground);
467 break;
468 }
469 case TONE_HIGH_RISING_FALLING:
470 {
471 int w = fm.width(char_type('-'));
472 int h = fm.ascent(char_type('M'));
473 int x2 = x + w;
474 int y2 = y - h;
475 int x3 = x + int(w * 0.5);
476 int y3 = y - int(h * 0.75);
477
478 pi.pain.line(x2, y, x2, y2, Color_foreground);
479 pi.pain.line(x2, y3, x3, y2, Color_foreground);
480 pi.pain.line(x3, y2, x, y3, Color_foreground);
481 break;
482 }
483 }
484 }
485
486
write(ostream & os) const487 void InsetIPAChar::write(ostream & os) const
488 {
489 string const command = ipachartranslator().find(kind_);
490 if (command.empty()) {
491 LYXERR0("InsetIPAChar::write: Unknown type");
492 return;
493 }
494 os << "\\IPAChar " << command << "\n";
495 }
496
497
read(Lexer & lex)498 void InsetIPAChar::read(Lexer & lex)
499 {
500 lex.next();
501 string const command = lex.getString();
502 kind_ = ipachartranslator().find(command);
503 }
504
505
latex(otexstream & os,OutputParams const &) const506 void InsetIPAChar::latex(otexstream & os,
507 OutputParams const &) const
508 {
509 string const command = ipachartranslator().find(kind_);
510 os << command;
511 }
512
513
plaintext(odocstringstream & os,OutputParams const &,size_t) const514 int InsetIPAChar::plaintext(odocstringstream & os, OutputParams const &, size_t) const
515 {
516 switch (kind_) {
517 case TONE_FALLING:
518 os.put(0x02e5);
519 os.put(0x02e9);
520 return 2;
521 case TONE_RISING:
522 os.put(0x02e9);
523 os.put(0x02e5);
524 return 2;
525 case TONE_HIGH_RISING:
526 os.put(0x02e7);
527 os.put(0x02e5);
528 return 2;
529 case TONE_LOW_RISING:
530 os.put(0x02e9);
531 os.put(0x02e7);
532 return 2;
533 case TONE_HIGH_RISING_FALLING:
534 os.put(0x02e8);
535 os.put(0x02e5);
536 os.put(0x02e8);
537 return 3;
538 }
539 return 0;
540 }
541
542
docbook(odocstream &,OutputParams const &) const543 int InsetIPAChar::docbook(odocstream & /*os*/, OutputParams const &) const
544 {
545 switch (kind_) {
546 case TONE_FALLING:
547 case TONE_RISING:
548 case TONE_HIGH_RISING:
549 case TONE_LOW_RISING:
550 case TONE_HIGH_RISING_FALLING:
551 // FIXME
552 LYXERR0("IPA tone macros not yet implemented with DocBook!");
553 break;
554 }
555 return 0;
556 }
557
558
xhtml(XHTMLStream & xs,OutputParams const &) const559 docstring InsetIPAChar::xhtml(XHTMLStream & xs, OutputParams const &) const
560 {
561 switch (kind_) {
562 case TONE_FALLING:
563 xs << XHTMLStream::ESCAPE_NONE << "˥"
564 << XHTMLStream::ESCAPE_NONE << "˩";
565 break;
566 case TONE_RISING:
567 xs << XHTMLStream::ESCAPE_NONE << "˩"
568 << XHTMLStream::ESCAPE_NONE << "˥";
569 break;
570 case TONE_HIGH_RISING:
571 xs << XHTMLStream::ESCAPE_NONE << "˧"
572 << XHTMLStream::ESCAPE_NONE << "˥";
573 break;
574 case TONE_LOW_RISING:
575 xs << XHTMLStream::ESCAPE_NONE << "˩"
576 << XHTMLStream::ESCAPE_NONE << "˧";
577 break;
578 case TONE_HIGH_RISING_FALLING:
579 xs << XHTMLStream::ESCAPE_NONE << "˨"
580 << XHTMLStream::ESCAPE_NONE << "˥"
581 << XHTMLStream::ESCAPE_NONE << "˨";
582 break;
583 }
584 return docstring();
585 }
586
587
toString(odocstream & os) const588 void InsetIPAChar::toString(odocstream & os) const
589 {
590 odocstringstream ods;
591 plaintext(ods, OutputParams(0));
592 os << ods.str();
593 }
594
595
forOutliner(docstring & os,size_t const,bool const) const596 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
validate(LaTeXFeatures & features) const604 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
621