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