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