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