]> git.lyx.org Git - lyx.git/blob - src/insets/InsetSpecialChar.cpp
6554cce16e552078a3ad0d01bde208333c01a01f
[lyx.git] / src / insets / InsetSpecialChar.cpp
1 /**
2  * \file InsetSpecialChar.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup Nielsen
7  * \author Jean-Marc Lasgouttes
8  * \author Lars Gullik Bjønnes
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "InsetSpecialChar.h"
16
17 #include "Dimension.h"
18 #include "Font.h"
19 #include "LaTeXFeatures.h"
20 #include "Lexer.h"
21 #include "MetricsInfo.h"
22 #include "output_xhtml.h"
23 #include "texstream.h"
24
25 #include "frontends/FontMetrics.h"
26 #include "frontends/Painter.h"
27
28 #include "support/debug.h"
29 #include "support/docstream.h"
30
31 using namespace std;
32
33 namespace lyx {
34
35
36 InsetSpecialChar::InsetSpecialChar(Kind k)
37         : Inset(0), kind_(k)
38 {}
39
40
41 InsetSpecialChar::Kind InsetSpecialChar::kind() const
42 {
43         return kind_;
44 }
45
46
47 namespace {
48
49 int logoWidth(FontInfo const & font, InsetSpecialChar::Kind kind) {
50         frontend::FontMetrics const & fm = theFontMetrics(font);
51         int const em = fm.em();
52         int width = 0;
53         // See drawlogo() below to understand what this does.
54         switch (kind) {
55         case InsetSpecialChar::PHRASE_LYX:
56                 width = fm.width(from_ascii("L")) - em / 6
57                         + fm.width(from_ascii("Y")) - em / 8
58                         + fm.width(from_ascii("X"));
59                 break;
60
61         case InsetSpecialChar::PHRASE_TEX:
62                 width = fm.width(from_ascii("T")) - em / 6
63                         + fm.width(from_ascii("E")) - em / 8
64                         + fm.width(from_ascii("X"));
65                 break;
66
67         case InsetSpecialChar::PHRASE_LATEX2E:
68                 width = logoWidth(font, InsetSpecialChar::PHRASE_LATEX)
69                         + 3 * em / 20
70                         + fm.width(from_ascii("2") + char_type(0x03b5));
71                 break;
72         case InsetSpecialChar::PHRASE_LATEX: {
73                 FontInfo smaller = font;
74                 smaller.decSize().decSize();
75                 width = fm.width(from_ascii("L")) - 9 * em / 25
76                         + theFontMetrics(smaller).width(from_ascii("A")) - 3 * em / 20
77                         + logoWidth(font, InsetSpecialChar::PHRASE_TEX);
78                 break;
79         }
80         default:
81                 LYXERR0("No information for computing width of logo " << kind);
82         }
83
84         return width;
85 }
86
87 }
88
89
90 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
91 {
92         frontend::FontMetrics const & fm =
93                 theFontMetrics(mi.base.font);
94         dim.asc = fm.maxAscent();
95         dim.des = fm.maxDescent();
96         dim.wid = 0;
97
98         docstring s;
99         switch (kind_) {
100                 case LIGATURE_BREAK:
101                         s = from_ascii("|");
102                         break;
103                 case END_OF_SENTENCE:
104                         s = from_ascii(".");
105                         break;
106                 case LDOTS:
107                         s = from_ascii(". . .");
108                         break;
109                 case MENU_SEPARATOR:
110                         s = from_ascii(" x ");
111                         break;
112                 case HYPHENATION:
113                         dim.wid = fm.width(from_ascii("-"));
114                         if (dim.wid > 5)
115                                 dim.wid -= 2; // to make it look shorter
116                         break;
117                 case SLASH:
118                         s = from_ascii("/");
119                         break;
120                 case NOBREAKDASH:
121                         s = from_ascii("-");
122                         break;
123                 case PHRASE_LYX:
124                 case PHRASE_TEX:
125                 case PHRASE_LATEX2E:
126                 case PHRASE_LATEX:
127                         dim.wid = logoWidth(mi.base.font, kind_);
128                         break;
129         }
130         if (dim.wid == 0)
131                 dim.wid = fm.width(s);
132
133         setDimCache(mi, dim);
134 }
135
136
137 void InsetSpecialChar::drawBackground(PainterInfo & pi, int x, int y) const
138 {
139         if (pi.full_repaint)
140                 return;
141         Dimension const dim = dimension(*pi.base.bv);
142         pi.pain.fillRectangle(x, y - dim.asc, dim.wid, dim.asc + dim.des,
143                               pi.backgroundColor(this));
144 }
145
146
147 namespace {
148
149 // helper function: draw text and update x.
150 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
151 {
152         pi.pain.text(x, y, ch, pi.base.font);
153         x += theFontMetrics(pi.base.font).width(ch);
154 }
155
156
157 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
158 {
159         FontInfo const & font = pi.base.font;
160         int const em = theFontMetrics(font).em();
161         switch (kind) {
162         case InsetSpecialChar::PHRASE_LYX:
163                 /** Reference macro:
164                  *  \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
165                  */
166                 drawChar(pi, x, y, 'L');
167                 x -= em / 6;
168                 drawChar(pi, x, y + em / 4, 'Y');
169                 x -= em / 8;
170                 drawChar(pi, x, y, 'X');
171                 break;
172
173         case InsetSpecialChar::PHRASE_TEX: {
174                 /** Reference macro:
175                  *  \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
176                  */
177                 int const ex = theFontMetrics(font).ascent('x');
178                 drawChar(pi, x, y, 'T');
179                 x -= em / 6;
180                 drawChar(pi, x, y + ex / 2, 'E');
181                 x -= em / 8;
182                 drawChar(pi, x, y, 'X');
183                 break;
184         }
185         case InsetSpecialChar::PHRASE_LATEX2E:
186                 /** Reference macro:
187                  *  \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
188                  *    \if b\expandafter\@car\f@series\@nil\boldmath\fi
189                  *    \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
190                  */
191                 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
192                 x += 3 * em / 20;
193                 drawChar(pi, x, y, '2');
194                 drawChar(pi, x, y + em / 4, char_type(0x03b5));
195                 break;
196
197         case InsetSpecialChar::PHRASE_LATEX: {
198                 /** Reference macro:
199                  * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
200                  *        {\sbox\z@ T%
201                  *         \vbox to\ht\z@{\hbox{\check@mathfonts
202                  *                              \fontsize\sf@size\z@
203                  *                              \math@fontsfalse\selectfont
204                  *                              A}%
205                  *                        \vss}%
206                  *        }%
207                  *        \kern-.15em%
208                  *        \TeX}
209                  */
210                 drawChar(pi, x, y, 'L');
211                 x -= 9 * em / 25;
212                 PainterInfo pi2 = pi;
213                 pi2.base.font.decSize().decSize();
214                 drawChar(pi2, x, y - em / 5, 'A');
215                 x -= 3 * em / 20;
216                 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
217                 break;
218         }
219         default:
220                 LYXERR0("No information for drawing logo " << kind);
221         }
222 }
223
224 }
225
226 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
227 {
228         FontInfo font = pi.base.font;
229
230         switch (kind_) {
231         case HYPHENATION:
232         {
233                 font.setColor(Color_special);
234                 pi.pain.text(x, y, char_type('-'), font);
235                 break;
236         }
237         case LIGATURE_BREAK:
238         {
239                 font.setColor(Color_special);
240                 pi.pain.text(x, y, char_type('|'), font);
241                 break;
242         }
243         case END_OF_SENTENCE:
244         {
245                 font.setColor(Color_special);
246                 pi.pain.text(x, y, char_type('.'), font);
247                 break;
248         }
249         case LDOTS:
250         {
251                 font.setColor(Color_special);
252                 string ell = ". . . ";
253                 docstring dell(ell.begin(), ell.end());
254                 pi.pain.text(x, y, dell, font);
255                 break;
256         }
257         case MENU_SEPARATOR:
258         {
259                 frontend::FontMetrics const & fm =
260                         theFontMetrics(font);
261
262                 // A triangle the width and height of an 'x'
263                 int w = fm.width(char_type('x'));
264                 int ox = fm.width(char_type(' ')) + x;
265                 int h = fm.ascent(char_type('x'));
266                 int xp[4], yp[4];
267
268                 xp[0] = ox;     yp[0] = y;
269                 xp[1] = ox;     yp[1] = y - h;
270                 xp[2] = ox + w; yp[2] = y - h/2;
271                 xp[3] = ox;     yp[3] = y;
272
273                 pi.pain.lines(xp, yp, 4, Color_special);
274                 break;
275         }
276         case SLASH:
277         {
278                 font.setColor(Color_special);
279                 pi.pain.text(x, y, char_type('/'), font);
280                 break;
281         }
282         case NOBREAKDASH:
283         {
284                 font.setColor(Color_latex);
285                 pi.pain.text(x, y, char_type('-'), font);
286                 break;
287         }
288         case PHRASE_LYX:
289         case PHRASE_TEX:
290         case PHRASE_LATEX2E:
291         case PHRASE_LATEX:
292                 drawLogo(pi, x, y, kind_);
293                 break;
294         }
295 }
296
297
298 // In lyxf3 this will be just LaTeX
299 void InsetSpecialChar::write(ostream & os) const
300 {
301         string command;
302         switch (kind_) {
303         case HYPHENATION:
304                 command = "softhyphen";
305                 break;
306         case LIGATURE_BREAK:
307                 command = "ligaturebreak";
308                 break;
309         case END_OF_SENTENCE:
310                 command = "endofsentence";
311                 break;
312         case LDOTS:
313                 command = "ldots";
314                 break;
315         case MENU_SEPARATOR:
316                 command = "menuseparator";
317                 break;
318         case SLASH:
319                 command = "breakableslash";
320                 break;
321         case NOBREAKDASH:
322                 command = "nobreakdash";
323                 break;
324         case PHRASE_LYX:
325                 command = "LyX";
326                 break;
327         case PHRASE_TEX:
328                 command = "TeX";
329                 break;
330         case PHRASE_LATEX2E:
331                 command = "LaTeX2e";
332                 break;
333         case PHRASE_LATEX:
334                 command = "LaTeX";
335                 break;
336         }
337         os << "\\SpecialChar " << command << "\n";
338 }
339
340
341 // This function will not be necessary when lyx3
342 void InsetSpecialChar::read(Lexer & lex)
343 {
344         lex.next();
345         string const command = lex.getString();
346
347         if (command == "softhyphen")
348                 kind_ = HYPHENATION;
349         else if (command == "ligaturebreak")
350                 kind_ = LIGATURE_BREAK;
351         else if (command == "endofsentence")
352                 kind_ = END_OF_SENTENCE;
353         else if (command == "ldots")
354                 kind_ = LDOTS;
355         else if (command == "menuseparator")
356                 kind_ = MENU_SEPARATOR;
357         else if (command == "breakableslash")
358                 kind_ = SLASH;
359         else if (command == "nobreakdash")
360                 kind_ = NOBREAKDASH;
361         else if (command == "LyX")
362                 kind_ = PHRASE_LYX;
363         else if (command == "TeX")
364                 kind_ = PHRASE_TEX;
365         else if (command == "LaTeX2e")
366                 kind_ = PHRASE_LATEX2E;
367         else if (command == "LaTeX")
368                 kind_ = PHRASE_LATEX;
369         else
370                 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
371 }
372
373
374 void InsetSpecialChar::latex(otexstream & os,
375                              OutputParams const & rp) const
376 {
377         switch (kind_) {
378         case HYPHENATION:
379                 os << "\\-";
380                 break;
381         case LIGATURE_BREAK:
382                 os << "\\textcompwordmark{}";
383                 break;
384         case END_OF_SENTENCE:
385                 os << "\\@.";
386                 break;
387         case LDOTS:
388                 os << "\\ldots{}";
389                 break;
390         case MENU_SEPARATOR:
391                 if (rp.local_font->isRightToLeft())
392                         os << "\\lyxarrow*{}";
393                 else
394                         os << "\\lyxarrow{}";
395                 break;
396         case SLASH:
397                 os << "\\slash{}";
398                 break;
399         case NOBREAKDASH:
400                 if (rp.moving_arg)
401                         os << "\\protect";
402                 os << "\\nobreakdash-";
403                 break;
404         case PHRASE_LYX:
405                 if (rp.moving_arg)
406                         os << "\\protect";
407                 os << "\\LyX{}";
408                 break;
409         case PHRASE_TEX:
410                 if (rp.moving_arg)
411                         os << "\\protect";
412                 os << "\\TeX{}";
413                 break;
414         case PHRASE_LATEX2E:
415                 if (rp.moving_arg)
416                         os << "\\protect";
417                 os << "\\LaTeXe{}";
418                 break;
419         case PHRASE_LATEX:
420                 if (rp.moving_arg)
421                         os << "\\protect";
422                 os << "\\LaTeX{}";
423                 break;
424         }
425 }
426
427
428 int InsetSpecialChar::plaintext(odocstringstream & os,
429         OutputParams const &, size_t) const
430 {
431         switch (kind_) {
432         case HYPHENATION:
433                 return 0;
434         case LIGATURE_BREAK:
435                 os.put(0x200c);
436                 return 1;
437         case END_OF_SENTENCE:
438                 os << '.';
439                 return 1;
440         case LDOTS:
441                 os.put(0x2026);
442                 return 1;
443         case MENU_SEPARATOR:
444                 os << "->";
445                 return 2;
446         case SLASH:
447                 os << '/';
448                 return 1;
449         case NOBREAKDASH:
450                 os.put(0x2011);
451                 return 1;
452         case PHRASE_LYX:
453                 os << "LyX";
454                 return 3;
455         case PHRASE_TEX:
456                 os << "TeX";
457                 return 3;
458         case PHRASE_LATEX2E:
459                 os << "LaTeX2";
460                 os.put(0x03b5);
461                 return 7;
462         case PHRASE_LATEX:
463                 os << "LaTeX";
464                 return 5;
465         }
466         return 0;
467 }
468
469
470 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
471 {
472         switch (kind_) {
473         case HYPHENATION:
474         case LIGATURE_BREAK:
475                 break;
476         case END_OF_SENTENCE:
477                 os << '.';
478                 break;
479         case LDOTS:
480                 os << "&hellip;";
481                 break;
482         case MENU_SEPARATOR:
483                 os << "&lyxarrow;";
484                 break;
485         case SLASH:
486                 os << '/';
487                 break;
488         case NOBREAKDASH:
489                 os << '-';
490                 break;
491         case PHRASE_LYX:
492                 os << "LyX";
493                 break;
494         case PHRASE_TEX:
495                 os << "TeX";
496                 break;
497         case PHRASE_LATEX2E:
498                 os << "LaTeX2";
499                 os.put(0x03b5);
500                 break;
501         case PHRASE_LATEX:
502                 os << "LaTeX";
503                 break;
504         }
505         return 0;
506 }
507
508
509 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
510 {
511         switch (kind_) {
512         case HYPHENATION:
513                 break;
514         case LIGATURE_BREAK:
515                 xs << XHTMLStream::ESCAPE_NONE << "&#8204;";
516                 break;
517         case END_OF_SENTENCE:
518                 xs << '.';
519                 break;
520         case LDOTS:
521                 xs << XHTMLStream::ESCAPE_NONE << "&hellip;";
522                 break;
523         case MENU_SEPARATOR:
524                 xs << XHTMLStream::ESCAPE_NONE << "&rArr;";
525                 break;
526         case SLASH:
527                 xs << XHTMLStream::ESCAPE_NONE << "&frasl;";
528                 break;
529         case NOBREAKDASH:
530                 xs << XHTMLStream::ESCAPE_NONE << "&#8209;";
531                 break;
532         case PHRASE_LYX:
533                 xs << "LyX";
534                 break;
535         case PHRASE_TEX:
536                 xs << "TeX";
537                 break;
538         case PHRASE_LATEX2E:
539                 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "&#x3b5;";
540                 break;
541         case PHRASE_LATEX:
542                 xs << "LaTeX";
543                 break;
544         }
545         return docstring();
546 }
547
548
549 void InsetSpecialChar::toString(odocstream & os) const
550 {
551         switch (kind_) {
552         case LIGATURE_BREAK:
553                 // Do not output ZERO WIDTH NON JOINER here
554                 // Spell checker would choke on it.
555                 return;
556         default:
557                 break;
558         }
559         odocstringstream ods;
560         plaintext(ods, OutputParams(0));
561         os << ods.str();
562 }
563
564
565 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
566                                                                    bool const) const
567 {
568         odocstringstream ods;
569         plaintext(ods, OutputParams(0));
570         os += ods.str();
571 }
572
573
574 void InsetSpecialChar::validate(LaTeXFeatures & features) const
575 {
576         if (kind_ == MENU_SEPARATOR)
577                 features.require("lyxarrow");
578         if (kind_ == NOBREAKDASH)
579                 features.require("amsmath");
580         if (kind_ == PHRASE_LYX)
581                 features.require("LyX");
582 }
583
584
585 bool InsetSpecialChar::isLetter() const
586 {
587         return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
588                 || kind_ == NOBREAKDASH;
589 }
590
591
592 bool InsetSpecialChar::isLineSeparator() const
593 {
594 #if 0
595         // this would be nice, but it does not work, since
596         // Paragraph::stripLeadingSpaces nukes the characters which
597         // have this property. I leave the code here, since it should
598         // eventually be made to work. (JMarc 20020327)
599         return kind_ == HYPHENATION || kind_ == MENU_SEPARATOR
600                 || kind_ == SLASH;
601 #else
602         return false;
603 #endif
604 }
605
606
607 } // namespace lyx