]> git.lyx.org Git - lyx.git/blob - src/insets/InsetIPAMacro.cpp
Whitespace
[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 "Font.h"
20 #include "FuncRequest.h"
21 #include "FuncStatus.h"
22 #include "LaTeXFeatures.h"
23 #include "Lexer.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/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
45 IPADecoTranslator const init_ipadecotranslator()
46 {
47         IPADecoTranslator translator("toptiebar", InsetIPADecoParams::Toptiebar);
48         translator.addPair("bottomtiebar", InsetIPADecoParams::Bottomtiebar);
49         return translator;
50 }
51
52
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
61 IPADecoTranslator const & ipadecotranslator()
62 {
63         static IPADecoTranslator const decotranslator =
64                         init_ipadecotranslator();
65         return decotranslator;
66 }
67
68
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
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
90 IPACharTranslator const & ipachartranslator()
91 {
92         static IPACharTranslator const chartranslator =
93             init_ipachartranslator();
94         return chartranslator;
95 }
96
97 } // namespace
98
99
100 InsetIPADecoParams::InsetIPADecoParams()
101         : type(Bottomtiebar)
102 {}
103
104
105 void InsetIPADecoParams::write(ostream & os) const
106 {
107         string const label = ipadecotranslator().find(type);
108         os << "IPADeco " << label << "\n";
109 }
110
111
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
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
136 InsetIPADeco::~InsetIPADeco()
137 {}
138
139
140 docstring InsetIPADeco::layoutName() const
141 {
142         return from_ascii("IPADeco:" + ipadecotranslator().find(params_.type));
143 }
144
145
146 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
179 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
223 void InsetIPADeco::write(ostream & os) const
224 {
225         params_.write(os);
226         InsetCollapsible::write(os);
227 }
228
229
230 void InsetIPADeco::read(Lexer & lex)
231 {
232         params_.read(lex);
233         InsetCollapsible::read(lex);
234 }
235
236
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
252 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
274 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
285 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
308 void InsetIPADeco::docbook(XMLStream & xs, OutputParams const & runparams) const
309 {
310         // The special combining character must be put in the middle, between the two other characters.
311         // It will not work if there is anything else than two pure characters, so going back to plaintext.
312         odocstringstream ods;
313         int h = (int)(InsetText::plaintext(ods, runparams) / 2);
314         docstring result = ods.str();
315         docstring const before = result.substr(0, h);
316         docstring const after = result.substr(h, result.size());
317
318         xs << XMLStream::ESCAPE_NONE << before;
319         if (params_.type == InsetIPADecoParams::Toptiebar)
320                 xs << XMLStream::ESCAPE_NONE << "&#x0361;";
321         else if (params_.type == InsetIPADecoParams::Bottomtiebar)
322                 xs << XMLStream::ESCAPE_NONE << "&#x035c;";
323         xs << XMLStream::ESCAPE_NONE << after;
324 }
325
326
327 docstring InsetIPADeco::xhtml(XMLStream & xs, OutputParams const & runparams) const
328 {
329         // FIXME: Like in plaintext, the combining characters "&#x361;" (toptiebar)
330         // or "&#x35c;" (bottomtiebar) would need to be inserted just in the mid
331         // of the text string. (How) can this be done with the xhtml stream?
332         return InsetCollapsible::xhtml(xs, runparams);
333 }
334
335
336 docstring InsetIPADeco::toolTip(BufferView const &, int, int) const
337 {
338         return ipadecotranslator_loc().find(params_.type);
339 }
340
341
342 string InsetIPADeco::params2string(InsetIPADecoParams const & params)
343 {
344         ostringstream data;
345         data << "IPADeco" << ' ';
346         params.write(data);
347         return data.str();
348 }
349
350
351 void InsetIPADeco::string2params(string const & in, InsetIPADecoParams & params)
352 {
353         params = InsetIPADecoParams();
354
355         if (in.empty())
356                 return;
357
358         istringstream data(in);
359         Lexer lex;
360         lex.setStream(data);
361         lex.setContext("InsetIPADeco::string2params");
362         lex >> "IPADeco" >> "toptiebar";
363
364         params.read(lex);
365 }
366
367
368 void InsetIPADeco::validate(LaTeXFeatures & features) const
369 {
370         features.require("tipa");
371         InsetText::validate(features);
372 }
373
374
375 bool InsetIPADeco::insetAllowed(InsetCode code) const
376 {
377         switch (code) {
378         // code that is allowed
379         case ERT_CODE:
380         case IPACHAR_CODE:
381         case SCRIPT_CODE:
382                 return true;
383         default:
384                 return false;
385         }
386 }
387
388 /////////////////////////////////////////////////////////////////////////
389 //
390 // InsetIPAChar
391 //
392 /////////////////////////////////////////////////////////////////////////
393
394
395 InsetIPAChar::InsetIPAChar(Kind k)
396         : Inset(0), kind_(k)
397 {}
398
399
400 InsetIPAChar::Kind InsetIPAChar::kind() const
401 {
402         return kind_;
403 }
404
405
406 void InsetIPAChar::metrics(MetricsInfo & mi, Dimension & dim) const
407 {
408         frontend::FontMetrics const & fm =
409                 theFontMetrics(mi.base.font);
410         dim.asc = fm.maxAscent();
411         dim.des = fm.maxDescent();
412
413         string s;
414         switch (kind_) {
415                 case TONE_FALLING:
416                 case TONE_RISING:
417                 case TONE_HIGH_RISING:
418                 case TONE_LOW_RISING:
419                 case TONE_HIGH_RISING_FALLING:
420                         s = "_";
421                         break;
422         }
423         docstring ds(s.begin(), s.end());
424         dim.wid = fm.width(ds);
425 }
426
427
428 void InsetIPAChar::draw(PainterInfo & pi, int x, int y) const
429 {
430         FontInfo font = pi.base.font;
431         frontend::FontMetrics const & fm =
432                         theFontMetrics(font);
433
434         switch (kind_) {
435         case TONE_FALLING:
436         {
437                 int w = fm.width(char_type('-'));
438                 int h = fm.ascent(char_type('M'));
439                 int x2 = x + w;
440                 int y2 = y - h;
441
442                 pi.pain.line(x2, y2, x2, y, Color_foreground);
443                 pi.pain.line(x2, y, x, y2, Color_foreground);
444                 break;
445         }
446         case TONE_RISING:
447         {
448                 int w = fm.width(char_type('-'));
449                 int h = fm.ascent(char_type('M'));
450                 int x2 = x + w;
451                 int y2 = y - h;
452
453                 pi.pain.line(x2, y, x2, y2, Color_foreground);
454                 pi.pain.line(x2, y2, x, y, Color_foreground);
455                 break;
456         }
457         case TONE_HIGH_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.75);
464
465                 pi.pain.line(x2, y, x2, y2, Color_foreground);
466                 pi.pain.line(x2, y2, x, y3, Color_foreground);
467                 break;
468         }
469         case TONE_LOW_RISING:
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 y3 = y - int(h * 0.25);
476
477                 pi.pain.line(x2, y, x2, y2, Color_foreground);
478                 pi.pain.line(x2, y3, x, y, Color_foreground);
479                 break;
480         }
481         case TONE_HIGH_RISING_FALLING:
482         {
483                 int w = fm.width(char_type('-'));
484                 int h = fm.ascent(char_type('M'));
485                 int x2 = x + w;
486                 int y2 = y - h;
487                 int x3 = x + int(w * 0.5);
488                 int y3 = y - int(h * 0.75);
489
490                 pi.pain.line(x2, y, x2, y2, Color_foreground);
491                 pi.pain.line(x2, y3, x3, y2, Color_foreground);
492                 pi.pain.line(x3, y2, x, y3, Color_foreground);
493                 break;
494         }
495         }
496 }
497
498
499 void InsetIPAChar::write(ostream & os) const
500 {
501         string const command = ipachartranslator().find(kind_);
502         if (command.empty()) {
503                 LYXERR0("InsetIPAChar::write: Unknown type");
504                 return;
505         }
506         os << "\\IPAChar " << command << "\n";
507 }
508
509
510 void InsetIPAChar::read(Lexer & lex)
511 {
512         lex.next();
513         string const command = lex.getString();
514         kind_ = ipachartranslator().find(command);
515 }
516
517
518 void InsetIPAChar::latex(otexstream & os,
519                          OutputParams const &) const
520 {
521         string const command = ipachartranslator().find(kind_);
522         os << command;
523 }
524
525
526 int InsetIPAChar::plaintext(odocstringstream & os, OutputParams const &, size_t) const
527 {
528         switch (kind_) {
529         case TONE_FALLING:
530                 os.put(0x02e5);
531                 os.put(0x02e9);
532                 return 2;
533         case TONE_RISING:
534                 os.put(0x02e9);
535                 os.put(0x02e5);
536                 return 2;
537         case TONE_HIGH_RISING:
538                 os.put(0x02e7);
539                 os.put(0x02e5);
540                 return 2;
541         case TONE_LOW_RISING:
542                 os.put(0x02e9);
543                 os.put(0x02e7);
544                 return 2;
545         case TONE_HIGH_RISING_FALLING:
546                 os.put(0x02e8);
547                 os.put(0x02e5);
548                 os.put(0x02e8);
549                 return 3;
550         }
551         return 0;
552 }
553
554
555 void InsetIPAChar::docbook(XMLStream & xs, OutputParams const &) const
556 {
557         switch (kind_) {
558         case TONE_FALLING:
559                 xs << XMLStream::ESCAPE_NONE << "&#x2e5;";
560                 xs << XMLStream::ESCAPE_NONE << "&#x2e9;";
561                 break;
562         case TONE_RISING:
563                 xs << XMLStream::ESCAPE_NONE << "&#x2e9;";
564                 xs << XMLStream::ESCAPE_NONE << "&#x2e5;";
565                 break;
566         case TONE_HIGH_RISING:
567                 xs << XMLStream::ESCAPE_NONE << "&#x2e7;";
568                 xs << XMLStream::ESCAPE_NONE << "&#x2e5;";
569                 break;
570         case TONE_LOW_RISING:
571                 xs << XMLStream::ESCAPE_NONE << "&#x2e9;";
572                 xs << XMLStream::ESCAPE_NONE << "&#x2e7;";
573                 break;
574         case TONE_HIGH_RISING_FALLING:
575                 xs << XMLStream::ESCAPE_NONE << "&#x2e8;";
576                 xs << XMLStream::ESCAPE_NONE << "&#x2e5;";
577                 xs << XMLStream::ESCAPE_NONE << "&#x2e8;";
578                 break;
579         }
580 }
581
582
583 docstring InsetIPAChar::xhtml(XMLStream & xs, OutputParams const &) const
584 {
585         switch (kind_) {
586         case TONE_FALLING:
587                 xs << XMLStream::ESCAPE_NONE << "&#x2e5;"
588                    << XMLStream::ESCAPE_NONE << "&#x2e9;";
589                 break;
590         case TONE_RISING:
591                 xs << XMLStream::ESCAPE_NONE << "&#x2e9;"
592                    << XMLStream::ESCAPE_NONE << "&#x2e5;";
593                 break;
594         case TONE_HIGH_RISING:
595                 xs << XMLStream::ESCAPE_NONE << "&#x2e7;"
596                    << XMLStream::ESCAPE_NONE << "&#x2e5;";
597                 break;
598         case TONE_LOW_RISING:
599                 xs << XMLStream::ESCAPE_NONE << "&#x2e9;"
600                    << XMLStream::ESCAPE_NONE << "&#x2e7;";
601                 break;
602         case TONE_HIGH_RISING_FALLING:
603                 xs << XMLStream::ESCAPE_NONE << "&#x2e8;"
604                    << XMLStream::ESCAPE_NONE << "&#x2e5;"
605                    << XMLStream::ESCAPE_NONE << "&#x2e8;";
606                 break;
607         }
608         return docstring();
609 }
610
611
612 void InsetIPAChar::toString(odocstream & os) const
613 {
614         odocstringstream ods;
615         plaintext(ods, OutputParams(0));
616         os << ods.str();
617 }
618
619
620 void InsetIPAChar::forOutliner(docstring & os, size_t const, bool const) const
621 {
622         odocstringstream ods;
623         plaintext(ods, OutputParams(0));
624         os += ods.str();
625 }
626
627
628 void InsetIPAChar::validate(LaTeXFeatures & features) const
629 {
630         switch (kind_) {
631         case TONE_FALLING:
632         case TONE_RISING:
633         case TONE_HIGH_RISING:
634         case TONE_LOW_RISING:
635         case TONE_HIGH_RISING_FALLING:
636                 features.require("tone");
637                 break;
638         default:
639                 break;
640         }
641 }
642
643
644 } // namespace lyx