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