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