]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathDecoration.cpp
Make math decorations scalable with zoom level
[lyx.git] / src / mathed / InsetMathDecoration.cpp
1 /**
2  * \file InsetMathDecoration.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetMathDecoration.h"
15
16 #include "BufferView.h"
17 #include "MathData.h"
18 #include "MathParser.h"
19 #include "MathSupport.h"
20 #include "MathStream.h"
21 #include "MetricsInfo.h"
22
23 #include "LaTeXFeatures.h"
24
25 #include "support/debug.h"
26 #include "support/docstring.h"
27 #include "support/gettext.h"
28 #include "support/lassert.h"
29 #include "support/lstrings.h"
30
31 #include <algorithm>
32 #include <ostream>
33
34 using namespace lyx::support;
35
36 using namespace std;
37
38 namespace lyx {
39
40
41 InsetMathDecoration::InsetMathDecoration(Buffer * buf, latexkeys const * key)
42         : InsetMathNest(buf, 1), key_(key)
43 {
44 //      lyxerr << " creating deco " << key->name << endl;
45 }
46
47
48 Inset * InsetMathDecoration::clone() const
49 {
50         return new InsetMathDecoration(*this);
51 }
52
53
54 bool InsetMathDecoration::upper() const
55 {
56         return key_->name.substr(0, 5) != "under" && key_->name != "utilde";
57 }
58
59
60 MathClass InsetMathDecoration::mathClass() const
61 {
62         if (key_->name == "overbrace" || key_->name == "underbrace")
63                 return MC_OP;
64         return MC_ORD;
65 }
66
67
68 Limits InsetMathDecoration::defaultLimits(bool display) const
69 {
70         if (allowsLimitsChange() && display)
71                 return LIMITS;
72         else
73                 return NO_LIMITS;
74 }
75
76
77 bool InsetMathDecoration::protect() const
78 {
79         return
80                         key_->name == "overbrace" ||
81                         key_->name == "underbrace" ||
82                         key_->name == "overleftarrow" ||
83                         key_->name == "overrightarrow" ||
84                         key_->name == "overleftrightarrow" ||
85                         key_->name == "underleftarrow" ||
86                         key_->name == "underrightarrow" ||
87                         key_->name == "underleftrightarrow";
88 }
89
90
91 bool InsetMathDecoration::wide() const
92 {
93         return
94                         key_->name == "overline" ||
95                         key_->name == "underline" ||
96                         key_->name == "overbrace" ||
97                         key_->name == "underbrace" ||
98                         key_->name == "overleftarrow" ||
99                         key_->name == "overrightarrow" ||
100                         key_->name == "overleftrightarrow" ||
101                         key_->name == "widehat" ||
102                         key_->name == "widetilde" ||
103                         key_->name == "underleftarrow" ||
104                         key_->name == "underrightarrow" ||
105                         key_->name == "underleftrightarrow" ||
106                         key_->name == "undertilde" ||
107                         key_->name == "utilde";
108 }
109
110
111 InsetMath::mode_type InsetMathDecoration::currentMode() const
112 {
113         return key_->name == "underbar" ? TEXT_MODE : MATH_MODE;
114 }
115
116
117 void InsetMathDecoration::metrics(MetricsInfo & mi, Dimension & dim) const
118 {
119         Changer dummy = mi.base.changeEnsureMath(currentMode());
120
121         cell(0).metrics(mi, dim);
122
123         int const l1 = mi.base.bv->zoomedPixels(1);
124         int const l2 = 2 * l1;
125         int const l3 = 3 * l1;
126
127         dh_  = l2; //mathed_char_height(LM_TC_VAR, mi, 'I', ascent_, descent_);
128         dw_  = l3; //mathed_char_width(LM_TC_VAR, mi, 'x');
129
130         if (upper()) {
131                 dy_ = -dim.asc - dh_ - l1;
132                 dim.asc += dh_ + l1;
133         } else {
134                 dy_ = dim.des + l1;
135                 dim.des += dh_ + l2;
136         }
137 }
138
139
140 void InsetMathDecoration::draw(PainterInfo & pi, int x, int y) const
141 {
142         Changer dummy = pi.base.changeEnsureMath(currentMode());
143
144         cell(0).draw(pi, x, y);
145         Dimension const & dim0 = cell(0).dimension(*pi.base.bv);
146         if (wide())
147                 mathed_draw_deco(pi, x + 1, y + dy_, dim0.wid, dh_, key_->name);
148         else
149                 mathed_draw_deco(pi, x + 1 + (dim0.wid - dw_) / 2,
150                         y + dy_, dw_, dh_, key_->name);
151 }
152
153
154 void InsetMathDecoration::write(TeXMathStream & os) const
155 {
156         MathEnsurer ensurer(os);
157         if (os.fragile() && protect())
158                 os << "\\protect";
159         os << '\\' << key_->name << '{';
160         ModeSpecifier specifier(os, currentMode());
161         os << cell(0) << '}';
162         writeLimits(os);
163 }
164
165
166 void InsetMathDecoration::normalize(NormalStream & os) const
167 {
168         os << "[deco " << key_->name << ' ' <<  cell(0) << ']';
169 }
170
171
172 void InsetMathDecoration::infoize(odocstream & os) const
173 {
174         os << bformat(_("Decoration: %1$s"), key_->name);
175 }
176
177
178 namespace {
179         struct Attributes {
180                 Attributes() : over(false) {}
181                 Attributes(bool o, string const & t, string const & entity)
182                         : over(o), tag(t), entity(entity) {}
183                 bool over;
184                 string tag;
185                 string entity;
186         };
187
188         typedef map<string, Attributes> TranslationMap;
189
190         void buildTranslationMap(TranslationMap & t) {
191                 // the decorations we need to support are listed in lib/symbols
192                 t["acute"] = Attributes(true, "&acute;", "&#x00B4;");
193                 t["bar"]   = Attributes(true, "&OverBar;", "&#x00AF;");
194                 t["breve"] = Attributes(true, "&breve;", "&#x02D8;");
195                 t["check"] = Attributes(true, "&caron;", "&#x02C7;");
196                 t["ddddot"] = Attributes(true, "&DotDot;", "&#x20DC;");
197                 t["dddot"] = Attributes(true, "&TripleDot;", "&#x20DB;");
198                 t["ddot"] = Attributes(true, "&Dot;", "&#x00A8;");
199                 t["dot"] = Attributes(true, "&dot;", "&#x02D9;");
200                 t["grave"] = Attributes(true, "&grave;", "&#x0060;");
201                 t["hat"] = Attributes(true, "&circ;", "&#x02C6;");
202                 t["mathring"] = Attributes(true, "&ring;", "&#x02DA;");
203                 t["overbrace"] = Attributes(true, "&OverBrace;", "&#xFE37;");
204                 t["overleftarrow"] = Attributes(true, "&xlarr;", "&#x27F5;");
205                 t["overleftrightarrow"] = Attributes(true, "&xharr;", "&#x27F7;");
206                 t["overline"] = Attributes(true, "&macr;", "&#x00AF;");
207                 t["overrightarrow"] = Attributes(true, "&xrarr;", "&#x27F6;");
208                 t["tilde"] = Attributes(true, "&tilde;", "&#x02DC;");
209                 t["underbar"] = Attributes(false, "&UnderBar;", "&#x0332;");
210                 t["underbrace"] = Attributes(false, "&UnderBrace;", "&#xFE38;");
211                 t["underleftarrow"] = Attributes(false, "&xlarr;", "&#x27F5;");
212                 t["underleftrightarrow"] = Attributes(false, "&xharr;", "&#x27F7;");
213                 // this is the macron, again, but it works
214                 t["underline"] = Attributes(false, "&macr;", "&#x00AF;");
215                 t["underrightarrow"] = Attributes(false, "&xrarr;", "&#x27F6;");
216                 t["undertilde"] = Attributes(false, "&Tilde;", "&#x223C;");
217                 t["utilde"] = Attributes(false, "&Tilde;", "&#x223C;");
218                 t["vec"] = Attributes(true, "&rarr;", "&#x2192;");
219                 t["widehat"] = Attributes(true, "&Hat;", "&#x005E;");
220                 t["widetilde"] = Attributes(true, "&Tilde;", "&#x223C;");
221         }
222
223         TranslationMap const & translationMap() {
224                 static TranslationMap t;
225                 if (t.empty())
226                         buildTranslationMap(t);
227                 return t;
228         }
229 } // namespace
230
231 void InsetMathDecoration::mathmlize(MathMLStream & ms) const
232 {
233         TranslationMap const & t = translationMap();
234         TranslationMap::const_iterator cur = t.find(to_utf8(key_->name));
235         LASSERT(cur != t.end(), return);
236         char const * const outag = cur->second.over ? "mover" : "munder";
237         std::string decoration = ms.xmlMode() ? cur->second.entity : cur->second.tag;
238         ms << MTag(outag)
239            << MTag("mrow") << cell(0) << ETag("mrow")
240            << "<" << from_ascii(ms.namespacedTag("mo")) << " stretchy=\"true\">"
241            << from_ascii(decoration)
242            << "</" << from_ascii(ms.namespacedTag("mo")) << ">"
243            << ETag(outag);
244 }
245
246
247 void InsetMathDecoration::htmlize(HtmlStream & os) const
248 {
249         string const name = to_utf8(key_->name);
250         if (name == "bar") {
251                 os << MTag("span", "class='overbar'") << cell(0) << ETag("span");
252                 return;
253         }
254
255         if (name == "underbar" || name == "underline") {
256                 os << MTag("span", "class='underbar'") << cell(0) << ETag("span");
257                 return;
258         }
259
260         TranslationMap const & t = translationMap();
261         TranslationMap::const_iterator cur = t.find(name);
262         LASSERT(cur != t.end(), return);
263
264         bool symontop = cur->second.over;
265         string const symclass = symontop ? "symontop" : "symonbot";
266         os << MTag("span", "class='symbolpair " + symclass + "'")
267            << '\n';
268
269         if (symontop)
270                 os << MTag("span", "class='symbol'") << from_ascii(cur->second.tag);
271         else
272                 os << MTag("span", "class='base'") << cell(0);
273         os << ETag("span") << '\n';
274         if (symontop)
275                 os << MTag("span", "class='base'") << cell(0);
276         else
277                 os << MTag("span", "class='symbol'") << from_ascii(cur->second.tag);
278         os << ETag("span") << '\n' << ETag("span") << '\n';
279 }
280
281
282 // ideas borrowed from the eLyXer code
283 void InsetMathDecoration::validate(LaTeXFeatures & features) const
284 {
285         if (features.runparams().math_flavor == OutputParams::MathAsHTML) {
286                 string const name = to_utf8(key_->name);
287                 if (name == "bar") {
288                         features.addCSSSnippet("span.overbar{border-top: thin black solid;}");
289                 } else if (name == "underbar" || name == "underline") {
290                         features.addCSSSnippet("span.underbar{border-bottom: thin black solid;}");
291                 } else {
292                         features.addCSSSnippet(
293                                 "span.symbolpair{display: inline-block; text-align:center;}\n"
294                                 "span.symontop{vertical-align: top;}\n"
295                                 "span.symonbot{vertical-align: bottom;}\n"
296                                 "span.symbolpair span{display: block;}\n"
297                                 "span.symbol{height: 0.5ex;}");
298                 }
299         } else {
300                 if (!key_->required.empty())
301                         features.require(key_->required);
302         }
303         InsetMathNest::validate(features);
304 }
305
306 } // namespace lyx