]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathSideset.cpp
g-brief loads babel internally. So don't load it ourselves.
[lyx.git] / src / mathed / InsetMathSideset.cpp
1 /**
2  * \file InsetMathSideset.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Georg Baum
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetMathSideset.h"
15
16 #include "InsetMathSymbol.h"
17 #include "MathData.h"
18 #include "MathStream.h"
19 #include "MathSupport.h"
20
21 #include "BufferView.h"
22 #include "Cursor.h"
23 #include "DispatchResult.h"
24 #include "FuncRequest.h"
25 #include "FuncStatus.h"
26 #include "LaTeXFeatures.h"
27 #include "MetricsInfo.h"
28
29 #include "support/debug.h"
30 #include "support/lassert.h"
31
32
33 using namespace std;
34
35
36 namespace {
37         /// x spacing between the nucleus and the scripts
38         int const dx = 2;
39 } // namespace
40
41
42 namespace lyx {
43
44 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr)
45         : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
46           scriptr_(scriptr)
47 {}
48
49
50 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr,
51                                    MathAtom const & at)
52         : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
53           scriptr_(scriptr)
54 {
55         nuc().push_back(at);
56 }
57
58
59 Inset * InsetMathSideset::clone() const
60 {
61         return new InsetMathSideset(*this);
62 }
63
64
65 int InsetMathSideset::dybt(BufferView const & bv, int asc, int des, bool top) const
66 {
67         bool isCharBox = nuc().empty() ? false : isAlphaSymbol(nuc().back());
68         int dasc = 0;
69         if (scriptl_ && scriptr_)
70                 dasc = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
71         else if (scriptl_)
72                 dasc = bl().dimension(bv).ascent();
73         else if (scriptr_)
74                 dasc = br().dimension(bv).ascent();
75         int slevel = nuc().slevel();
76         int ascdrop = dasc - slevel;
77         int desdrop = isCharBox ? 0 : des + nuc().sshift();
78         int mindes = nuc().mindes();
79         des = max(desdrop, ascdrop);
80         des = max(mindes, des);
81         int minasc = nuc().minasc();
82         ascdrop = 0;
83         if (!isCharBox && (scriptl_ || scriptr_)) {
84                 if (scriptl_ && scriptr_)
85                         ascdrop = asc - min(tl().mindes(), tr().mindes());
86                 else if (scriptl_)
87                         ascdrop = asc - tl().mindes();
88                 else if (scriptr_)
89                         ascdrop = asc - tr().mindes();
90         }
91         int udes = 0;
92         if (scriptl_)
93                 udes = bl().dimension(bv).descent();
94         if (scriptr_)
95                 udes = max(udes, br().dimension(bv).descent());
96         asc = udes + nuc().sshift();
97         asc = max(ascdrop, asc);
98         asc = max(minasc, asc);
99         int del = asc - udes - dasc;
100         if (del + des <= 2) {
101                 int newdes = 2 - del;
102                 del = slevel - asc + udes;
103                 if (del > 0) {
104                         asc += del;
105                         newdes -= del;
106                 }
107                 des = max(des, newdes);
108         }
109         return top ? asc : des;
110 }
111
112
113 int InsetMathSideset::dyb(BufferView const & bv) const
114 {
115         int nd = ndes(bv);
116         int na = nasc(bv);
117         int des = dybt(bv, na, nd, false);
118         return des;
119 }
120
121
122 int InsetMathSideset::dyt(BufferView const & bv) const
123 {
124         int na = nasc(bv);
125         int nd = ndes(bv);
126         int asc = dybt(bv, na, nd, true);
127         return asc;
128 }
129
130
131 int InsetMathSideset::dxr(BufferView const & bv) const
132 {
133         return dxn(bv) + nwid(bv) + dx;
134 }
135
136
137 int InsetMathSideset::dxn(BufferView const & bv) const
138 {
139         Dimension const dimb = bl().dimension(bv);
140         Dimension const dimt = tl().dimension(bv);
141         return max(dimb.width(), dimt.width()) + dx;
142 }
143
144
145 int InsetMathSideset::nwid(BufferView const & bv) const
146 {
147         return nuc().dimension(bv).width();
148 }
149
150
151 int InsetMathSideset::nasc(BufferView const & bv) const
152 {
153         return nuc().dimension(bv).ascent();
154 }
155
156
157 int InsetMathSideset::ndes(BufferView const & bv) const
158 {
159         return nuc().dimension(bv).descent();
160 }
161
162
163 int InsetMathSideset::nker(BufferView const * bv) const
164 {
165         int const kerning = nuc().kerning(bv);
166         return max(kerning, 0);
167 }
168
169
170 void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const
171 {
172         Changer dummy2 = mi.base.changeEnsureMath();
173         Dimension dimn;
174         Dimension dimbl;
175         Dimension dimtl;
176         Dimension dimbr;
177         Dimension dimtr;
178         nuc().metrics(mi, dimn);
179         if (!scriptl_) {
180                 bl().metrics(mi, dimbl);
181                 dimtl = dimbl;
182         }
183         if (!scriptr_) {
184                 br().metrics(mi, dimbr);
185                 dimtr = dimbr;
186         }
187         Changer dummy = mi.base.changeScript();
188         if (scriptl_) {
189                 bl().metrics(mi, dimbl);
190                 tl().metrics(mi, dimtl);
191         }
192         if (scriptr_) {
193                 br().metrics(mi, dimbr);
194                 tr().metrics(mi, dimtr);
195         }
196
197         BufferView const & bv = *mi.base.bv;
198         // FIXME: data copying... not very efficient.
199
200         dim.wid = nwid(bv) + nker(mi.base.bv) + 2 * dx;
201         dim.wid += max(dimbl.width(), dimtl.width());
202         dim.wid += max(dimbr.width(), dimtr.width());
203         int na = nasc(bv);
204         int asc = dyt(bv) + max(dimtl.ascent(), dimtr.ascent());
205         dim.asc = max(na, asc);
206         int nd = ndes(bv);
207         int des = dyb(bv) + max(dimbl.descent(), dimbr.descent());
208         dim.des = max(nd, des);
209 }
210
211
212 void InsetMathSideset::draw(PainterInfo & pi, int x, int y) const
213 {
214         Changer dummy2 = pi.base.changeEnsureMath();
215         BufferView const & bv = *pi.base.bv;
216         nuc().draw(pi, x + dxn(bv), y);
217         if (!scriptl_)
218                 bl().draw(pi, x          , y);
219         if (!scriptr_)
220                 br().draw(pi, x + dxr(bv), y);
221         Changer dummy = pi.base.changeScript();
222         if (scriptl_) {
223                 bl().draw(pi, x          , y + dyb(bv));
224                 tl().draw(pi, x          , y - dyt(bv));
225         }
226         if (scriptr_) {
227                 br().draw(pi, x + dxr(bv), y + dyb(bv));
228                 tr().draw(pi, x + dxr(bv), y - dyt(bv));
229         }
230 }
231
232
233 void InsetMathSideset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
234 {
235         bl().metricsT(mi, dim);
236         tl().metricsT(mi, dim);
237         br().metricsT(mi, dim);
238         tr().metricsT(mi, dim);
239         nuc().metricsT(mi, dim);
240 }
241
242
243 void InsetMathSideset::drawT(TextPainter & pain, int x, int y) const
244 {
245         // FIXME: BROKEN
246         nuc().drawT(pain, x + 1, y);
247         bl().drawT(pain, x + 1, y + 1 /*dy0()*/);
248         tl().drawT(pain, x + 1, y - 1 /*dy1()*/);
249         br().drawT(pain, x + 1, y + 1 /*dy0()*/);
250         tr().drawT(pain, x + 1, y - 1 /*dy1()*/);
251 }
252
253
254
255 bool InsetMathSideset::idxForward(Cursor & cur) const
256 {
257         if (!scriptl_ && cur.idx() == 1) {
258                 // left => nucleus
259                 cur.idx() = 0;
260                 return true;
261         } else if (!scriptr_ && cur.idx() == 0) {
262                 // nucleus => right
263                 cur.idx() = 2 + scriptl_;
264                 return true;
265         }
266         return false;
267 }
268
269
270 bool InsetMathSideset::idxBackward(Cursor & cur) const
271 {
272         if (!scriptr_ && cur.idx() == (scriptl_ ? 3 : 2)) {
273                 // right => nucleus
274                 cur.idx() = 0;
275                 return true;
276         } else if (!scriptl_ && cur.idx() == 0) {
277                 // nucleus => left
278                 cur.idx() = 1;
279                 return true;
280         }
281         return false;
282 }
283
284
285 bool InsetMathSideset::idxUpDown(Cursor & cur, bool up) const
286 {
287         // in nucleus?
288         if (cur.idx() == 0) {
289                 // go up/down only if in the last position
290                 // or in the first position
291                 if ((scriptr_ && cur.pos() == cur.lastpos()) ||
292                     (scriptl_ && cur.pos() == 0)) {
293                         if (cur.pos() == 0)
294                                 cur.idx() = up ? 2 : 1;
295                         else
296                                 cur.idx() = (up ? 3 : 2) + scriptl_;
297                         cur.pos() = 0;
298                         return true;
299                 }
300                 return false;
301         }
302
303         // Are we 'up'?
304         if ((scriptl_ && cur.idx() == 2) ||
305             (scriptr_ && cur.idx() == (scriptl_ ? 4 : 3))) {
306                 // can't go further up
307                 if (up)
308                         return false;
309                 // otherwise go to first or last position in the nucleus
310                 cur.idx() = 0;
311                 if (cur.idx() == 2)
312                         cur.pos() = 0;
313                 else
314                         cur.pos() = cur.lastpos();
315                 return true;
316         }
317
318         // Are we 'down'?
319         if ((scriptl_ && cur.idx() == 1) ||
320             (scriptr_ && cur.idx() == (scriptl_ ? 3 : 2))) {
321                 // can't go further down
322                 if (!up)
323                         return false;
324                 // otherwise go to first or last position in the nucleus
325                 cur.idx() = 0;
326                 if (cur.idx() == 1)
327                         cur.pos() = 0;
328                 else
329                         cur.pos() = cur.lastpos();
330                 return true;
331         }
332
333         return false;
334 }
335
336
337 void InsetMathSideset::write(TeXMathStream & os) const
338 {
339         MathEnsurer ensurer(os);
340
341         os << "\\sideset";
342         os << '{';
343         if (scriptl_) {
344                 if (!bl().empty())
345                         os << "_{" << bl() << '}';
346                 if (!tl().empty())
347                         os << "^{" << tl() << '}';
348         } else
349                 os << bl();
350         os << "}{";
351         if (scriptr_) {
352                 if (!br().empty())
353                         os << "_{" << br() << '}';
354                 if (!tr().empty())
355                         os << "^{" << tr() << '}';
356         } else
357                 os << br();
358         os << '}';
359         os << nuc();
360
361         if (lock_ && !os.latex())
362                 os << "\\lyxlock ";
363 }
364
365
366 void InsetMathSideset::normalize(NormalStream & os) const
367 {
368         os << "[sideset ";
369
370         if (!bl().empty())
371                 os << bl() << ' ';
372         if (scriptl_ && !tl().empty())
373                 os << tl() << ' ';
374
375         if (!nuc().empty())
376                 os << nuc() << ' ';
377         else
378                 os << "[par]";
379
380         if (!br().empty())
381                 os << br() << ' ';
382         if (scriptr_ && !tr().empty())
383                 os << tr() << ' ';
384         os << ']';
385 }
386
387
388 void InsetMathSideset::mathmlize(MathMLStream & ms) const
389 {
390         // FIXME This is only accurate if both scriptl_ and scriptr_ are true
391         if (!scriptl_)
392                 ms << MTag("mrow") << bl() << ETag("mrow");
393         if (scriptl_ || scriptr_) {
394                 ms << MTag("mmultiscripts");
395
396                 if (nuc().empty())
397                         ms << CTag("mrow");
398                 else
399                         ms << MTag("mrow") << nuc() << ETag("mrow");
400
401                 if (br().empty() || !scriptr_)
402                         ms << CTag("none");
403                 else
404                         ms << MTag("mrow") << br() << ETag("mrow");
405                 if (tr().empty() || !scriptr_)
406                         ms << CTag("none");
407                 else
408                         ms << MTag("mrow") << tr() << ETag("mrow");
409
410                 if (bl().empty() || !scriptl_)
411                         ms << CTag("none");
412                 else
413                         ms << MTag("mrow") << bl() << ETag("mrow");
414                 if (tl().empty() || !scriptl_)
415                         ms << CTag("none");
416                 else
417                         ms << MTag("mrow") << tl() << ETag("mrow");
418
419                 ms << ETag("mmultiscripts");
420         }
421         if (!scriptr_)
422                 ms << MTag("mrow") << br() << ETag("mrow");
423 }
424
425
426 void InsetMathSideset::htmlize(HtmlStream & os) const
427 {
428         // FIXME This is only accurate if both scriptl_ and scriptr_ are true
429         bool const havebl = scriptl_ && !bl().empty();
430         bool const havetl = scriptl_ && !tl().empty();
431         bool const havebr = scriptr_ && !br().empty();
432         bool const havetr = scriptr_ && !tr().empty();
433
434         if (!scriptl_ && !bl().empty())
435                 os << bl();
436
437         if (havebl && havetl)
438                 os << MTag("span", "class='scripts'")
439                          << MTag("span") << tl() << ETag("span")
440                          << MTag("span") << bl() << ETag("span")
441                          << ETag("span");
442         else if (havebl)
443                 os << MTag("sub", "class='math'") << bl() << ETag("sub");
444         else if (havetl)
445                 os << MTag("sup", "class='math'") << tl() << ETag("sup");
446
447         if (!nuc().empty())
448                 os << nuc();
449
450         if (havebr && havetr)
451                 os << MTag("span", "class='scripts'")
452                          << MTag("span") << tr() << ETag("span")
453                          << MTag("span") << br() << ETag("span")
454                          << ETag("span");
455         else if (havebr)
456                 os << MTag("sub", "class='math'") << br() << ETag("sub");
457         else if (havetr)
458                 os << MTag("sup", "class='math'") << tr() << ETag("sup");
459
460         if (!scriptr_ && !br().empty())
461                 os << br();
462 }
463
464
465 void InsetMathSideset::infoize(odocstream & os) const
466 {
467         os << "Sideset";
468 }
469
470
471 // the idea for dual scripts came from the eLyXer code
472 void InsetMathSideset::validate(LaTeXFeatures & features) const
473 {
474         if (features.runparams().math_flavor == OutputParams::MathAsHTML)
475                 features.addCSSSnippet(
476                         "span.scripts{display: inline-block; vertical-align: middle; text-align:center; font-size: 75%;}\n"
477                         "span.scripts span {display: block;}\n"
478                         "sub.math{font-size: 75%;}\n"
479                         "sup.math{font-size: 75%;}");
480         features.require("amsmath");
481         InsetMathNest::validate(features);
482 }
483
484 } // namespace lyx