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 "&#x361;" (toptiebar)
318 	// or "&#x35c;" (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 << "&#x2e5;"
564 		   << XHTMLStream::ESCAPE_NONE << "&#x2e9;";
565 		break;
566 	case TONE_RISING:
567 		xs << XHTMLStream::ESCAPE_NONE << "&#x2e9;"
568 		   << XHTMLStream::ESCAPE_NONE << "&#x2e5;";
569 		break;
570 	case TONE_HIGH_RISING:
571 		xs << XHTMLStream::ESCAPE_NONE << "&#x2e7;"
572 		   << XHTMLStream::ESCAPE_NONE << "&#x2e5;";
573 		break;
574 	case TONE_LOW_RISING:
575 		xs << XHTMLStream::ESCAPE_NONE << "&#x2e9;"
576 		   << XHTMLStream::ESCAPE_NONE << "&#x2e7;";
577 		break;
578 	case TONE_HIGH_RISING_FALLING:
579 		xs << XHTMLStream::ESCAPE_NONE << "&#x2e8;"
580 		   << XHTMLStream::ESCAPE_NONE << "&#x2e5;"
581 		   << XHTMLStream::ESCAPE_NONE << "&#x2e8;";
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